Simplifying Task Spawning in Tokio with LocalRuntime
Core Problem
Currently, to spawn !Send tasks on Tokio, you must use a combination of a current-thread runtime and LocalSet to make that work. However, tasks on a LocalSet are separate from the rest of the runtime in an uncomfortable way.
Solution & Analysis
Introducing LocalRuntime
To address this issue, we propose introducing a new type called LocalRuntime, which behaves exactly like a current-thread Runtime. The key differences between LocalRuntime and Runtime are:
- The
LocalRuntimetype is!Sendto enable spawning!Sendtasks directly onto the runtime. - From within a
LocalRuntime, thetokio::spawnandspawn_localfunctions have identical behavior. - Tasks spawned using
spawn_localare scheduled in the same way as normaltokio::spawntasks.
Implementation
The implementation of LocalRuntime will be similar to that of Runtime, with some modifications to accommodate its new features. Here is a high-level overview of how it could work:
use tokio::runtime::{Builder, Runtime};
use tokio::task::{JoinHandle, Spawner};
pub struct LocalRuntime {
runtime: Runtime,
}
impl LocalRuntime {
pub fn new() -> Self {
Self {
runtime: Builder::new_multi_thread().build().unwrap(),
}
}
// Expose tokio::spawn and spawn_local with identical behavior
pub async fn spawn<F, R>(&self, task: F) -> JoinHandle<R>
where
F: FnOnce() + Send + 'static,
{
self.runtime.spawn(task)
}
pub async fn spawn_local<F>(&self, task: F) -> JoinHandle<()>
where
F: FnOnce() + Send + 'static,
{
self.spawn(move || task())
}
}
Benefits
The introduction of LocalRuntime will provide several benefits:
- Simplified task spawning: With
LocalRuntime, you can spawn tasks using eitherspawnorspawn_local, without having to worry about the complexities ofLocalSet. - Improved performance: By avoiding the overhead of
LocalSet, tasks spawned onLocalRuntimeshould be faster. - Better compatibility with Deno and other runtimes.
Conclusion
In this blog post, we introduced a new type called LocalRuntime, which simplifies task spawning in Tokio by providing a seamless experience for both !Send and non-!Send tasks. With its !Send behavior, identical spawn and spawn_local functionality, and performance benefits, LocalRuntime is an attractive alternative to the current approach using Runtime and LocalSet.