├── .github └── workflows │ └── build.yml ├── .gitignore ├── CODEOWNERS ├── Cargo.toml ├── LICENSE ├── README.md ├── examples └── block_on.rs └── src └── lib.rs /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | test: 6 | strategy: 7 | matrix: 8 | platform: 9 | - ubuntu-latest 10 | - macos-latest 11 | - windows-latest 12 | runs-on: ${{ matrix.platform }} 13 | env: 14 | RUST_BACKTRACE: 1 15 | steps: 16 | - uses: actions/checkout@master 17 | 18 | - uses: actions-rs/toolchain@v1 19 | with: 20 | profile: minimal 21 | toolchain: "1.39.0" 22 | default: true 23 | 24 | - run: cargo test 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * me@stephencoakley.com 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wakeful" 3 | version = "0.1.1" 4 | description = "Utilities to aid implementing wakers and working with tasks." 5 | authors = ["Stephen M. Coakley "] 6 | license = "MIT" 7 | keywords = ["async", "waker"] 8 | categories = ["asynchronous"] 9 | repository = "https://github.com/sagebind/wakeful" 10 | documentation = "https://docs.rs/wakeful/" 11 | readme = "README.md" 12 | edition = "2018" 13 | 14 | [badges.maintenance] 15 | status = "actively-developed" 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Stephen M. Coakley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wakeful 2 | 3 | Utilities to aid implementing [`Waker`][Waker]s and working with tasks. 4 | 5 | **Currently unsound. I might come back to this later and fix it if I can come up with a solution.** 6 | 7 | [![Crates.io](https://img.shields.io/crates/v/wakeful.svg)](https://crates.io/crates/wakeful) 8 | [![Documentation](https://docs.rs/wakeful/badge.svg)][documentation] 9 | ![License](https://img.shields.io/github/license/sagebind/wakeful) 10 | ![Build](https://github.com/sagebind/wakeful/workflows/build/badge.svg) 11 | 12 | ## [Documentation] 13 | 14 | Please check out the [documentation] for details on what Wakeful can do and how to use it. 15 | 16 | ## License 17 | 18 | This library is licensed under the MIT license. See the [LICENSE](LICENSE) file for details. 19 | 20 | 21 | [documentation]: https://docs.rs/wakeful 22 | [Waker]: https://doc.rust-lang.org/stable/std/task/struct.Waker.html 23 | -------------------------------------------------------------------------------- /examples/block_on.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | pin::Pin, 4 | task::{Context, Poll}, 5 | thread, 6 | }; 7 | use wakeful::Wake; 8 | 9 | /// Block the current thread until this future is ready. 10 | pub fn block_on(mut future: F) -> F::Output { 11 | /// Note that this crate already implements `Wake` for `Thread`, this just 12 | /// demonstrates how simple the implementation is. 13 | #[derive(Clone)] 14 | struct ThreadWaker(thread::Thread); 15 | 16 | impl Wake for ThreadWaker { 17 | fn wake_by_ref(&self) { 18 | self.0.unpark(); 19 | } 20 | } 21 | 22 | // Now that we can easily create a waker that does what we want (unpark this 23 | // thread), it is now easy to create a context and begin polling the given 24 | // future efficiently. 25 | let waker = ThreadWaker(thread::current()).into_waker(); 26 | let mut context = Context::from_waker(&waker); 27 | let mut future = unsafe { Pin::new_unchecked(&mut future) }; 28 | 29 | loop { 30 | match future.as_mut().poll(&mut context) { 31 | Poll::Ready(output) => return output, 32 | Poll::Pending => thread::park(), 33 | } 34 | } 35 | } 36 | 37 | async fn number_async() -> usize { 38 | 42 39 | } 40 | 41 | fn main() { 42 | block_on(async { 43 | assert_eq!(number_async().await, 42); 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Utilities to aid implementing [`Waker`s](std::task::Waker) and working with 2 | //! tasks. 3 | //! 4 | //! The highlight of this crate is [`Wake`], which allows you to construct 5 | //! wakers from your own types by implementing this trait. 6 | //! 7 | //! # Examples 8 | //! 9 | //! Implementing your own `block_on` function using this crate: 10 | //! 11 | //! ``` 12 | //! use std::{ 13 | //! future::Future, 14 | //! pin::Pin, 15 | //! task::{Context, Poll}, 16 | //! thread, 17 | //! }; 18 | //! use wakeful::Wake; 19 | //! 20 | //! fn block_on(mut future: F) -> F::Output { 21 | //! let waker = thread::current().into_waker(); 22 | //! let mut context = Context::from_waker(&waker); 23 | //! let mut future = unsafe { Pin::new_unchecked(&mut future) }; 24 | //! 25 | //! loop { 26 | //! match future.as_mut().poll(&mut context) { 27 | //! Poll::Ready(output) => return output, 28 | //! Poll::Pending => thread::park(), 29 | //! } 30 | //! } 31 | //! } 32 | //! ``` 33 | 34 | #![warn( 35 | future_incompatible, 36 | missing_debug_implementations, 37 | missing_docs, 38 | rust_2018_idioms, 39 | unreachable_pub, 40 | unused, 41 | clippy::all 42 | )] 43 | 44 | use std::{ 45 | mem, ptr, 46 | sync::Arc, 47 | task::{RawWaker, RawWakerVTable, Waker}, 48 | }; 49 | 50 | /// Zero-cost helper trait that makes it easier to implement wakers. 51 | /// 52 | /// Implementing this trait provides you with [`Wake::into_waker`], which allows 53 | /// you to construct a [`Waker`] from any type implementing [`Wake`]. The only 54 | /// method you must implement is [`Wake::wake_by_ref`] which can encapsulate all 55 | /// your custom wake-up behavior. 56 | /// 57 | /// Your custom wakers must also implement [`Clone`], [`Send`], and [`Sync`] to 58 | /// comply with the contract of [`Waker`]. You are free to choose any strategy 59 | /// you like to handle cloning; bundling your state in an inner [`Arc`] is 60 | /// common and plays nicely with this trait. 61 | /// 62 | /// # Provided implementations 63 | /// 64 | /// A simple waker implementation is provided for [`std::thread::Thread`], which 65 | /// merely calls `unpark()`. This almost trivializes implementing your own 66 | /// single-threaded `block_on` executor. An example of this is provided in the 67 | /// `examples/` directory. 68 | /// 69 | /// # Optimizations 70 | /// 71 | /// If the size of `Self` is less than or equal to pointer size, as an 72 | /// optimization the underlying implementation will pass `self` in directly to 73 | /// [`RawWakerVTable`] functions. For types larger than a pointer, an allocation 74 | /// will be made on creation and when cloning. 75 | /// 76 | /// # Examples 77 | /// 78 | /// ``` 79 | /// use wakeful::Wake; 80 | /// 81 | /// /// Doesn't actually do anything except print a message when wake is called. 82 | /// #[derive(Clone)] 83 | /// struct PrintWaker; 84 | /// 85 | /// impl Wake for PrintWaker { 86 | /// fn wake_by_ref(&self) { 87 | /// println!("wake called!"); 88 | /// } 89 | /// } 90 | /// 91 | /// let waker = PrintWaker.into_waker(); 92 | /// waker.wake(); // prints "wake called!" 93 | /// ``` 94 | /// 95 | /// ``` 96 | /// use std::task::Waker; 97 | /// use wakeful::Wake; 98 | /// 99 | /// /// Delegates wake calls to multiple wakers. 100 | /// #[derive(Clone)] 101 | /// struct MultiWaker(Vec); 102 | /// 103 | /// impl Wake for MultiWaker { 104 | /// fn wake(self) { 105 | /// for waker in self.0 { 106 | /// waker.wake(); 107 | /// } 108 | /// } 109 | /// 110 | /// fn wake_by_ref(&self) { 111 | /// for waker in &self.0 { 112 | /// waker.wake_by_ref(); 113 | /// } 114 | /// } 115 | /// } 116 | /// ``` 117 | pub trait Wake: Send + Sync + Clone { 118 | /// Wake up the task associated with this waker, consuming the waker. When 119 | /// converted into a waker handle, this method is invoked whenever 120 | /// [`Waker::wake`] is called. 121 | /// 122 | /// By default, this delegates to [`Wake::wake_by_ref`], but can be 123 | /// overridden if a more efficient owned implementation is possible. 124 | fn wake(self) { 125 | self.wake_by_ref(); 126 | } 127 | 128 | /// Wake up the task associated with this waker, consuming the waker. When 129 | /// converted into a waker handle, this method is invoked whenever 130 | /// [`Waker::wake_by_ref`] is called. 131 | fn wake_by_ref(&self); 132 | 133 | /// Convert this into a [`Waker`] handle. 134 | fn into_waker(self) -> Waker { 135 | // There's a fair bit of magic going on here, so watch out. There are 136 | // two possible implementations for this function, and which one we 137 | // invoke is decided at compile time based on the memory size of `Self`. 138 | // 139 | // When the size of `Self` is less than or equal to pointer size, we can 140 | // avoid allocations altogether by treating the data pointer used in the 141 | // waker vtable as the waker itself. 142 | // 143 | // If `Self` is larger than a pointer, then we take the more obvious 144 | // approach of putting the waker on the heap and passing around a 145 | // pointer to it. 146 | // 147 | // The pointer-size optimization is extremely useful when you want to 148 | // combine your waker implementation with things like `Arc`, which is 149 | // already pointer sized. With this approach, such wakers automatically 150 | // use the best possible implementation as the arc pointer is 151 | // essentially being passed around directly with no indirection without 152 | // any extra effort from the implementer. 153 | 154 | /// Convert a wake into a [`RawWaker`] handle by allocating a box. 155 | /// 156 | /// This is the easier implementation to understand. We create a data 157 | /// pointer by moving self into a box and then getting its raw pointer. 158 | fn create_boxed(wake: W) -> RawWaker { 159 | RawWaker::new( 160 | Box::into_raw(Box::new(wake)) as *const (), 161 | &RawWakerVTable::new( 162 | |data| unsafe { 163 | create_boxed((&*(data as *const W)).clone()) 164 | }, 165 | |data| unsafe { 166 | Box::from_raw(data as *mut W).wake(); 167 | }, 168 | |data| unsafe { 169 | (&*(data as *const W)).wake_by_ref(); 170 | }, 171 | |data| unsafe { 172 | Box::from_raw(data as *mut W); 173 | }, 174 | ), 175 | ) 176 | } 177 | 178 | /// Convert a wake into a [`RawWaker`] handle by transmuting into a data 179 | /// pointer. 180 | /// 181 | /// This is the trickier implementation, where we treat the data pointer 182 | /// as a plain `usize` and store the bits of self in it. 183 | /// 184 | /// Unsafe because we cannot statically guarantee that the size of `W` 185 | /// does not exceed pointer size. 186 | unsafe fn create_thin(wake: W) -> RawWaker { 187 | let mut data = ptr::null(); 188 | 189 | // The following code will unleash the kraken if this invariant 190 | // isn't upheld. 191 | debug_assert!(mem::size_of::() <= mem::size_of_val(&data)); 192 | 193 | // The size of `W` might be _smaller_ than a pointer, so we can't 194 | // simply transmute here as that would potentially read off the end 195 | // of `wake`. Instead, we copy from `wake` to `data` (not the 196 | // _target_ of `data`, which has no meaning to us). 197 | ptr::copy_nonoverlapping( 198 | &wake as *const W, 199 | &mut data as *mut *const () as *mut W, 200 | 1, 201 | ); 202 | 203 | // We moved `wake` into `data`, so make sure we don't keep the old 204 | // copy around (there can be only one!). 205 | mem::forget(wake); 206 | 207 | RawWaker::new( 208 | data, 209 | &RawWakerVTable::new( 210 | |data| { 211 | create_thin((&*(&data as *const *const () as *const W)).clone()) 212 | }, 213 | |data| { 214 | mem::transmute_copy::<_, W>(&data).wake(); 215 | }, 216 | |data| { 217 | (&*(&data as *const *const () as *const W)).wake_by_ref(); 218 | }, 219 | |data| { 220 | mem::transmute_copy::<_, W>(&data); 221 | }, 222 | ), 223 | ) 224 | } 225 | 226 | unsafe { 227 | if mem::size_of::() <= mem::size_of::<*const ()>() { 228 | Waker::from_raw(create_thin(self)) 229 | } else { 230 | Waker::from_raw(create_boxed(self)) 231 | } 232 | } 233 | } 234 | } 235 | 236 | impl Wake for std::thread::Thread { 237 | fn wake_by_ref(&self) { 238 | self.unpark(); 239 | } 240 | } 241 | 242 | /// Create a waker from a closure. 243 | /// 244 | /// # Examples 245 | /// 246 | /// ``` 247 | /// let waker = wakeful::waker_fn(move || { 248 | /// println!("time for work!"); 249 | /// }); 250 | /// 251 | /// waker.wake(); 252 | /// ``` 253 | pub fn waker_fn(f: impl Fn() + Send + Sync + 'static) -> Waker { 254 | struct Impl(Arc); 255 | 256 | impl Clone for Impl { 257 | fn clone(&self) -> Self { 258 | Impl(self.0.clone()) 259 | } 260 | } 261 | 262 | impl Wake for Impl { 263 | fn wake_by_ref(&self) { 264 | (self.0)() 265 | } 266 | } 267 | 268 | Impl(Arc::new(f)).into_waker() 269 | } 270 | 271 | #[cfg(test)] 272 | mod tests { 273 | use super::*; 274 | use std::sync::{ 275 | atomic::{AtomicUsize, Ordering}, 276 | Arc, 277 | }; 278 | 279 | #[test] 280 | fn zero_sized_impl() { 281 | static WOKE: AtomicUsize = AtomicUsize::new(0); 282 | 283 | #[derive(Clone)] 284 | struct Impl; 285 | 286 | impl Wake for Impl { 287 | fn wake_by_ref(&self) { 288 | WOKE.fetch_add(1, Ordering::SeqCst); 289 | } 290 | } 291 | 292 | let waker = Impl.into_waker(); 293 | waker.wake_by_ref(); 294 | assert_eq!(WOKE.load(Ordering::SeqCst), 1); 295 | 296 | waker.clone().wake(); 297 | assert_eq!(WOKE.load(Ordering::SeqCst), 2); 298 | } 299 | 300 | #[test] 301 | fn ptr_sized_impl() { 302 | #[derive(Clone, Default)] 303 | struct Impl(Arc); 304 | 305 | impl Wake for Impl { 306 | fn wake_by_ref(&self) { 307 | self.0.fetch_add(1, Ordering::SeqCst); 308 | } 309 | } 310 | 311 | let woke = Arc::new(AtomicUsize::new(0)); 312 | 313 | let waker = Impl(woke.clone()).into_waker(); 314 | waker.wake_by_ref(); 315 | assert_eq!(woke.load(Ordering::SeqCst), 1); 316 | 317 | waker.clone().wake(); 318 | assert_eq!(woke.load(Ordering::SeqCst), 2); 319 | } 320 | 321 | #[test] 322 | fn bigger_than_ptr_sized_impl() { 323 | #[derive(Clone)] 324 | struct Impl(Arc, usize); 325 | 326 | impl Wake for Impl { 327 | fn wake_by_ref(&self) { 328 | self.0.fetch_add(1, Ordering::SeqCst); 329 | } 330 | } 331 | 332 | let woke = Arc::new(AtomicUsize::new(0)); 333 | 334 | let waker = Impl(woke.clone(), 0).into_waker(); 335 | waker.wake_by_ref(); 336 | assert_eq!(woke.load(Ordering::SeqCst), 1); 337 | 338 | waker.clone().wake(); 339 | assert_eq!(woke.load(Ordering::SeqCst), 2); 340 | } 341 | } 342 | --------------------------------------------------------------------------------