Skip to content
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

Question: Calling JS callbacks in rayon #1006

Closed
nukeop opened this issue Nov 12, 2023 · 2 comments
Closed

Question: Calling JS callbacks in rayon #1006

nukeop opened this issue Nov 12, 2023 · 2 comments

Comments

@nukeop
Copy link

nukeop commented Nov 12, 2023

I'm passing a callback from JS to Rust. Then, when iterating over a collection, I want to call it with some parameters during each iteration (it's how I track progress of a long-running Rust function in JS).

However, if I do the same in a rayon ParallelIterator, understandably I'm getting this error:

`*mut napi::bindings::types::Value__` cannot be shared between threads safely
within `[closure@blahblah/lib.rs:103:14: 103:20]`, the trait `Sync` is not implemented for `*mut napi::bindings::types::Value__`
required because it appears within the type `&Handle<'_, JsFunction>`

Is there any way to do this? Any workarounds? For clarity, here's a simplified version of what I want to do:

let my_callback: Handle<JsFunction> = cx.argument(0)?;
let chars = vec!['a', 'b', 'c'];
let result: Vec<_> = chars
    .into_par_iter()
    .for_each(|| {
        my_callback(cx, cx.undefined(), cx.undefined());
    })
    .collect();

Inside the for_each, I don't want to modify any JS objects or values, I only want to call the callback I passed and JS will handle the rest.

@kjvalencik
Copy link
Member

JavaScript methods/values are only accessible from the main thread. They can be held in other threads with Root, but require calling back to the main thread for use.

With your use of rayon, is the Neon function synchronous (i.e., no callback or promise)? The example seems like that's the case. If that's the case, you still need some way to synchronize back to the main thread (since multiple calls to for_each would be concurrent).

A possible option would be to create a mpsc channel where each call to for_each can send and then a single consumer outside of the parallel iterator consumes and calls the function.

It looks like the indicatif crate does something similar. https://docs.rs/indicatif/0.17.7/indicatif/trait.ParallelProgressIterator.html

@nukeop
Copy link
Author

nukeop commented Nov 13, 2023

Thanks for the advice, I'll look into this. My use case is scanning a music library - Rust opens audio files, extracts metadata, and generates thumbnails for every album, and it's all synchronous. The callback is used to report progress back to JS. I'm a Rust n00b, so I didn't know about mpsc, but it looks promising.

@nukeop nukeop closed this as completed Nov 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants