From 894eb8b83fa8edbee2b67e1a6a8e7a1c3d74c096 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 24 Apr 2020 21:05:10 +0200 Subject: [PATCH] runtime: improve runtime and handle doc (#2440) Refs: #2437 --- tokio/src/runtime/builder.rs | 3 +- tokio/src/runtime/handle.rs | 120 ++++++++++++++++++++++++++++++++--- tokio/src/runtime/mod.rs | 51 +++++++++++++-- 3 files changed, 156 insertions(+), 18 deletions(-) diff --git a/tokio/src/runtime/builder.rs b/tokio/src/runtime/builder.rs index cfde99825..56fb898ad 100644 --- a/tokio/src/runtime/builder.rs +++ b/tokio/src/runtime/builder.rs @@ -399,12 +399,13 @@ cfg_rt_core! { /// Sets runtime to use a simpler scheduler that runs all tasks on the current-thread. /// /// The executor and all necessary drivers will all be run on the current - /// thread during `block_on` calls. + /// thread during [`block_on`] calls. /// /// See also [the module level documentation][1], which has a section on scheduler /// types. /// /// [1]: index.html#runtime-configurations + /// [`block_on`]: Runtime::block_on pub fn basic_scheduler(&mut self) -> &mut Self { self.kind = Kind::Basic; self diff --git a/tokio/src/runtime/handle.rs b/tokio/src/runtime/handle.rs index b232431ba..a5761b6a9 100644 --- a/tokio/src/runtime/handle.rs +++ b/tokio/src/runtime/handle.rs @@ -31,7 +31,39 @@ pub struct Handle { } impl Handle { - /// Enter the runtime context. + /// Enter the runtime context. This allows you to construct types that must + /// have an executor available on creation such as [`Delay`] or [`TcpStream`]. + /// It will also allow you to call methods such as [`tokio::spawn`]. + /// + /// This function is also available as [`Runtime::enter`]. + /// + /// [`Delay`]: struct@crate::time::Delay + /// [`TcpStream`]: struct@crate::net::TcpStream + /// [`Runtime::enter`]: fn@crate::runtime::Runtime::enter + /// [`tokio::spawn`]: fn@crate::spawn + /// + /// # Example + /// + /// ``` + /// use tokio::runtime::Runtime; + /// + /// fn function_that_spawns(msg: String) { + /// // Had we not used `handle.enter` below, this would panic. + /// tokio::spawn(async move { + /// println!("{}", msg); + /// }); + /// } + /// + /// fn main() { + /// let rt = Runtime::new().unwrap(); + /// let handle = rt.handle().clone(); + /// + /// let s = "Hello World!".to_string(); + /// + /// // By entering the context, we tie `tokio::spawn` to this executor. + /// handle.enter(|| function_that_spawns(s)); + /// } + /// ``` pub fn enter(&self, f: F) -> R where F: FnOnce() -> R, @@ -110,8 +142,14 @@ cfg_rt_core! { /// /// # Panics /// - /// This function panics if the spawn fails. Failure occurs if the executor - /// is currently at capacity and is unable to spawn a new future. + /// This function will not panic unless task execution is disabled on the + /// executor. This can only happen if the runtime was built using + /// [`Builder`] without picking either [`basic_scheduler`] or + /// [`threaded_scheduler`]. + /// + /// [`Builder`]: struct@crate::runtime::Builder + /// [`threaded_scheduler`]: fn@crate::runtime::Builder::threaded_scheduler + /// [`basic_scheduler`]: fn@crate::runtime::Builder::basic_scheduler pub fn spawn(&self, future: F) -> JoinHandle where F: Future + Send + 'static, @@ -127,26 +165,47 @@ cfg_rt_core! { /// complete, and yielding its resolved result. Any tasks or timers which /// the future spawns internally will be executed on the runtime. /// - /// This method should not be called from an asynchronous context. + /// If the provided executor currently has no active core thread, this + /// function might hang until a core thread is added. This is not a + /// concern when using the [threaded scheduler], as it always has active + /// core threads, but if you use the [basic scheduler], some other + /// thread must currently be inside a call to [`Runtime::block_on`]. + /// See also [the module level documentation][1], which has a section on + /// scheduler types. + /// + /// This method may not be called from an asynchronous context. + /// + /// [threaded scheduler]: fn@crate::runtime::Builder::threaded_scheduler + /// [basic scheduler]: fn@crate::runtime::Builder::basic_scheduler + /// [`Runtime::block_on`]: fn@crate::runtime::Runtime::block_on + /// [1]: index.html#runtime-configurations /// /// # Panics /// - /// This function panics if the executor is at capacity, if the provided - /// future panics, or if called within an asynchronous execution context. + /// This function panics if the provided future panics, or if called + /// within an asynchronous execution context. /// /// # Examples /// - /// ```no_run + /// Using `block_on` with the [threaded scheduler]. + /// + /// ``` /// use tokio::runtime::Runtime; /// use std::thread; /// - /// // Create the runtime + /// // Create the runtime. + /// // + /// // If the rt-threaded feature is enabled, this creates a threaded + /// // scheduler by default. /// let rt = Runtime::new().unwrap(); /// let handle = rt.handle().clone(); /// - /// // Use the runtime from another thread + /// // Use the runtime from another thread. /// let th = thread::spawn(move || { - /// // Execute the future, blocking the current thread until completion + /// // Execute the future, blocking the current thread until completion. + /// // + /// // This example uses the threaded scheduler, so no concurrent call to + /// // `rt.block_on` is required. /// handle.block_on(async { /// println!("hello"); /// }); @@ -154,6 +213,47 @@ cfg_rt_core! { /// /// th.join().unwrap(); /// ``` + /// + /// Using the [basic scheduler] requires a concurrent call to + /// [`Runtime::block_on`]: + /// + /// [threaded scheduler]: fn@crate::runtime::Builder::threaded_scheduler + /// [basic scheduler]: fn@crate::runtime::Builder::basic_scheduler + /// [`Runtime::block_on`]: fn@crate::runtime::Runtime::block_on + /// + /// ``` + /// use tokio::runtime::Builder; + /// use tokio::sync::oneshot; + /// use std::thread; + /// + /// // Create the runtime. + /// let mut rt = Builder::new() + /// .enable_all() + /// .basic_scheduler() + /// .build() + /// .unwrap(); + /// + /// let handle = rt.handle().clone(); + /// + /// // Signal main thread when task has finished. + /// let (send, recv) = oneshot::channel(); + /// + /// // Use the runtime from another thread. + /// let th = thread::spawn(move || { + /// // Execute the future, blocking the current thread until completion. + /// handle.block_on(async { + /// send.send("done").unwrap(); + /// }); + /// }); + /// + /// // The basic scheduler is used, so the thread above might hang if we + /// // didn't call block_on on the rt too. + /// rt.block_on(async { + /// assert_eq!(recv.await.unwrap(), "done"); + /// }); + /// # th.join().unwrap(); + /// ``` + /// pub fn block_on(&self, future: F) -> F::Output { self.enter(|| { let mut enter = crate::runtime::enter(true); diff --git a/tokio/src/runtime/mod.rs b/tokio/src/runtime/mod.rs index 293f5c936..9b7d41a3b 100644 --- a/tokio/src/runtime/mod.rs +++ b/tokio/src/runtime/mod.rs @@ -379,8 +379,14 @@ impl Runtime { /// /// # Panics /// - /// This function panics if the spawn fails. Failure occurs if the executor - /// is currently at capacity and is unable to spawn a new future. + /// This function will not panic unless task execution is disabled on the + /// executor. This can only happen if the runtime was built using + /// [`Builder`] without picking either [`basic_scheduler`] or + /// [`threaded_scheduler`]. + /// + /// [`Builder`]: struct@Builder + /// [`threaded_scheduler`]: fn@Builder::threaded_scheduler + /// [`basic_scheduler`]: fn@Builder::basic_scheduler #[cfg(feature = "rt-core")] pub fn spawn(&self, future: F) -> JoinHandle where @@ -407,12 +413,12 @@ impl Runtime { /// configured. [`runtime::Handle::block_on`][handle] provides a version /// that takes `&self`. /// - /// This method should not be called from an asynchronous context. + /// This method may not be called from an asynchronous context. /// /// # Panics /// - /// This function panics if the executor is at capacity, if the provided - /// future panics, or if called within an asynchronous execution context. + /// This function panics if the provided future panics, or if called within an + /// asynchronous execution context. /// /// # Examples /// @@ -441,7 +447,38 @@ impl Runtime { }) } - /// Enter the runtime context. + /// Enter the runtime context. This allows you to construct types that must + /// have an executor available on creation such as [`Delay`] or [`TcpStream`]. + /// It will also allow you to call methods such as [`tokio::spawn`]. + /// + /// This function is also available as [`Handle::enter`]. + /// + /// [`Delay`]: struct@crate::time::Delay + /// [`TcpStream`]: struct@crate::net::TcpStream + /// [`Handle::enter`]: fn@crate::runtime::Handle::enter + /// [`tokio::spawn`]: fn@crate::spawn + /// + /// # Example + /// + /// ``` + /// use tokio::runtime::Runtime; + /// + /// fn function_that_spawns(msg: String) { + /// // Had we not used `rt.enter` below, this would panic. + /// tokio::spawn(async move { + /// println!("{}", msg); + /// }); + /// } + /// + /// fn main() { + /// let rt = Runtime::new().unwrap(); + /// + /// let s = "Hello World!".to_string(); + /// + /// // By entering the context, we tie `tokio::spawn` to this executor. + /// rt.enter(|| function_that_spawns(s)); + /// } + /// ``` pub fn enter(&self, f: F) -> R where F: FnOnce() -> R, @@ -476,7 +513,7 @@ impl Runtime { /// Usually, dropping a `Runtime` handle is sufficient as tasks are able to /// shutdown in a timely fashion. However, dropping a `Runtime` will wait /// indefinitely for all tasks to terminate, and there are cases where a long - /// blocking task has been spawned which can block dropping `Runtime`. + /// blocking task has been spawned, which can block dropping `Runtime`. /// /// In this case, calling `shutdown_timeout` with an explicit wait timeout /// can work. The `shutdown_timeout` will signal all tasks to shutdown and