1use collections::BTreeMap;
2use gpui_util::post_inc;
3use std::{
4 cell::{Cell, RefCell},
5 fmt::Debug,
6 rc::Rc,
7};
8
9pub(crate) struct SubscriberSet<EmitterKey, Callback>(
10 Rc<RefCell<SubscriberSetState<EmitterKey, Callback>>>,
11);
12
13impl<EmitterKey, Callback> Clone for SubscriberSet<EmitterKey, Callback> {
14 fn clone(&self) -> Self {
15 SubscriberSet(self.0.clone())
16 }
17}
18
19struct SubscriberSetState<EmitterKey, Callback> {
20 subscribers: BTreeMap<EmitterKey, Option<BTreeMap<usize, Subscriber<Callback>>>>,
21 next_subscriber_id: usize,
22}
23
24struct Subscriber<Callback> {
25 active: Rc<Cell<bool>>,
26 dropped: Rc<Cell<bool>>,
27 callback: Callback,
28}
29
30impl<EmitterKey, Callback> SubscriberSet<EmitterKey, Callback>
31where
32 EmitterKey: 'static + Ord + Clone + Debug,
33 Callback: 'static,
34{
35 pub fn new() -> Self {
36 Self(Rc::new(RefCell::new(SubscriberSetState {
37 subscribers: Default::default(),
38 next_subscriber_id: 0,
39 })))
40 }
41
42 pub fn insert(
47 &self,
48 emitter_key: EmitterKey,
49 callback: Callback,
50 ) -> (Subscription, impl FnOnce() + use<EmitterKey, Callback>) {
51 let active = Rc::new(Cell::new(false));
52 let dropped = Rc::new(Cell::new(false));
53 let mut lock = self.0.borrow_mut();
54 let subscriber_id = post_inc(&mut lock.next_subscriber_id);
55 lock.subscribers
56 .entry(emitter_key.clone())
57 .or_default()
58 .get_or_insert_with(Default::default)
59 .insert(
60 subscriber_id,
61 Subscriber {
62 active: active.clone(),
63 dropped: dropped.clone(),
64 callback,
65 },
66 );
67 let this = self.0.clone();
68
69 let subscription = Subscription {
70 unsubscribe: Some(Box::new(move || {
71 dropped.set(true);
72
73 let mut lock = this.borrow_mut();
74 let Some(subscribers) = lock.subscribers.get_mut(&emitter_key) else {
75 return;
76 };
77
78 if let Some(subscribers) = subscribers {
79 subscribers.remove(&subscriber_id);
80 if subscribers.is_empty() {
81 lock.subscribers.remove(&emitter_key);
82 }
83 }
84 })),
85 };
86 (subscription, move || active.set(true))
87 }
88
89 pub fn remove(
90 &self,
91 emitter: &EmitterKey,
92 ) -> impl IntoIterator<Item = Callback> + use<EmitterKey, Callback> {
93 let subscribers = self.0.borrow_mut().subscribers.remove(emitter);
94 subscribers
95 .unwrap_or_default()
96 .map(|s| s.into_values())
97 .into_iter()
98 .flatten()
99 .filter_map(|subscriber| {
100 if subscriber.active.get() {
101 Some(subscriber.callback)
102 } else {
103 None
104 }
105 })
106 }
107
108 pub fn retain<F>(&self, emitter: &EmitterKey, mut f: F)
111 where
112 F: FnMut(&mut Callback) -> bool,
113 {
114 let Some(mut subscribers) = self
115 .0
116 .borrow_mut()
117 .subscribers
118 .get_mut(emitter)
119 .and_then(|s| s.take())
120 else {
121 return;
122 };
123
124 subscribers.retain(|_, subscriber| {
125 if !subscriber.active.get() {
126 return true;
127 }
128 if subscriber.dropped.get() {
129 return false;
130 }
131 let keep = f(&mut subscriber.callback);
132 keep && !subscriber.dropped.get()
133 });
134 let mut lock = self.0.borrow_mut();
135
136 if let Some(Some(new_subscribers)) = lock.subscribers.remove(emitter) {
138 subscribers.extend(new_subscribers);
139 }
140
141 if !subscribers.is_empty() {
142 lock.subscribers.insert(emitter.clone(), Some(subscribers));
143 }
144 }
145}
146
147#[must_use]
156pub struct Subscription {
157 unsubscribe: Option<Box<dyn FnOnce() + 'static>>,
158}
159
160impl Subscription {
161 pub fn new(unsubscribe: impl 'static + FnOnce()) -> Self {
183 Self {
184 unsubscribe: Some(Box::new(unsubscribe)),
185 }
186 }
187
188 pub fn detach(mut self) {
193 self.unsubscribe.take();
194 }
195
196 pub fn join(mut subscription_a: Self, mut subscription_b: Self) -> Self {
201 let a_unsubscribe = subscription_a.unsubscribe.take();
202 let b_unsubscribe = subscription_b.unsubscribe.take();
203 Self {
204 unsubscribe: Some(Box::new(move || {
205 if let Some(self_unsubscribe) = a_unsubscribe {
206 self_unsubscribe();
207 }
208 if let Some(other_unsubscribe) = b_unsubscribe {
209 other_unsubscribe();
210 }
211 })),
212 }
213 }
214}
215
216impl Drop for Subscription {
217 fn drop(&mut self) {
218 if let Some(unsubscribe) = self.unsubscribe.take() {
219 unsubscribe();
220 }
221 }
222}
223
224impl std::fmt::Debug for Subscription {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 f.debug_struct("Subscription").finish()
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233 use crate::{Global, TestApp};
234
235 #[test]
236 fn test_unsubscribe_during_callback_with_insert() {
237 struct TestGlobal;
238 impl Global for TestGlobal {}
239
240 let mut app = TestApp::new();
241 app.set_global(TestGlobal);
242
243 let observer_a_count = Rc::new(Cell::new(0usize));
244 let observer_b_count = Rc::new(Cell::new(0usize));
245
246 let sub_a: Rc<RefCell<Option<Subscription>>> = Default::default();
247 let sub_b: Rc<RefCell<Option<Subscription>>> = Default::default();
248
249 *sub_a.borrow_mut() = Some(app.update({
252 let count = observer_a_count.clone();
253 let sub_a = sub_a.clone();
254 move |cx| {
255 cx.observe_global::<TestGlobal>(move |cx| {
256 count.set(count.get() + 1);
257 sub_a.borrow_mut().take();
258 cx.observe_global::<TestGlobal>(|_| {}).detach();
259 })
260 }
261 }));
262
263 *sub_b.borrow_mut() = Some(app.update({
265 let count = observer_b_count.clone();
266 let sub_b = sub_b.clone();
267 move |cx| {
268 cx.observe_global::<TestGlobal>(move |_cx| {
269 count.set(count.get() + 1);
270 sub_b.borrow_mut().take();
271 })
272 }
273 }));
274
275 app.update(|cx| cx.set_global(TestGlobal));
277 assert_eq!(observer_a_count.get(), 1);
278 assert_eq!(observer_b_count.get(), 1);
279
280 app.update(|cx| cx.set_global(TestGlobal));
282 assert_eq!(observer_a_count.get(), 1);
283 assert_eq!(observer_b_count.get(), 1, "orphaned subscriber fired again");
284 }
285
286 #[test]
287 fn test_callback_dropped_by_earlier_callback_does_not_fire() {
288 struct TestGlobal;
289 impl Global for TestGlobal {}
290
291 let mut app = TestApp::new();
292 app.set_global(TestGlobal);
293
294 let observer_b_count = Rc::new(Cell::new(0usize));
295 let sub_b: Rc<RefCell<Option<Subscription>>> = Default::default();
296
297 app.update({
299 let sub_b = sub_b.clone();
300 move |cx| {
301 cx.observe_global::<TestGlobal>(move |_cx| {
302 sub_b.borrow_mut().take();
303 })
304 .detach();
305 }
306 });
307
308 *sub_b.borrow_mut() = Some(app.update({
310 let count = observer_b_count.clone();
311 move |cx| {
312 cx.observe_global::<TestGlobal>(move |_cx| {
313 count.set(count.get() + 1);
314 })
315 }
316 }));
317
318 app.update(|cx| cx.set_global(TestGlobal));
319 assert_eq!(
320 observer_b_count.get(),
321 0,
322 "B should not fire — A dropped its subscription"
323 );
324 }
325
326 #[test]
327 fn test_self_drop_during_callback() {
328 struct TestGlobal;
329 impl Global for TestGlobal {}
330
331 let mut app = TestApp::new();
332 app.set_global(TestGlobal);
333
334 let count = Rc::new(Cell::new(0usize));
335 let sub: Rc<RefCell<Option<Subscription>>> = Default::default();
336
337 *sub.borrow_mut() = Some(app.update({
338 let count = count.clone();
339 let sub = sub.clone();
340 move |cx| {
341 cx.observe_global::<TestGlobal>(move |_cx| {
342 count.set(count.get() + 1);
343 sub.borrow_mut().take();
344 })
345 }
346 }));
347
348 app.update(|cx| cx.set_global(TestGlobal));
349 assert_eq!(count.get(), 1);
350
351 app.update(|cx| cx.set_global(TestGlobal));
352 assert_eq!(count.get(), 1, "should not fire after self-drop");
353 }
354
355 #[test]
356 fn test_subscription_drop() {
357 struct TestGlobal;
358 impl Global for TestGlobal {}
359
360 let mut app = TestApp::new();
361 app.set_global(TestGlobal);
362
363 let count = Rc::new(Cell::new(0usize));
364
365 let subscription = app.update({
366 let count = count.clone();
367 move |cx| {
368 cx.observe_global::<TestGlobal>(move |_cx| {
369 count.set(count.get() + 1);
370 })
371 }
372 });
373
374 drop(subscription);
375
376 app.update(|cx| cx.set_global(TestGlobal));
377 assert_eq!(count.get(), 0, "should not fire after drop");
378 }
379}