A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

87 lines
1.9 KiB

  1. use std::{
  2. future::Future,
  3. pin::Pin,
  4. sync::{Arc, Mutex},
  5. task::{Context, Poll, Waker},
  6. thread,
  7. time::Duration,
  8. };
  9. pub use async_trait::async_trait;
  10. pub fn run<F: Future>(future: F) -> F::Output {
  11. tokio::runtime::Runtime::new().unwrap().block_on(future)
  12. }
  13. pub use tokio::try_join;
  14. #[derive(Debug)]
  15. pub struct TimerFuture {
  16. state: Arc<Mutex<State>>,
  17. }
  18. #[derive(Debug)]
  19. enum State {
  20. NotStarted(Duration),
  21. Running(Waker),
  22. Completed,
  23. }
  24. impl Future for TimerFuture {
  25. type Output = ();
  26. fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
  27. let mut state = self.state.lock().unwrap();
  28. if let State::Completed = *state {
  29. return Poll::Ready(());
  30. }
  31. if let State::NotStarted(duration) = *state {
  32. let thread_state = self.state.clone();
  33. thread::spawn(move || {
  34. thread::sleep(duration);
  35. let mut state = thread_state.lock().unwrap();
  36. let waker = if let State::Running(waker) = &*state {
  37. Some(waker.clone())
  38. } else {
  39. None
  40. };
  41. *state = State::Completed;
  42. if let Some(w) = waker {
  43. w.wake()
  44. }
  45. });
  46. }
  47. *state = State::Running(cx.waker().clone());
  48. Poll::Pending
  49. }
  50. }
  51. pub fn sleep(duration: Duration) -> impl Future<Output = ()> {
  52. TimerFuture {
  53. state: Arc::new(Mutex::new(State::NotStarted(duration))),
  54. }
  55. }
  56. #[cfg(test)]
  57. mod test {
  58. use crate::async_utils::{run, sleep};
  59. use futures::future::FutureExt;
  60. use std::time::{Duration, Instant};
  61. #[test]
  62. fn test_sleep() {
  63. run(async {
  64. let start = Instant::now();
  65. let sleep = sleep(Duration::from_millis(100)).fuse();
  66. let ok = async {}.fuse();
  67. futures::pin_mut!(sleep, ok);
  68. loop {
  69. futures::select! {
  70. _ = sleep => {},
  71. _ = ok => assert!((Instant::now() - start).as_millis() < 100),
  72. complete => break,
  73. }
  74. }
  75. })
  76. }
  77. }