-
Notifications
You must be signed in to change notification settings - Fork 284
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
Add ThreadSafeCallback helper to schedule callbacks on the main thread #375
Add ThreadSafeCallback helper to schedule callbacks on the main thread #375
Conversation
I don't have the depth to comment on the viability of this, but I would love to have this functionality! I'm currently building something kind of complicated using |
@geovie sorry for the delay on this. We'll review this soon |
Ignore my previous comment. I looked at the code and see that each is being run in a separate thread. |
Oh wow, this is exactly what I need. |
Thanks for this. I needed some sane way to get callbacks working. This has been a life saver! |
any updates? |
I tried the new branch, and it breaks unfortunately, using |
you can see the issue here: https://github.com/deltachat/deltachat-neon/blob/master/test.js#L14 |
sorry, it was my mistake, I was putting too much work into the |
060f578
to
c5ffa7a
Compare
src/eventhandler/mod.rs
Outdated
|
||
impl EventHandler { | ||
pub fn new(callback: Handle<JsFunction>) -> Self { | ||
TaskContext::with(|mut cx: TaskContext| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is TaskContext correct here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's correct, unfortunately. In fact, this is a mistake we made in the RFC--this API is impossible because it doesn't have access to the current context.
One option is to take the context as the first argument, but then this loses most of the convenience over the bind
method.
I'm thinking the simplest thing would be:
- Remove this method
- Rename
bind
back tonew
- Defer the creation of convenience methods to the future. They could potentially be built-in methods of the
Context
trait, likecx.event_handler(func)
. But there's an open question in my mind about whether you want to offer conveniences for both Rust callbacks (e.g.cx.event_handler(|cx| { ... }
) and JS callbacks (e.g.cx.event_handler(func)
). It seems OK to wait for some usage experience before we know exactly what convenience shorthands we want.
@geovie @kjvalencik What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's impossible, but the reference to the global context would need to be lazily created as part of the callback and not here.
With that said, I agree that it follows the existing patterns in neon better to add it to the Context
trait.
I also support deferring the convenience method to a separate PR.
src/eventhandler/mod.rs
Outdated
|
||
pub fn schedule<T, F>(&self, arg_cb: F) | ||
where T: Value, | ||
F: for<'a> FnOnce(&mut TaskContext<'a>) -> Vec<Handle<'a, T>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd love to have IntoIterator<...>
instead of Vec<..>
, but I couldn't get it to work with the lifetimes....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be a future improvement. I think to benefit from this, it would require a more holistic change to arguments in Neon.
src/eventhandler/mod.rs
Outdated
let func: Handle<JsFunction> = Handle::new_internal(JsFunction::from_raw(func)); | ||
let callback: Box<F> = Box::from_raw(mem::transmute(callback)); | ||
let args = callback(&mut cx); | ||
let _result = func.call(&mut cx, this, args); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here the result (return value or exception) is discarded, I'm not sure if this is okay.
EventHandler::schedule_with
was added to allow to use the result.
@dherman @kjvalencik I've updated the PR with the changes from the RFC |
@kjvalencik The PR is already up to date with the RFC, I will rebase it later. Any review is welcome |
@geovie Ah, sorry I missed that update! Thanks! I'll start reviewing. |
d166f1a
to
49b544b
Compare
49b544b
to
45d28f7
Compare
@kjvalencik rebased on master, should be good once CI passes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@geovie This is truly amazing work. Thank you so much! I do have a number of suggestions for improvement, as well as a couple of tweaks to the API we came up with in the RFC discussion. But I don't think it's too far away from being ready. Feel free to ping me on Slack to discuss!
src/eventhandler/mod.rs
Outdated
|
||
impl EventHandler { | ||
pub fn new(callback: Handle<JsFunction>) -> Self { | ||
TaskContext::with(|mut cx: TaskContext| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's correct, unfortunately. In fact, this is a mistake we made in the RFC--this API is impossible because it doesn't have access to the current context.
One option is to take the context as the first argument, but then this loses most of the convenience over the bind
method.
I'm thinking the simplest thing would be:
- Remove this method
- Rename
bind
back tonew
- Defer the creation of convenience methods to the future. They could potentially be built-in methods of the
Context
trait, likecx.event_handler(func)
. But there's an open question in my mind about whether you want to offer conveniences for both Rust callbacks (e.g.cx.event_handler(|cx| { ... }
) and JS callbacks (e.g.cx.event_handler(func)
). It seems OK to wait for some usage experience before we know exactly what convenience shorthands we want.
@geovie @kjvalencik What do you think?
Thanks a lot @geovie , I guess I cannot get the value out of the |
When will be this feature merged? |
Any updates on this? It will probably make Neon lot more useful and amazing. Because the current state of this solution limits us as we always have to use node as the data source. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are so close! Thank you so much for keeping on this, @geovie. We have so many people who will benefit from this work.
We just have one last thing to change, in order to ensure we aren't creating something that we'll have to break compatibility on for the N-API transition. See the comment inline for details.
Thanks again!
crates/neon-sys/native/src/neon.cc
Outdated
@@ -522,17 +522,23 @@ extern "C" void Neon_Task_Schedule(void *task, Neon_TaskPerformCallback perform, | |||
neon::queue_task(internal_task); | |||
} | |||
|
|||
extern "C" void* Neon_ThreadSafeCallback_New(v8::Local<v8::Value> self, v8::Local<v8::Function> callback) { | |||
extern "C" void* Neon_EventHandler_New(v8::Local<v8::Function> callback) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a nice idea, but unfortunately it's not future-proof for N-API, which doesn't have an equivalent of isolate::GetCurrent()
and isolate->GetCurrentContext()
. And in fact, this means the bind
API is also not future-proof for N-API.
What this means is, we really need the EventHandler
to take a Neon context as an explicit argument to its constructor(s).
So the best path forward is going to be: let's just drop this down to a single constructor, which takes three arguments: cx
, self
, and callback
. It's a noticeably less convenient API, but I think that's OK to get started. Down the road, we can experiment with convenience methods of cx
(just like the other convenience constructors such as cx.string(...)
etc), like cx.event_handler(...)
.
But let's wait on the convenience forms and for now just have one method in the API:
EventHandler::new(cx, self, callback)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dherman Should be good now, please take a final look! 😄
@Acanguven Thanks for keeping us on task (no pun intended) :P I've just done a review on the latest revisions. @geovie Hopefully the next round should be the last needed revision! |
30d0b2b
to
e62daa6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks perfect! Thanks for the amazing work, @geovie, I think this is ready to merge.
Ah, I see we're failing some Windows builds. I reran and it's failing again. Investigating... |
I was curious about this test failure. Here are the two interesting bits of the CI build that failed: |
So the compile-fail bug is probably just the usual flakiness of those tests (they're extremely sensitive to tiny changes in the Rust compiler's output formatting). I'm going to need to just disable those tests until we can come up with a better strategy for them. From searching around, I think the electron failure is also likely unrelated to this PR: it seems like this bug. I'm looking into what we can do to fix that issue. |
OK I can confirm that the workaround from that issue ( This is a bit annoying! We'll need to track that node-gyp issue until they release a fix. For the time being, maybe we can get CI working by applying the workaround in the Appveyor config. I'll try a little PR to do that, which should rebase fine with this PR. |
Testing out a workaround: #483 |
- add internal ThreadSafeCallback helper to schedule callbacks on the main thread
e62daa6
to
df135a5
Compare
I got CI working again on master with #483 so I went ahead and rebased this PR -- let's see how rerunning CI goes! 🤞 |
Doh! It looks like the changes in master did break this PR (my fault, since it took me so long to diagnose these CI issues). @geovie Would you like me to try to fix this in your PR? (GitHub gives me permission to update your PR, but I don't want to push non-rebase changes without your permission.) I'm happy to do it unless you'd rather. |
@dherman Go for it, I won't manage to do it this week... |
internal refactor.
Next step to get CI working should be to conditionally replace the implementation with |
using the N-API backend, since that's not yet implemented.
Looking forward to see that part going live |
Hi,
this adds a little helper to call a JavaScript function from any rust thread as requested in #197.
I'm not sure if this will need a RFC, but I can write one if requested.RFC
Feedback welcome, especially about the v8 scope setup.
Inspired by mika-fischer/napi-thread-safe-callback