tokio/runtime/blocking/
task.rs

1use std::future::Future;
2use std::pin::Pin;
3use std::task::{Context, Poll};
4
5/// Converts a function to a future that completes on poll.
6pub(crate) struct BlockingTask<T> {
7    func: Option<T>,
8}
9
10impl<T> BlockingTask<T> {
11    /// Initializes a new blocking task from the given function.
12    pub(crate) fn new(func: T) -> BlockingTask<T> {
13        BlockingTask { func: Some(func) }
14    }
15}
16
17// The closure `F` is never pinned
18impl<T> Unpin for BlockingTask<T> {}
19
20impl<T, R> Future for BlockingTask<T>
21where
22    T: FnOnce() -> R + Send + 'static,
23    R: Send + 'static,
24{
25    type Output = R;
26
27    fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<R> {
28        let me = &mut *self;
29        let func = me
30            .func
31            .take()
32            .expect("[internal exception] blocking task ran twice.");
33
34        // This is a little subtle:
35        // For convenience, we'd like _every_ call tokio ever makes to Task::poll() to be budgeted
36        // using coop. However, the way things are currently modeled, even running a blocking task
37        // currently goes through Task::poll(), and so is subject to budgeting. That isn't really
38        // what we want; a blocking task may itself want to run tasks (it might be a Worker!), so
39        // we want it to start without any budgeting.
40        crate::runtime::coop::stop();
41
42        Poll::Ready(func())
43    }
44}