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