thread_waker/
lib.rs

1//!Waker implementation using current thread token.
2//!
3//!This is useful to work with futures without actually employing runtime
4//!
5//!## Usage
6//!
7//!```rust
8//!use core::{time, task};
9//!use std::thread;
10//!
11//!use thread_waker::waker;
12//!
13//!fn my_future(waker: task::Waker) {
14//!    thread::sleep(time::Duration::from_millis(250));
15//!    waker.wake();
16//!}
17//!
18//!let waker = waker(thread::current());
19//!
20//!for _ in 0..4 {
21//!    let waker = waker.clone();
22//!    thread::spawn(move || my_future(waker));
23//!    thread::park();
24//!}
25//!
26//!println!("I'm done!");
27//!```
28
29#![warn(missing_docs)]
30#![allow(clippy::style)]
31// Dudue, do you think I'm retard not to know what I transmute?
32#![allow(clippy::missing_transmute_annotations)]
33
34use core::{task, mem};
35use core::future::Future;
36use std::thread::{self, Thread};
37
38const VTABLE: task::RawWakerVTable = task::RawWakerVTable::new(clone, wake, wake_by_ref, on_drop);
39
40unsafe fn on_drop(thread: *const ()) {
41     let thread = Box::from_raw(thread as *mut Thread);
42    drop(thread);
43}
44
45unsafe fn clone(thread: *const()) -> task::RawWaker {
46    //Thread handle is just simple Arc pointer to cloning it is cheap and efficient
47    //but we need to make sure to forget current thread, otherwise it is no clone
48    //as Clone callback is done via reference
49    let thread = Box::from_raw(thread as *mut Thread);
50    let new_ptr = thread.clone();
51    mem::forget(thread);
52    task::RawWaker::new(Box::into_raw(new_ptr) as _, &VTABLE)
53}
54
55unsafe fn wake(thread: *const ()) {
56    let thread = Box::from_raw(thread as *mut () as *mut Thread);
57    thread.unpark();
58}
59
60unsafe fn wake_by_ref(thread: *const ()) {
61    //wake_by_ref should not consume self
62    let thread = &*(thread as *const Thread);
63    thread.unpark();
64}
65
66#[inline(always)]
67///Creates waker from thread handle
68pub fn waker(thread: Thread) -> task::Waker {
69    let thread = Box::new(thread);
70    unsafe {
71        task::Waker::from_raw(task::RawWaker::new(Box::into_raw(thread) as _, &VTABLE))
72    }
73}
74
75
76///Await for future forever via thread token
77///
78///Note that this is only viable for futures that doesn't require IO runtime
79pub fn block_on<F: Future>(fut: F) -> F::Output {
80    let waker = waker(thread::current());
81    let mut fut = core::pin::pin!(fut);
82    loop {
83        let mut context = task::Context::from_waker(&waker);
84        match Future::poll(fut.as_mut(), &mut context) {
85            task::Poll::Pending => thread::park(),
86            task::Poll::Ready(result) => break result,
87        }
88    }
89}