Implement a Send Handle for the single-threaded Runtime (#340)

Implement a Send'able Handle for the single-threaded `Runtime` and
`CurrentThread` executor to spawn new tasks from other threads.
This commit is contained in:
Sebastian Dröge
2018-06-05 16:56:15 -07:00
committed by Carl Lerche
parent c07a7b26d3
commit 0d41ba7a08
4 changed files with 163 additions and 17 deletions
+9 -13
View File
@@ -17,11 +17,9 @@
//!
//! # Spawning from other threads
//!
//! By default, [`current_thread::Runtime`][rt] does not provide a way to spawn
//! tasks from other threads. However, this can be accomplished by using a
//! [`mpsc::channel`][chan]. To do so, create a channel to send the task, then
//! spawn a task on [`current_thread::Runtime`][rt] that consumes the channel
//! messages and spawns new tasks for them.
//! While [`current_thread::Runtime`][rt] does not implement `Send` and cannot
//! safely be moved to other threads, it provides a `Handle` that can be sent
//! to other threads and allows to spawn new tasks from there.
//!
//! For example:
//!
@@ -30,17 +28,15 @@
//! # extern crate futures;
//! use tokio::runtime::current_thread::Runtime;
//! use tokio::prelude::*;
//! use futures::sync::mpsc;
//! use std::thread;
//!
//! # fn main() {
//! let mut runtime = Runtime::new().unwrap();
//! let (tx, rx) = mpsc::channel(128);
//! # tx.send(future::ok(()));
//! let handle = runtime.handle();
//!
//! runtime.spawn(rx.for_each(|task| {
//! tokio::spawn(task);
//! Ok(())
//! }).map_err(|e| panic!("channel error")));
//! thread::spawn(move || {
//! handle.spawn(future::ok(()));
//! }).join().unwrap();
//!
//! # /*
//! runtime.run().unwrap();
@@ -69,4 +65,4 @@
mod runtime;
pub use self::runtime::Runtime;
pub use self::runtime::{Runtime, Handle};
+27 -1
View File
@@ -1,4 +1,5 @@
use executor::current_thread::{self, CurrentThread};
use executor::current_thread::Handle as ExecutorHandle;
use tokio_reactor::{self, Reactor};
use tokio_timer::timer::{self, Timer};
@@ -21,6 +22,23 @@ pub struct Runtime {
executor: CurrentThread<Timer<Reactor>>,
}
/// Handle to spawn a future on the corresponding `CurrentThread` runtime instance
#[derive(Debug, Clone)]
pub struct Handle(ExecutorHandle);
impl Handle {
/// Spawn a future onto the `CurrentThread` runtime instance corresponding to this handle
///
/// # Panics
///
/// This function panics if the spawn fails. Failure occurs if the `CurrentThread`
/// instance of the `Handle` does not exist anymore.
pub fn spawn<F>(&self, future: F) -> Result<(), tokio_executor::SpawnError>
where F: Future<Item = (), Error = ()> + Send + 'static {
self.0.spawn(future)
}
}
/// Error returned by the `run` function.
#[derive(Debug)]
pub struct RunError {
@@ -48,6 +66,14 @@ impl Runtime {
Ok(runtime)
}
/// Get a new handle to spawn futures on the single-threaded Tokio runtime
///
/// Different to the runtime itself, the handle can be sent to different
/// threads.
pub fn handle(&self) -> Handle {
Handle(self.executor.handle().clone())
}
/// Spawn a future onto the single-threaded Tokio runtime.
///
/// See [module level][mod] documentation for more details.
@@ -124,7 +150,7 @@ impl Runtime {
fn enter<F, R>(&mut self, f: F) -> R
where F: FnOnce(&mut current_thread::Entered<Timer<Reactor>>) -> R
{
let Runtime { ref reactor_handle, ref timer_handle, ref mut executor } = *self;
let Runtime { ref reactor_handle, ref timer_handle, ref mut executor, .. } = *self;
// Binds an executor to this thread
let mut enter = tokio_executor::enter().expect("Multiple executors at once");