-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dispatch and native plugins #2987
Comments
Discussed offline - the API looks fine, but we should have a |
Also looks good to me. Maybe we should also take some time to reorganize
|
That's a good idea @kevinkassimo actually new prototype allows to keep CLI ops as they were (taking |
I'm closing this issue because we introduced all changes described in this PR. Native plugins are still TODO, but we still need a few prerequisites before it can be tackled. |
How much of this discussion is still relevant & interesting now that #3372 has landed? How much of native plugins was implemented alike or as per these ideas? Which parts of this issue are n/a with the native plugins we have? |
@rektide I think that implementation mostly follows what was discussed here. Built-in ops are now registered in similar ways (with 0 reserved to retrieve other op name to id mapping), and external ops still cannot access CLI state directly yet. |
Deno op dispatch and native plugins
Overview
Deno dispatch system is based on
opId
.Message passing is done using so-called "shared queue", which for now has served it's purpose well.
The
opId
which identifies the op and its dispatcher is a number that is shared between TypeScript and Rust.As of Deno v0.18.0 all ops dispatching logic is hardcoded.
Problem
Current solution is very simple and quite efficient, however has several shortcomings that need to be addressed
to expose native plugins API to users;
OpId
has to be manually synchronized between TS and Rust. In current situation, where we have about 60 ops, it's not that bad, but surely we'll be cumbersome as number of ops grows.// see example above
//js/
while Rust code lives in//cli/ops/
. Ideally we'd like to have//ops
directory that will contain both TS and Rust code (eg.//ops/fs/main.ts
and//ops/fs/mod.ts
as entry points).Nomenclature
dispatch
- main method used to call into Rust code from JS. SeeIsolate::set_dispatch
method.dispatcher
- an abstract construct that is called fromdispatch
. It's aware of serialization method ("minimal", "json") and sets contract forop handler
.op
/op handler
- a function that performs some actual work, eg. "read"/"write"/"dial".Op
has to be used with adispatcher
that knows how to transform result of thatop
intoCoreOp
.CoreOp
- fundamental type that describes how Rust code can return value to JS.Requirements
Deno.core
and shared queue infrastructure.ThreadSafeState
because it's adeno_cli
concept.dispatcher
with custom serialization method (eg. "msgpack").op
statically (before build).opIds
- after registration of an op, during startup Rust sends an "op map" that is a dictonary mapping op names to op ids.op
dynamically (from JS code), loading some Rust code (as dynamic library?). This functionality can be exposed as another op -op_register_new_op
.op
multiple times. Say I have some native plugin that requires "read" op, but I'm not sure that host will have "read" available. So I register the op in my library setup code (potentially with namespace of my plugin).Proposed dispatch implementation
Prototype by @afinch7
In #2840 there's a complete solution.
After registration of a new op a bunch of listener methods is called to reset the ids of all ops.I believe this can be refactored to encapsulate this logic in
JsonOp
(presented in "pros" section) and ultimatelyshould be handled by separate op that calls "callback" function on dispatch to register op ids that are sent from Rust.
This is only done when using
set_dispatch_registry
, which can be combined withset_dispatch
. It's functionality is equivalentto proposed
setOpMap
below.notifier_reg
is used to trigger "op listeners" described above.Pros:
It might need a little tweaking as each op would create a closure, but that has to verified experimentally.
All code (both Rust and TS) related to
fs
lives inside//ops/fs
crate.Cons:
This leads for a lot of boilerplate code, where we essentialy have an empty structure that must implement a trait.
A concept of "import map" (not import maps we have implemented in Deno tho!) is used to map bare specifier to path of .ts file inside the crate. Eg. "deno_op_fs" is mapped to "bundle/main.ts" in "ops_fs" crate.
Prototype by @bartlomieju
Taking a lot of inspiration from @afinch7 I came up with a bit different solution that allows to avoid problem of initial double lookup.
In this case each registered op is placed in the
op_phone_book
structure to identify it by it's namespace and name andOpId
is an offset in theop_registry
vector obtained from length of the vector.In the example above
OpHandler
type can be roughly described as:Which is equivalent of wrapping any
op
with its designateddispatcher
.Then on startup a map of all registered ops can be sent to JS to synchronize JS counterparts functions:
That way we get free validation that all ops are properly registered. Additional check can be added to ensure
that all ops sent from Rust are registered as well.
Upon dynamic registration of op we can do the same thing synchronously. The only constraint is that ops couldn't be unregistered
unless some synchronisation mechanism is added.
Questions/TODOs
Isolate
which meansops
becomecore
concept (which in turn will requirepermissions
to becomecore
concept).Proposed native plugins implementation
TODO
Starting this issue for further discussion.
This is at least partially 1.0 blocker (#2473)
Related issues:
Related PRs:
CC @afinch7 @ry @piscisaureus @kevinkassimo
The text was updated successfully, but these errors were encountered: