use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
use crate::flags;
use crate::flags::Flag;
use crate::ring::RingWaker;
pub(super) fn new(ring_waker: RingWaker) -> Waker {
let inner = WakerInner::new(ring_waker);
Waker(Arc::new(inner))
}
pub(super) struct Waker(Arc<WakerInner>);
impl Waker {
pub(super) fn ask_for_wake(&self) {
self.0.ask_for_wake();
}
pub(super) fn signal_work_added(&self) -> bool {
self.0.increment_work_counter();
self.0.maybe_wake()
}
pub(super) fn current_work_counter(&self) -> u64 {
self.0.current_work_counter()
}
}
impl Clone for Waker {
fn clone(&self) -> Self {
self.0.ref_count.fetch_add(1, Ordering::Release);
Self(self.0.clone())
}
}
impl Drop for Waker {
fn drop(&mut self) {
let ref_count = self.0.ref_count.fetch_sub(1, Ordering::AcqRel);
if ref_count <= 2 {
self.signal_work_added();
}
}
}
struct WakerInner {
ring_waker: RingWaker,
wants_wake: AtomicBool,
work_counter: AtomicU64,
ref_count: AtomicU32,
}
impl WakerInner {
fn new(ring_waker: RingWaker) -> Self {
Self {
ring_waker,
wants_wake: AtomicBool::new(false),
work_counter: AtomicU64::new(0),
ref_count: AtomicU32::new(1),
}
}
fn increment_work_counter(&self) {
self.work_counter.fetch_add(1, Ordering::Release);
}
fn current_work_counter(&self) -> u64 {
self.work_counter.load(Ordering::Acquire)
}
fn ask_for_wake(&self) {
#[cfg(feature = "trace-hotpath")]
tracing::trace!("scheduler: asking for wake");
self.wants_wake.store(true, Ordering::SeqCst);
}
fn maybe_wake(&self) -> bool {
let wants_wake = self.wants_wake.swap(false, Ordering::SeqCst);
#[cfg(feature = "trace-hotpath")]
tracing::trace!(wants_wake = wants_wake, "waking ring if it wants");
if wants_wake {
#[cfg(feature = "trace-hotpath")]
tracing::trace!("scheduler has asked us to wake them up");
let user_data = flags::pack(Flag::Wake, 0, 0);
self.ring_waker.wake(user_data)
} else {
#[cfg(feature = "trace-hotpath")]
tracing::trace!("scheduler has not asked us to wake them");
false
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_wake_behaviour() {
let ring = crate::ring::IoRing::for_test(10).unwrap();
let ring_waker = ring.create_waker();
let waker = super::new(ring_waker);
let did_wake = waker.signal_work_added();
assert!(!did_wake);
waker.ask_for_wake();
let did_wake = waker.signal_work_added();
assert!(did_wake);
let did_wake = waker.signal_work_added();
assert!(!did_wake);
}
}