Hi, I am using a rust binding library rusty_v8, since it's from C++ world, and the rust bindings are actually !Send and !Sync, all smart pointers have to be Rc, and all the interops (rust implemented builtin APIs, exposed to javascript side) have to be sync.
When using it with tokio runtime, I'm trying to send a message inside a sync function to the main thread (main loop) handled by tokio::select!.
The API I'm using is mpsc::Sender::blocking_send. Everything seems correct when I run cargo build, but when I start my command line to run it, there's an error:
thread 'main' panicked at src\js\binding\global.rs:76:6:
Cannot block the current thread from within a runtime. This happens because a function attempted to block the current thread while the thread is being used to drive asynchronous tasks.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at library\core\src\panicking.rs:221:5:
panic in a function that cannot unwind
Is there any best practice I can follow for such pattern? Maybe I need to find a way to convert these sync functions to futures::Poll type?
Why can't you make the function asynchronously send the value? If you can't mark the function with async, you can get a handle to the runtime you are currently in (which you are as otherwise the panic wouldn't occur) with Handle::current and do the asynchronous send by spawning a task with Handle::spawn.
Thanks for teaching, I think I have one more question:
If I use Handle::current and Handle::spawn, is there a way to join these tasks after complete? In my current codebase, I'm using TaskTracker to spawn async tasks, so these tasks will be automatically be joined.
After I rewrite my code with TaskTracker, instead of using Handle::current and Handle::spawn APIs (sorry I didn't follow your instructions yet, but I will try later) The cargo build is current, and there's a new error:
thread 'main' panicked at src\js\binding\global.rs:76:34:
`spawn_local` called from outside of a `task::LocalSet`
stack backtrace:
Can you use task::spawn_local instead to spawn the future with the send? TaskTracker can also be used. Without seeing your code, we can only guess what your setup looks like.
When create the EventLoop structure, it creates a task_tracker inside it.
It also creates the JsRuntime structure, and copied a clone of task_tracker into it.
Initialize the JsRuntime (it wraps the V8 engine, and provide most of interops with javascript). It will execute a javascript file during the initialization.
Inside the javascript file, it calls a function setTimeout which is implemented in rust side (named set_timeout).
Start the loop of tokio::select! on multiple channels and stdin/stdout.
The error message is throwed in step-4.
One thing need to notice is: the step-2 and step-3 is running inside the V8 engine, which is a C++ library, maybe some context is missing when rust application invokes C++ library, and C++ library calls interops provided by rust bindings.
I can't find the place where you create and execute a LocalSet in your repo (I'd expect this in your main function, more specifically inside the future you pass to Runtime::block_on). Without a LocalSet you can't spawn thread-local, !Send futures via any sort of spawn_local call. The runtime handle does not contain a LocalSet, you need to create one yourself. You can simply wrap your event loop logic in a call to LocalSet::run_until and continue to use your TaskTracker. Note though that you can'tspawn_local from a task that you spawned with task::spawn.
The block_on API need to run with a Runtime, not a runtime Handle, how can I get the current runtime this function is running in?
The run_until API need to call await to take effect, which means the caller function will also to be an async function. But the caller function has to be a sync function because it will be binded into V8 as a callback.
The spawn_local API will never be triggered or scheduled by the runtime.
The block_on API failed, the error message is: Cannot start a runtime from within a runtime. This happens because a function (like block_on) attempted to block the current thread while the thread is being used to drive asynchronous tasks.
How to send a message from sync world to tokio's async world?
Somehow, I believe this is a deep gap between tokio's async world and the normal sync world.
Because it seems tokio frameworks handles everything in an async way, and also requires user code to make everything async, but it conflicts with the V8 API which is from C++ world, i.e. the old and normal sync world.
Actually I just want to send a message from the sync world to tokio's async world, to trigger the tokio::select! to run a loop for once.
I'm not sure if I could simply use a std::sync::mpsc::Sender to send a message inside the sync function, and let tokio::select! listen to the std::sync::mpsc::Receiver concurrently with other async triggers?
In this case, why is this "sync world" function ever running inside runtime? Shouldn't it be on a different thread, where blocking, including send_blocking, is a correct way?
Spawn the V8 engine running in a completely separate thread (i.e. the std::thread::spawn), the solution will requires too many engineering effort to sync data between the V8 and tokio runtime.