tuwunel_database/map/
watch.rs1use std::{
2 collections::{BTreeMap, btree_map::Entry},
3 ops::RangeToInclusive,
4 sync::Mutex,
5};
6
7use futures::pin_mut;
8use serde::Serialize;
9use tokio::sync::watch::{Receiver, Sender, channel};
10use tuwunel_core::{debug, defer, implement, smallvec::SmallVec};
11
12use crate::keyval::{KeyBuf, serialize_key};
13
14type Watchers = Mutex<BTreeMap<KeyBuf, Sender<()>>>;
19type KeyVec = SmallVec<[KeyBuf; 1]>;
24
25#[derive(Default)]
30pub(super) struct Watch {
31 watchers: Watchers,
32}
33
34#[implement(super::Map)]
45pub fn watch_prefix<K>(&self, prefix: K) -> impl Future<Output = ()> + Send + '_
46where
47 K: Serialize,
48{
49 let prefix = serialize_key(prefix).expect("failed to serialize watch prefix key");
50 self.watch_raw_prefix(&prefix)
51}
52
53#[implement(super::Map)]
63pub fn watch_raw_prefix_once<K>(&self, prefix: K) -> impl Future<Output = ()> + Send + '_
64where
65 K: AsRef<[u8]>,
66{
67 let key: KeyBuf = prefix.as_ref().into();
68 let rx = self.subscribe(key.clone());
69
70 async move {
71 pin_mut!(rx);
72
73 defer! {{
75 let mut watchers = self.watch.watchers.lock().expect("locked");
76 if watchers.get(&key).is_some_and(|tx| tx.receiver_count() == 1) {
77 watchers.remove(&key);
78 }
79 }}
80
81 rx.changed()
82 .await
83 .expect("watcher sender dropped");
84 }
85}
86
87#[implement(super::Map)]
98pub fn watch_raw_prefix<'a, K>(&self, prefix: &'a K) -> impl Future<Output = ()> + Send + use<K>
99where
100 K: AsRef<[u8]> + ?Sized + 'a,
101{
102 let rx = self.subscribe(prefix.as_ref().into());
103
104 async move {
105 pin_mut!(rx);
106 rx.changed()
107 .await
108 .expect("watcher sender dropped");
109 }
110}
111
112#[implement(super::Map)]
121fn subscribe(&self, key: KeyBuf) -> Receiver<()> {
122 match self
123 .watch
124 .watchers
125 .lock()
126 .expect("locked")
127 .entry(key)
128 {
129 | Entry::Occupied(node) => node.get().subscribe(),
130 | Entry::Vacant(node) => {
131 let (tx, rx) = channel(());
132 node.insert(tx);
133 rx
134 },
135 }
136}
137
138#[implement(super::Map)]
148#[tracing::instrument(
149 level = "trace",
150 skip_all,
151 fields(
152 map = self.name(),
153 key = str::from_utf8(key.as_ref()).unwrap_or("<binary>"),
154 )
155)]
156pub(crate) fn notify<K>(&self, key: &K)
157where
158 K: AsRef<[u8]> + Ord + ?Sized,
159{
160 let range = RangeToInclusive::<KeyBuf> { end: key.as_ref().into() };
161
162 let mut watchers = self.watch.watchers.lock().expect("locked");
163
164 let num_notified = watchers
165 .range(range)
166 .rev()
167 .take_while(|(k, _)| key.as_ref().starts_with(k))
168 .filter_map(|(k, tx)| tx.send(()).is_err().then_some(k))
169 .cloned()
170 .collect::<KeyVec>()
171 .into_iter()
172 .fold(0_usize, |num_notified, key| {
173 watchers.remove(&key);
174 num_notified.saturating_add(1)
175 });
176
177 if num_notified > 0 {
178 debug!(watchers = watchers.len(), num_notified, "notified");
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use tokio::sync::watch::channel;
185
186 #[test]
189 fn receiver_count_reaps_at_last_drop() {
190 let (tx, rx) = channel(());
191 assert_eq!(tx.receiver_count(), 1, "fresh channel has one receiver");
192
193 let rx2 = tx.subscribe();
194 assert_eq!(tx.receiver_count(), 2, "subscribe adds a receiver");
195
196 drop(rx2);
197 assert_eq!(tx.receiver_count(), 1, "drop is reflected synchronously");
198
199 drop(rx);
200 assert_eq!(tx.receiver_count(), 0, "last drop leaves no receiver");
201 assert!(tx.send(()).is_err(), "send fails with zero receivers");
202 }
203}