tokio/task/
builder.rs

1#![allow(unreachable_pub)]
2use crate::{
3    runtime::{Handle, BOX_FUTURE_THRESHOLD},
4    task::{JoinHandle, LocalSet},
5    util::trace::SpawnMeta,
6};
7use std::{future::Future, io, mem};
8
9/// Factory which is used to configure the properties of a new task.
10///
11/// **Note**: This is an [unstable API][unstable]. The public API of this type
12/// may break in 1.x releases. See [the documentation on unstable
13/// features][unstable] for details.
14///
15/// Methods can be chained in order to configure it.
16///
17/// Currently, there is only one configuration option:
18///
19/// - [`name`], which specifies an associated name for
20///   the task
21///
22/// There are three types of task that can be spawned from a Builder:
23/// - [`spawn_local`] for executing futures on the current thread
24/// - [`spawn`] for executing [`Send`] futures on the runtime
25/// - [`spawn_blocking`] for executing blocking code in the
26///   blocking thread pool.
27///
28/// ## Example
29///
30/// ```no_run
31/// use tokio::net::{TcpListener, TcpStream};
32///
33/// use std::io;
34///
35/// async fn process(socket: TcpStream) {
36///     // ...
37/// # drop(socket);
38/// }
39///
40/// #[tokio::main]
41/// async fn main() -> io::Result<()> {
42///     let listener = TcpListener::bind("127.0.0.1:8080").await?;
43///
44///     loop {
45///         let (socket, _) = listener.accept().await?;
46///
47///         tokio::task::Builder::new()
48///             .name("tcp connection handler")
49///             .spawn(async move {
50///                 // Process each socket concurrently.
51///                 process(socket).await
52///             })?;
53///     }
54/// }
55/// ```
56/// [unstable]: crate#unstable-features
57/// [`name`]: Builder::name
58/// [`spawn_local`]: Builder::spawn_local
59/// [`spawn`]: Builder::spawn
60/// [`spawn_blocking`]: Builder::spawn_blocking
61#[derive(Default, Debug)]
62#[cfg_attr(docsrs, doc(cfg(all(tokio_unstable, feature = "tracing"))))]
63pub struct Builder<'a> {
64    name: Option<&'a str>,
65}
66
67impl<'a> Builder<'a> {
68    /// Creates a new task builder.
69    pub fn new() -> Self {
70        Self::default()
71    }
72
73    /// Assigns a name to the task which will be spawned.
74    pub fn name(&self, name: &'a str) -> Self {
75        Self { name: Some(name) }
76    }
77
78    /// Spawns a task with this builder's settings on the current runtime.
79    ///
80    /// # Panics
81    ///
82    /// This method panics if called outside of a Tokio runtime.
83    ///
84    /// See [`task::spawn`](crate::task::spawn()) for
85    /// more details.
86    #[track_caller]
87    pub fn spawn<Fut>(self, future: Fut) -> io::Result<JoinHandle<Fut::Output>>
88    where
89        Fut: Future + Send + 'static,
90        Fut::Output: Send + 'static,
91    {
92        let fut_size = mem::size_of::<Fut>();
93        Ok(if fut_size > BOX_FUTURE_THRESHOLD {
94            super::spawn::spawn_inner(Box::pin(future), SpawnMeta::new(self.name, fut_size))
95        } else {
96            super::spawn::spawn_inner(future, SpawnMeta::new(self.name, fut_size))
97        })
98    }
99
100    /// Spawn a task with this builder's settings on the provided [runtime
101    /// handle].
102    ///
103    /// See [`Handle::spawn`] for more details.
104    ///
105    /// [runtime handle]: crate::runtime::Handle
106    /// [`Handle::spawn`]: crate::runtime::Handle::spawn
107    #[track_caller]
108    pub fn spawn_on<Fut>(self, future: Fut, handle: &Handle) -> io::Result<JoinHandle<Fut::Output>>
109    where
110        Fut: Future + Send + 'static,
111        Fut::Output: Send + 'static,
112    {
113        let fut_size = mem::size_of::<Fut>();
114        Ok(if fut_size > BOX_FUTURE_THRESHOLD {
115            handle.spawn_named(Box::pin(future), SpawnMeta::new(self.name, fut_size))
116        } else {
117            handle.spawn_named(future, SpawnMeta::new(self.name, fut_size))
118        })
119    }
120
121    /// Spawns `!Send` a task on the current [`LocalSet`] with this builder's
122    /// settings.
123    ///
124    /// The spawned future will be run on the same thread that called `spawn_local`.
125    /// This may only be called from the context of a [local task set][`LocalSet`].
126    ///
127    /// # Panics
128    ///
129    /// This function panics if called outside of a [local task set][`LocalSet`].
130    ///
131    /// See [`task::spawn_local`] for more details.
132    ///
133    /// [`task::spawn_local`]: crate::task::spawn_local
134    /// [`LocalSet`]: crate::task::LocalSet
135    #[track_caller]
136    pub fn spawn_local<Fut>(self, future: Fut) -> io::Result<JoinHandle<Fut::Output>>
137    where
138        Fut: Future + 'static,
139        Fut::Output: 'static,
140    {
141        let fut_size = mem::size_of::<Fut>();
142        Ok(if fut_size > BOX_FUTURE_THRESHOLD {
143            super::local::spawn_local_inner(Box::pin(future), SpawnMeta::new(self.name, fut_size))
144        } else {
145            super::local::spawn_local_inner(future, SpawnMeta::new(self.name, fut_size))
146        })
147    }
148
149    /// Spawns `!Send` a task on the provided [`LocalSet`] with this builder's
150    /// settings.
151    ///
152    /// See [`LocalSet::spawn_local`] for more details.
153    ///
154    /// [`LocalSet::spawn_local`]: crate::task::LocalSet::spawn_local
155    /// [`LocalSet`]: crate::task::LocalSet
156    #[track_caller]
157    pub fn spawn_local_on<Fut>(
158        self,
159        future: Fut,
160        local_set: &LocalSet,
161    ) -> io::Result<JoinHandle<Fut::Output>>
162    where
163        Fut: Future + 'static,
164        Fut::Output: 'static,
165    {
166        let fut_size = mem::size_of::<Fut>();
167        Ok(if fut_size > BOX_FUTURE_THRESHOLD {
168            local_set.spawn_named(Box::pin(future), SpawnMeta::new(self.name, fut_size))
169        } else {
170            local_set.spawn_named(future, SpawnMeta::new(self.name, fut_size))
171        })
172    }
173
174    /// Spawns blocking code on the blocking threadpool.
175    ///
176    /// # Panics
177    ///
178    /// This method panics if called outside of a Tokio runtime.
179    ///
180    /// See [`task::spawn_blocking`](crate::task::spawn_blocking)
181    /// for more details.
182    #[track_caller]
183    pub fn spawn_blocking<Function, Output>(
184        self,
185        function: Function,
186    ) -> io::Result<JoinHandle<Output>>
187    where
188        Function: FnOnce() -> Output + Send + 'static,
189        Output: Send + 'static,
190    {
191        let handle = Handle::current();
192        self.spawn_blocking_on(function, &handle)
193    }
194
195    /// Spawns blocking code on the provided [runtime handle]'s blocking threadpool.
196    ///
197    /// See [`Handle::spawn_blocking`] for more details.
198    ///
199    /// [runtime handle]: crate::runtime::Handle
200    /// [`Handle::spawn_blocking`]: crate::runtime::Handle::spawn_blocking
201    #[track_caller]
202    pub fn spawn_blocking_on<Function, Output>(
203        self,
204        function: Function,
205        handle: &Handle,
206    ) -> io::Result<JoinHandle<Output>>
207    where
208        Function: FnOnce() -> Output + Send + 'static,
209        Output: Send + 'static,
210    {
211        use crate::runtime::Mandatory;
212        let fn_size = mem::size_of::<Function>();
213        let (join_handle, spawn_result) = if fn_size > BOX_FUTURE_THRESHOLD {
214            handle.inner.blocking_spawner().spawn_blocking_inner(
215                Box::new(function),
216                Mandatory::NonMandatory,
217                SpawnMeta::new(self.name, fn_size),
218                handle,
219            )
220        } else {
221            handle.inner.blocking_spawner().spawn_blocking_inner(
222                function,
223                Mandatory::NonMandatory,
224                SpawnMeta::new(self.name, fn_size),
225                handle,
226            )
227        };
228
229        spawn_result?;
230        Ok(join_handle)
231    }
232}