task_notify/
lib.rs

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