1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
// SPDX-FileCopyrightText: 2022-2023 Katharina Fey <kookie@spacekookie.de>
//
// SPDX-License-Identifier: GPL-3.0-or-later WITH LicenseRef-AppStore
//! A utility wrapper type for notifying async tasks about mutation of
//! data they might be interested in.
use std::ops::{Deref, DerefMut};
use std::task::Waker;
/// A wrapper which wakes tasks on mutable accesses to the wrapped
/// value.
///
/// This can be used to transparently notify an asyncronous task that
/// it should, for example, check for more work in a queue or try
/// again to acquire a lock.
///
/// Importantly: only DerefMut is used to wake the target task.
/// Read-only access is possible without causing wake events. If no
/// Waker has been registered yet, no task will be woken.
#[derive(Default, Debug, Clone)]
pub struct Notify<T> {
inner: T,
waker: Option<Waker>,
}
impl<T> Deref for Notify<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for Notify<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.waker.as_ref().map(|w| w.wake_by_ref());
&mut self.inner
}
}
impl<T> Notify<T> {
pub fn new(inner: T) -> Self {
Self { inner, waker: None }
}
/// Check whether or not this `Notify` has a registered `Waker`.
///
/// This function is implemented as an associated function rather than a
/// method to avoid conflicts with methods on the wrapped type.
/// Call it as `Notify::has_waker()`.
pub fn has_waker(ptr: &Notify<T>) -> bool {
ptr.waker.is_some()
}
/// Get a copy of the registered `Waker` for this `Notify`.
///
/// This function is implemented as an associated function rather than a
/// method to avoid conflicts with methods on the wrapped type.
/// Call it as `Notify::waker()`.
pub fn waker(ptr: &mut Notify<T>) -> Option<Waker> {
ptr.waker.as_ref().map(|w| w.clone())
}
/// Call wake on the waker, if it's a waker, yehaa!
#[inline]
pub fn wake(ptr: &mut Notify<T>) {
if let Some(ref w) = ptr.waker {
w.clone().wake();
}
}
/// Register a `Waker` to be woken upon mutable accesses to the wrapped value.
///
/// This function is implemented as an associated function rather than a
/// method to avoid conflicts with methods on the wrapped type.
/// Call it as `Notify::register_waker()`.
///
/// # Panics
/// Panics if there is an already registered `Waker`.
/// Use `Notify::has_waker` to check the state before using this.
#[inline]
pub fn register_waker(ptr: &mut Notify<T>, waker: &Waker) {
if !Notify::has_waker(ptr) {
ptr.waker = Some(waker.clone())
}
}
/// Removes and returns the `Waker` registered to this `Notify`.
///
/// This function is implemented as an associated function rather than a
/// method to avoid conflicts with methods on the wrapped type.
/// Call it as `Notify::clear_waker()`.
pub fn clear_waker(ptr: &mut Notify<T>) -> Option<Waker> {
ptr.waker.take()
}
/// Consumes the `Notify`, dropping any associated `Waker` and
/// returning the inner value without notifying the `Waker`.
///
/// This function is implemented as an associated function rather than a
/// method to avoid conflicts with methods on the wrapped type.
/// Call it as `Notify::into_inner()`.
pub fn into_inner(ptr: Notify<T>) -> T {
ptr.inner
}
}