[![Travis (.com)](https://img.shields.io/travis/com/Lucretiel/cooked-waker.svg?logo=travis)](https://travis-ci.com/Lucretiel/cooked-waker/) [![GitHub stars](https://img.shields.io/github/stars/Lucretiel/cooked-waker.svg?label=stars&logo=github&logoColor=white)](https://github.com/Lucretiel/cooked-waker) [![Crates.io](https://img.shields.io/crates/d/cooked-waker.svg?logo=rust&logoColor=white&label=crates.io)](https://crates.io/crates/cooked-waker) [![docs.rs](https://docs.rs/cooked-waker/badge.svg)](https://docs.rs/cooked-waker) [![license](https://img.shields.io/github/license/Lucretiel/cooked-waker.svg)](https://crates.io/crates/cooked-waker/)
# cooked-waker
cooked_waker provides safe traits for working with [`std::task::Waker`][waker] and creating those wakers out of regular, safe Rust structs. It cooks `RawWaker` and `RawWakerVTable`, making them safe for consumption.
It provides the `Wake` and `WakeRef` traits, which correspond to the `wake` and `wake_by_ref` methods on `std::task::Waker`, and it provides implenetations of these types for the common reference & pointer types (`Arc`, `Rc`, `&'static`, etc).
Additionally, it provides `IntoWaker`, which allows converting any `Wake + Clone` type into a `Waker`. This trait is automatically derived for any `Wake + Clone + Send + Sync + 'static` type.
# Basic example
```rust
use cooked_waker::{Wake, WakeRef, IntoWaker};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::task::Waker;
static wake_ref_count: AtomicUsize = AtomicUsize::new(0);
static wake_value_count: AtomicUsize = AtomicUsize::new(0);
static drop_count: AtomicUsize = AtomicUsize::new(0);
// A simple Waker struct that atomically increments the relevant static
// counters.
#[derive(Debug, Clone)]
struct StaticWaker;
impl WakeRef for StaticWaker {
fn wake_by_ref(&self) {
wake_ref_count.fetch_add(1, Ordering::SeqCst);
}
}
impl Wake for StaticWaker {
fn wake(self) {
wake_value_count.fetch_add(1, Ordering::SeqCst);
}
}
impl Drop for StaticWaker {
fn drop(&mut self) {
drop_count.fetch_add(1, Ordering::SeqCst);
}
}
assert_eq!(drop_count.load(Ordering::SeqCst), 0);
let waker = StaticWaker;
{
let waker1: Waker = waker.into_waker();
waker1.wake_by_ref();
assert_eq!(wake_ref_count.load(Ordering::SeqCst), 1);
let waker2: Waker = waker1.clone();
waker2.wake_by_ref();
assert_eq!(wake_ref_count.load(Ordering::SeqCst), 2);
waker1.wake();
assert_eq!(wake_value_count.load(Ordering::SeqCst), 1);
assert_eq!(drop_count.load(Ordering::SeqCst), 1);
}
assert_eq!(drop_count.load(Ordering::SeqCst), 2);
```
# Arc example
```rust
use cooked_waker::{Wake, WakeRef, IntoWaker};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::task::Waker;
// A simple struct that counts the number of times it is awoken. Can't
// be awoken by value (because that would discard the counter), so we
// must instead wrap it in an Arc.
#[derive(Debug, Default)]
struct Counter {
// We use atomic usize because we need Send + Sync and also interior
// mutability
count: AtomicUsize,
}
impl Counter {
fn get(&self) -> usize {
self.count.load(Ordering::SeqCst)
}
}
impl WakeRef for Counter {
fn wake_by_ref(&self) {
let _prev = self.count.fetch_add(1, Ordering::SeqCst);
}
}
let counter_handle = Arc::new(Counter::default());
// Create an std::task::Waker
let waker: Waker = counter_handle.clone().into_waker();
waker.wake_by_ref();
waker.wake_by_ref();
let waker2 = waker.clone();
waker2.wake_by_ref();
// Because IntoWaker wrap the pointer directly, without additional
// boxing, we can use will_wake
assert!(waker.will_wake(&waker2));
// This calls Counter::wake_by_ref because the Arc doesn't have exclusive
// ownership of the underlying Counter
waker2.wake();
assert_eq!(counter_handle.get(), 4);
```