Some logic needs to fire tokio::spawn and related runtime functionalities, and then basically forget it.
These logic often executes in a rayon threadpool
Explicitly passing tokio::runtime::Handle around will damage other interface designs
Therefore, it is better to pave all rayon threads during their initialization with tokio contexts.
In order to pave rayon threads, I can only think of calling tokio::runtime::Runtime::enter and then std::mem::forget the tokio::runtime::EnterGuard. Basically,
rayon_threadpool.broadcast(|_| {
let _guard = tokio_runtime.enter();
std::mem::forget(_guard);
});
Then the rayon worker threads can fire as many tokio functions in a non-blocking manner, as long as they are alive.
Questions:
Is it even a recommended pattern?
If rayon threadpool is shut down, does the leaked EnterGuard affect tokio runtime? We can safely assume the shut down is rare and is not rapidly leaking stuff.
As you pointed out, that's a memory leak. It may be untenable for a long-lived process, especially if it happens often or has the potential to happen often because the leak occurs in a public API. There are no correctness issues with leaking the guard, however.
Avoiding the need to pass a Handle is a matter of using static or scoped references that the thread pool has access to. Static if the tokio runtime is shared application-wide, or scoped if different thread pools need to use their own runtimes. Something like that should work [1].
Tokio itself uses thread-locals for setting the current runtime context. âŠī¸
If you're creating a rayon thread pool explicitly, you can use ThreadPoolBuilder::spawn_handler() to provide a thread-spawning function that does the enter() without forgetting anything.