Skip to main content

pink_sidevm/
time.rs

1//! Provides functionalities in tokio::time.
2
3use super::*;
4
5use core::pin::Pin;
6use derive_more::{Display, Error};
7use std::future::Future;
8use std::task::{Context, Poll};
9use std::time::Duration;
10
11use crate::ResourceId;
12
13/// The future to sleep for a given duration.
14pub struct Sleep {
15    id: ResourceId,
16}
17
18/// Sleep for the specified duration.
19///
20/// # Example
21/// ```ignore
22/// use pink_sidevm::time;
23/// time::sleep(Duration::from_millis(100)).await;
24/// ```
25pub fn sleep(duration: Duration) -> Sleep {
26    let id = ocall::create_timer(duration.as_millis() as i32).expect("failed to create timer");
27    Sleep { id: ResourceId(id) }
28}
29
30impl Future for Sleep {
31    type Output = ();
32
33    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
34        use env::OcallError;
35        let waker_id = env::tasks::intern_waker(cx.waker().clone());
36        let rv = ocall::poll_read(waker_id, self.id.0, &mut []);
37        match rv {
38            Ok(_) => Poll::Ready(()),
39            Err(OcallError::Pending) => Poll::Pending,
40            Err(err) => panic!("unexpected error: {:?}", err),
41        }
42    }
43}
44
45/// Indicates that a timeout has elapsed for `timeout(future)`.
46#[derive(Display, Error, Debug)]
47pub struct TimedOut;
48
49/// Timeout the provided future for the specified duration.
50pub async fn timeout<T: Future<Output = O>, O>(
51    duration: Duration,
52    future: T,
53) -> Result<O, TimedOut> {
54    use futures::FutureExt;
55    futures::select! {
56        v = future.fuse() => Ok(v),
57        _ = sleep(duration).fuse() => Err(TimedOut),
58    }
59}
60
61/// The future returned by `maybe_rest`.
62pub struct Rest {
63    resting: bool,
64}
65
66impl Rest {
67    /// Creates a new `Rest` instance.
68    pub fn new(resting: bool) -> Self {
69        Rest { resting }
70    }
71}
72
73impl Future for Rest {
74    type Output = ();
75    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
76        if self.resting {
77            // Return `Pending` and become Ready immediately.
78            self.resting = false;
79            cx.waker().wake_by_ref();
80            Poll::Pending
81        } else {
82            Poll::Ready(())
83        }
84    }
85}
86
87/// Take a rest if it is about to be stifled
88///
89/// If the remaining gas of current slot is less than 30%, the returned future will be `Pending` on
90/// first poll and become `Ready` again immediately.
91/// If the remaining gas is equal or more than 30%, the returned future will be `Ready` immediately.
92pub fn maybe_rest() -> Rest {
93    let remaining = ocall::gas_remaining().expect("failed to get gas remaining");
94    // Yield if there is less than 30% of gas remaining.
95    Rest::new(remaining < 30)
96}