Expand description
cooked_waker provides safe traits for working with
std::task::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
use cooked_waker::{Wake, WakeRef, IntoWaker, ViaRawPointer};
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);
}
}
// Usually in practice you'll be using an Arc or Box, which already
// implement this, so there will be no need to implement it yourself.
unsafe impl ViaRawPointer for StaticWaker {
type Target = ();
fn into_raw(self) -> *mut () {
// Need to forget self because we're being converted into a pointer,
// so destructors should not run.
std::mem::forget(self);
std::ptr::null_mut()
}
unsafe fn from_raw(ptr: *mut ()) -> Self {
StaticWaker
}
}
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
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);
Traits§
- Objects that can be converted into an
Waker
. This trait is automatically implemented for types that fulfill the waker interface. Such types must be: - Trait for types that can be converted into raw pointers and back again.
- Wakers that can wake by value. This is the primary means of waking a task.
- Wakers that can wake by reference. This trait is used to enable a
Wake
implementation for types that don’t own an underlying handle, likeArc<T>
and&T
.