1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
use std::fmt::Debug;
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
use std::thread;
use std::time::Duration;

/// It implements the [`Future`](/std/future/trait.Future.html) trait.
///
/// # Examples
///
/// Create a [`Future`](/std/future/trait.Future.html) to be ready after some point:
///
/// ```
/// use std::time::Duration;
/// use settimeout::set_timeout;
/// use futures::executor::block_on;
///
/// async fn foo() {
///   println!("The Future will be ready after some time");
///   set_timeout(Duration::from_secs(5)).await;
///   println!("Now, it is ready");
/// }
///
/// block_on(foo());
/// ```
#[derive(Debug)]
pub struct Timer {
  state: Arc<Mutex<TimerState>>,
  duration: Duration,
}

impl Timer {
  /// Creates a new instance of `Timer` to be used as a `impl Future`.
  pub fn new(duration: Duration) -> Timer {
    Timer {
      state: Arc::new(Mutex::new(TimerState {
        timed_out: false,
        poll_called_already: false,
      })),
      duration,
    }
  }

  fn spawn_timer_thread(&self, cx: &mut Context<'_>) {
    let duration = self.duration;
    let state = Arc::clone(&self.state);
    let waker = cx.waker().clone();

    thread::spawn(move || {
      thread::sleep(duration);
      let mut state = state
        .lock()
        .expect("Couldn't lock the Timer state inside the Timer thread");
      (*state).timed_out = true;
      waker.wake();
    });
  }
}

impl Future for Timer {
  type Output = ();

  fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
    let mut state = self.state.lock().expect("Couldn't lock the Timer state");

    if state.timed_out {
      Poll::Ready(())
    } else if state.poll_called_already {
      Poll::Pending
    } else {
      (*state).poll_called_already = true;
      self.spawn_timer_thread(cx);
      Poll::Pending
    }
  }
}

#[derive(Debug)]
struct TimerState {
  timed_out: bool,
  poll_called_already: bool,
}

/// It returns an implementation of [`Future`](/std/future/trait.Future.html) trait.
///
/// # Examples
///
/// Create a [`Future`](/std/future/trait.Future.html) to be ready after some point:
///
/// ```
/// use std::time::Duration;
/// use settimeout::set_timeout;
/// use futures::executor::block_on;
///
/// async fn foo() {
///   println!("The Future will be ready after some time");
///   set_timeout(Duration::from_secs(5)).await;
///   println!("Now, it is ready");
/// }
///
/// block_on(foo());
/// ```
pub fn set_timeout(duration: Duration) -> impl Future {
  Timer::new(duration)
}