• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::future::Future;
2 use std::pin::Pin;
3 use std::task::{Context, Poll};
4 
5 /// Converts a function to a future that completes on poll
6 pub(crate) struct BlockingTask<T> {
7     func: Option<T>,
8 }
9 
10 impl<T> BlockingTask<T> {
11     /// Initializes a new blocking task from the given function
new(func: T) -> BlockingTask<T>12     pub(crate) fn new(func: T) -> BlockingTask<T> {
13         BlockingTask { func: Some(func) }
14     }
15 }
16 
17 // The closure `F` is never pinned
18 impl<T> Unpin for BlockingTask<T> {}
19 
20 impl<T, R> Future for BlockingTask<T>
21 where
22     T: FnOnce() -> R + Send + 'static,
23     R: Send + 'static,
24 {
25     type Output = R;
26 
poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<R>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::coop::stop();
41 
42         Poll::Ready(func())
43     }
44 }
45