rust / expert
Snippet
Implementing a Thread-Safe Asynchronous Timer Future from Scratch
Writing custom futures requires implementing the Future trait and handling task wakers manually. This asynchronous timer spawns a worker thread and updates shared state, waking the async runtime task once the duration expires.
snippet.rs
rust
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
use std::future::Future;use std::pin::Pin;use std::sync::{Arc, Mutex};use std::task::{Context, Poll, Waker};use std::thread;use std::time::Duration;struct AsyncTimer {state: Arc<Mutex<TimerState>>,}struct TimerState {completed: bool,waker: Option<Waker>,}impl Future for AsyncTimer {type Output = ();fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {let mut state = self.state.lock().unwrap();if state.completed {Poll::Ready(())} else {state.waker = Some(cx.waker().clone());Poll::Pending}}}impl AsyncTimer {fn new(duration: Duration) -> Self {let state = Arc::new(Mutex::new(TimerState { completed: false, waker: None }));let thread_state = state.clone();thread::spawn(move || {thread::sleep(duration);let mut state = thread_state.lock().unwrap();state.completed = true;if let Some(waker) = state.waker.take() {waker.wake();}});AsyncTimer { state }}}
Breakdown
1
state.waker = Some(cx.waker().clone());
Saves the current task waker context so the background thread can notify the executor when ready.
2
Poll::Pending
Returns Pending to inform the runtime that the operation is not yet complete and it should yield control.
3
waker.wake();
Triggers the waker, scheduling the task associated with this future for another poll invocation.