mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-08 00:00:13 +02:00
task: add LocalSet example (#3438)
This commit is contained in:
+105
-4
@@ -43,10 +43,13 @@ cfg_rt! {
|
|||||||
/// }).await.unwrap();
|
/// }).await.unwrap();
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
/// In order to spawn `!Send` futures, we can use a local task set to
|
///
|
||||||
/// schedule them on the thread calling [`Runtime::block_on`]. When running
|
/// # Use with `run_until`
|
||||||
/// inside of the local task set, we can use [`task::spawn_local`], which can
|
///
|
||||||
/// spawn `!Send` futures. For example:
|
/// To spawn `!Send` futures, we can use a local task set to schedule them
|
||||||
|
/// on the thread calling [`Runtime::block_on`]. When running inside of the
|
||||||
|
/// local task set, we can use [`task::spawn_local`], which can spawn
|
||||||
|
/// `!Send` futures. For example:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use std::rc::Rc;
|
/// use std::rc::Rc;
|
||||||
@@ -71,6 +74,9 @@ cfg_rt! {
|
|||||||
/// }).await;
|
/// }).await;
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
/// **Note:** The `run_until` method can only be used in `#[tokio::main]`,
|
||||||
|
/// `#[tokio::test]` or directly inside a call to [`Runtime::block_on`]. It
|
||||||
|
/// cannot be used inside a task spawned with `tokio::spawn`.
|
||||||
///
|
///
|
||||||
/// ## Awaiting a `LocalSet`
|
/// ## Awaiting a `LocalSet`
|
||||||
///
|
///
|
||||||
@@ -104,11 +110,106 @@ cfg_rt! {
|
|||||||
/// local.await;
|
/// local.await;
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
/// **Note:** Awaiting a `LocalSet` can only be done inside
|
||||||
|
/// `#[tokio::main]`, `#[tokio::test]` or directly inside a call to
|
||||||
|
/// [`Runtime::block_on`]. It cannot be used inside a task spawned with
|
||||||
|
/// `tokio::spawn`.
|
||||||
|
///
|
||||||
|
/// ## Use inside `tokio::spawn`
|
||||||
|
///
|
||||||
|
/// The two methods mentioned above cannot be used inside `tokio::spawn`, so
|
||||||
|
/// to spawn `!Send` futures from inside `tokio::spawn`, we need to do
|
||||||
|
/// something else. The solution is to create the `LocalSet` somewhere else,
|
||||||
|
/// and communicate with it using an [`mpsc`] channel.
|
||||||
|
///
|
||||||
|
/// The following example puts the `LocalSet` inside a new thread.
|
||||||
|
/// ```
|
||||||
|
/// use tokio::runtime::Builder;
|
||||||
|
/// use tokio::sync::{mpsc, oneshot};
|
||||||
|
/// use tokio::task::LocalSet;
|
||||||
|
///
|
||||||
|
/// // This struct describes the task you want to spawn. Here we include
|
||||||
|
/// // some simple examples. The oneshot channel allows sending a response
|
||||||
|
/// // to the spawner.
|
||||||
|
/// #[derive(Debug)]
|
||||||
|
/// enum Task {
|
||||||
|
/// PrintNumber(u32),
|
||||||
|
/// AddOne(u32, oneshot::Sender<u32>),
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[derive(Clone)]
|
||||||
|
/// struct LocalSpawner {
|
||||||
|
/// send: mpsc::UnboundedSender<Task>,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl LocalSpawner {
|
||||||
|
/// pub fn new() -> Self {
|
||||||
|
/// let (send, mut recv) = mpsc::unbounded_channel();
|
||||||
|
///
|
||||||
|
/// let rt = Builder::new_current_thread()
|
||||||
|
/// .enable_all()
|
||||||
|
/// .build()
|
||||||
|
/// .unwrap();
|
||||||
|
///
|
||||||
|
/// std::thread::spawn(move || {
|
||||||
|
/// let local = LocalSet::new();
|
||||||
|
///
|
||||||
|
/// local.spawn_local(async move {
|
||||||
|
/// while let Some(new_task) = recv.recv().await {
|
||||||
|
/// tokio::task::spawn_local(run_task(new_task));
|
||||||
|
/// }
|
||||||
|
/// // If the while loop returns, then all the LocalSpawner
|
||||||
|
/// // objects have have been dropped.
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// // This will return once all senders are dropped and all
|
||||||
|
/// // spawned tasks have returned.
|
||||||
|
/// rt.block_on(local);
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// Self {
|
||||||
|
/// send,
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// pub fn spawn(&self, task: Task) {
|
||||||
|
/// self.send.send(task).expect("Thread with LocalSet has shut down.");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // This task may do !Send stuff. We use printing a number as an example,
|
||||||
|
/// // but it could be anything.
|
||||||
|
/// //
|
||||||
|
/// // The Task struct is an enum to support spawning many different kinds
|
||||||
|
/// // of operations.
|
||||||
|
/// async fn run_task(task: Task) {
|
||||||
|
/// match task {
|
||||||
|
/// Task::PrintNumber(n) => {
|
||||||
|
/// println!("{}", n);
|
||||||
|
/// },
|
||||||
|
/// Task::AddOne(n, response) => {
|
||||||
|
/// // We ignore failures to send the response.
|
||||||
|
/// let _ = response.send(n + 1);
|
||||||
|
/// },
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[tokio::main]
|
||||||
|
/// async fn main() {
|
||||||
|
/// let spawner = LocalSpawner::new();
|
||||||
|
///
|
||||||
|
/// let (send, response) = oneshot::channel();
|
||||||
|
/// spawner.spawn(Task::AddOne(10, send));
|
||||||
|
/// let eleven = response.await.unwrap();
|
||||||
|
/// assert_eq!(eleven, 11);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`Send`]: trait@std::marker::Send
|
/// [`Send`]: trait@std::marker::Send
|
||||||
/// [local task set]: struct@LocalSet
|
/// [local task set]: struct@LocalSet
|
||||||
/// [`Runtime::block_on`]: method@crate::runtime::Runtime::block_on
|
/// [`Runtime::block_on`]: method@crate::runtime::Runtime::block_on
|
||||||
/// [`task::spawn_local`]: fn@spawn_local
|
/// [`task::spawn_local`]: fn@spawn_local
|
||||||
|
/// [`mpsc`]: mod@crate::sync::mpsc
|
||||||
pub struct LocalSet {
|
pub struct LocalSet {
|
||||||
/// Current scheduler tick
|
/// Current scheduler tick
|
||||||
tick: Cell<u8>,
|
tick: Cell<u8>,
|
||||||
|
|||||||
Reference in New Issue
Block a user