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}