1use std::{
2 cell::Cell,
3 collections::VecDeque,
4 sync::atomic::{AtomicBool, Ordering},
5 sync::{Arc, Weak},
6};
7
8use parking_lot::{Mutex, ReentrantMutex};
9use tokio::sync::Notify;
10use xmtp_common::{MaybeSend, MaybeSync};
11
12use crate::{ClientEvent, EventFilter, EventKind, Lagged};
13
14const UNBOUNDED_QUEUE_WARNING_DEPTH: usize = 10_000;
15const APP_QUEUE_DEPTH: usize = 1024;
16
17thread_local! {
18 static FILTER_DEPTH: Cell<usize> = const { Cell::new(0) };
19}
20
21struct FilterGuard;
22
23impl FilterGuard {
24 fn enter() -> Self {
25 FILTER_DEPTH.with(|depth| depth.set(depth.get() + 1));
26 Self
27 }
28}
29
30impl Drop for FilterGuard {
31 fn drop(&mut self) {
32 FILTER_DEPTH.with(|depth| depth.set(depth.get() - 1));
33 }
34}
35
36#[derive(Clone, Debug, Default, PartialEq, Eq)]
38pub struct EventContext {
39 pub dm_identifier: Option<Vec<u8>>,
40 pub references_own_messages: bool,
42 pub message_expires_at_ns: Option<i64>,
44}
45
46#[derive(Clone, Debug, PartialEq, Eq)]
48pub struct EventEnvelope<I> {
49 pub client: Option<ClientEvent>,
50 pub internal: Option<I>,
51 pub context: EventContext,
52}
53
54impl<I> EventEnvelope<I> {
55 pub fn new(client: Option<ClientEvent>, internal: Option<I>, context: EventContext) -> Self {
56 Self {
57 client,
58 internal,
59 context,
60 }
61 }
62}
63
64pub trait EventWriter<I = ()>: MaybeSend + MaybeSync {
66 fn emit(&self, client: Option<ClientEvent>, internal: Option<I>) {
67 self.emit_with_context(client, internal, EventContext::default());
68 }
69
70 fn emit_with_context(
71 &self,
72 client: Option<ClientEvent>,
73 internal: Option<I>,
74 context: EventContext,
75 );
76}
77
78struct BusInner<I> {
79 subscriptions: Mutex<Vec<Weak<SubscriptionInner<I>>>>,
80 dispatch_lock: ReentrantMutex<()>,
81 buffer_lock: ReentrantMutex<()>,
82 app_closed: AtomicBool,
83 internal_closed: AtomicBool,
84}
85
86pub struct EventBus<I> {
88 inner: Arc<BusInner<I>>,
89}
90
91impl<I> Clone for EventBus<I> {
92 fn clone(&self) -> Self {
93 Self {
94 inner: self.inner.clone(),
95 }
96 }
97}
98
99impl<I> Default for EventBus<I> {
100 fn default() -> Self {
101 Self::new()
102 }
103}
104
105impl<I> EventBus<I> {
106 pub fn new() -> Self {
107 Self {
108 inner: Arc::new(BusInner {
109 subscriptions: Mutex::new(Vec::new()),
110 dispatch_lock: ReentrantMutex::new(()),
111 buffer_lock: ReentrantMutex::new(()),
112 app_closed: AtomicBool::new(false),
113 internal_closed: AtomicBool::new(false),
114 }),
115 }
116 }
117
118 pub fn subscribe(&self, filter: EventFilter<I>, queue_depth: Option<usize>) -> Subscription<I> {
120 self.subscribe_with_role(filter, queue_depth, false)
121 }
122
123 pub fn subscribe_app(&self, filter: EventFilter<I>) -> Option<Subscription<I>> {
125 if self.inner.app_closed.load(Ordering::Acquire) {
126 return None;
127 }
128 let subscription = self.subscribe_with_role(filter, Some(APP_QUEUE_DEPTH), true);
129 (!subscription.is_closed()).then_some(subscription)
130 }
131
132 fn subscribe_with_role(
133 &self,
134 filter: EventFilter<I>,
135 queue_depth: Option<usize>,
136 app: bool,
137 ) -> Subscription<I> {
138 self.assert_not_dispatching();
139 let _dispatch = self.inner.dispatch_lock.lock();
140 let inner = Arc::new(SubscriptionInner {
141 filter,
142 queue_depth,
143 app,
144 state: Mutex::new(QueueState::default()),
145 changed: Notify::new(),
146 });
147 if (app && self.inner.app_closed.load(Ordering::Acquire))
148 || (!app && self.inner.internal_closed.load(Ordering::Acquire))
149 {
150 inner.close();
151 }
152 self.inner.subscriptions.lock().push(Arc::downgrade(&inner));
153 Subscription { inner }
154 }
155
156 pub fn close_app_subscriptions(&self) {
158 self.inner.app_closed.store(true, Ordering::Release);
159 self.close_role(true);
160 }
161
162 pub fn close_internal_subscriptions(&self) {
164 self.inner.internal_closed.store(true, Ordering::Release);
165 self.close_role(false);
166 }
167
168 fn close_role(&self, app: bool) {
169 let _dispatch = self.inner.dispatch_lock.lock();
170 let mut subscriptions = self.inner.subscriptions.lock();
171 subscriptions.retain(|weak| {
172 if let Some(inner) = weak.upgrade() {
173 if inner.app == app {
174 inner.close();
175 }
176 true
177 } else {
178 false
179 }
180 });
181 }
182
183 pub fn with_buffer<R, E>(
185 &self,
186 f: impl FnOnce(&EventBuffer<'_, I>) -> Result<R, E>,
187 ) -> Result<R, E>
188 where
189 I: Clone,
190 {
191 self.assert_not_dispatching();
192 assert!(
193 !self.inner.buffer_lock.is_owned_by_current_thread(),
194 "nested event buffer on the same bus"
195 );
196 let _guard = self.inner.buffer_lock.lock();
197 let buffer = EventBuffer {
198 bus: self,
199 pending: Mutex::new(Vec::new()),
200 };
201 let result = f(&buffer);
202 if result.is_ok() {
203 buffer.flush();
204 } else {
205 buffer.clear();
206 }
207 result
208 }
209
210 fn publish(&self, mut event: EventEnvelope<I>)
211 where
212 I: Clone,
213 {
214 if event.client.is_none() && event.internal.is_none() {
215 return;
216 }
217 self.assert_not_dispatching();
218 let _dispatch = self.inner.dispatch_lock.lock();
219 if matches!(event.client.as_ref(), Some(ClientEvent::MessageReceived(_)))
220 && event
221 .context
222 .message_expires_at_ns
223 .is_some_and(|expiry| expiry <= xmtp_common::time::now_ns())
224 {
225 event.client = None;
226 if event.internal.is_none() {
227 return;
228 }
229 }
230 if let Some(client) = &event.client {
231 tracing::debug!(kind = client.kind().name(), "client event emitted");
232 }
233 let mut entries = self.inner.subscriptions.lock();
234 entries.retain(|weak| {
235 if let Some(subscription) = weak.upgrade() {
236 subscription.deliver(&event);
237 true
238 } else {
239 false
240 }
241 });
242 }
243
244 fn assert_not_dispatching(&self) {
245 assert!(
246 FILTER_DEPTH.with(|depth| depth.get() == 0),
247 "an internal event filter must not call an event bus"
248 );
249 assert!(
250 !self.inner.dispatch_lock.is_owned_by_current_thread(),
251 "an internal event filter must not call its event bus"
252 );
253 }
254}
255
256impl<I> EventWriter<I> for EventBus<I>
257where
258 I: Clone + MaybeSend + MaybeSync + 'static,
259{
260 fn emit_with_context(
261 &self,
262 client: Option<ClientEvent>,
263 internal: Option<I>,
264 context: EventContext,
265 ) {
266 self.publish(EventEnvelope::new(client, internal, context));
267 }
268}
269
270pub struct EventBuffer<'a, I> {
272 bus: &'a EventBus<I>,
273 pending: Mutex<Vec<EventEnvelope<I>>>,
274}
275
276impl<I: Clone> EventBuffer<'_, I> {
277 fn flush(&self) {
279 let mut pending = std::mem::take(&mut *self.pending.lock());
280 pending.sort_by_key(|event| event.client.as_ref().map(|client| client.kind()));
281 for event in pending {
282 self.bus.publish(event);
283 }
284 }
285
286 fn clear(&self) {
288 self.pending.lock().clear();
289 }
290
291 pub fn savepoint<R, E>(&self, f: impl FnOnce(&Self) -> Result<R, E>) -> Result<R, E> {
293 let checkpoint = self.pending.lock().len();
294 let result = f(self);
295 if result.is_err() {
296 self.pending.lock().truncate(checkpoint);
297 }
298 result
299 }
300}
301
302impl<I> EventWriter<I> for EventBuffer<'_, I>
303where
304 I: Clone + MaybeSend + MaybeSync + 'static,
305{
306 fn emit_with_context(
307 &self,
308 client: Option<ClientEvent>,
309 internal: Option<I>,
310 context: EventContext,
311 ) {
312 if client.is_some() || internal.is_some() {
313 self.pending
314 .lock()
315 .push(EventEnvelope::new(client, internal, context));
316 }
317 }
318}
319
320pub struct PublicBusWriter<I> {
322 bus: Weak<BusInner<I>>,
323}
324
325impl<I> PublicBusWriter<I> {
326 pub fn new(bus: &EventBus<I>) -> Self {
327 Self {
328 bus: Arc::downgrade(&bus.inner),
329 }
330 }
331}
332
333impl<I> EventWriter<()> for PublicBusWriter<I>
334where
335 I: Clone + MaybeSend + MaybeSync + 'static,
336{
337 fn emit_with_context(
338 &self,
339 client: Option<ClientEvent>,
340 _internal: Option<()>,
341 context: EventContext,
342 ) {
343 if let Some(inner) = self.bus.upgrade() {
344 EventBus { inner }.publish(EventEnvelope::new(client, None, context));
345 }
346 }
347}
348
349pub struct PublicBufferWriter<'b, 'bus, I> {
351 buffer: &'b EventBuffer<'bus, I>,
352}
353
354impl<'b, 'bus, I> PublicBufferWriter<'b, 'bus, I> {
355 pub fn new(buffer: &'b EventBuffer<'bus, I>) -> Self {
356 Self { buffer }
357 }
358}
359
360impl<I> EventWriter<()> for PublicBufferWriter<'_, '_, I>
361where
362 I: Clone + MaybeSend + MaybeSync + 'static,
363{
364 fn emit_with_context(
365 &self,
366 client: Option<ClientEvent>,
367 _internal: Option<()>,
368 context: EventContext,
369 ) {
370 self.buffer.emit_with_context(client, None, context);
371 }
372}
373
374struct QueueState<I> {
375 items: VecDeque<EventEnvelope<I>>,
376 discarded: u64,
377 lagged_after: usize,
378 in_flight: usize,
379 warned: bool,
380 closed: bool,
381}
382
383impl<I> Default for QueueState<I> {
384 fn default() -> Self {
385 Self {
386 items: VecDeque::new(),
387 discarded: 0,
388 lagged_after: 0,
389 in_flight: 0,
390 warned: false,
391 closed: false,
392 }
393 }
394}
395
396struct SubscriptionInner<I> {
397 filter: EventFilter<I>,
398 queue_depth: Option<usize>,
399 app: bool,
400 state: Mutex<QueueState<I>>,
401 changed: Notify,
402}
403
404impl<I> SubscriptionInner<I> {
405 fn close(&self) {
406 let mut state = self.state.lock();
407 state.closed = true;
408 state.items.clear();
409 state.discarded = 0;
410 drop(state);
411 self.changed.notify_waiters();
412 }
413}
414
415impl<I: Clone> SubscriptionInner<I> {
416 fn deliver(&self, event: &EventEnvelope<I>) {
417 let public = event
418 .client
419 .as_ref()
420 .filter(|client| self.filter.matches_public(client, &event.context));
421 let internal = event.internal.as_ref().filter(|internal| {
422 let _filter_guard = FilterGuard::enter();
423 self.filter.matches_internal(internal)
424 });
425 if public.is_none() && internal.is_none() {
426 return;
427 }
428 let selected =
429 EventEnvelope::new(public.cloned(), internal.cloned(), event.context.clone());
430 let mut state = self.state.lock();
431 if state.closed {
432 return;
433 }
434 if self
435 .queue_depth
436 .is_some_and(|depth| state.items.len() + state.in_flight >= depth)
437 {
438 if state.discarded == 0 {
439 state.lagged_after = state.items.len();
440 }
441 state.discarded = state.discarded.saturating_add(1);
442 } else {
443 state.items.push_back(selected);
444 if self.queue_depth.is_none()
445 && state.items.len() > UNBOUNDED_QUEUE_WARNING_DEPTH
446 && !state.warned
447 {
448 state.warned = true;
449 tracing::warn!(
450 depth = state.items.len(),
451 "unbounded event subscription queue is growing"
452 );
453 }
454 }
455 drop(state);
456 self.changed.notify_one();
457 }
458
459 fn take(&self, lease: bool) -> Option<Option<EventEnvelope<I>>> {
460 let mut state = self.state.lock();
461 if state.closed {
462 return Some(None);
463 }
464 if state.discarded > 0 && state.lagged_after == 0 {
465 let discarded = std::mem::take(&mut state.discarded);
466 let event = EventEnvelope::new(
467 Some(ClientEvent::Lagged(Lagged { discarded })),
468 None,
469 EventContext::default(),
470 );
471 return Some(Some(event));
472 }
473 if let Some(event) = state.items.pop_front() {
474 if state.lagged_after > 0 {
475 state.lagged_after -= 1;
476 }
477 if lease {
478 state.in_flight += 1;
479 }
480 return Some(Some(event));
481 }
482 None
483 }
484}
485
486pub struct Subscription<I> {
488 inner: Arc<SubscriptionInner<I>>,
489}
490
491impl<I> Subscription<I> {
492 pub fn close(&self) {
493 self.inner.close();
494 }
495
496 pub fn is_closed(&self) -> bool {
497 self.inner.state.lock().closed
498 }
499}
500
501impl<I: Clone> Subscription<I> {
502 pub async fn next(&self) -> Option<EventEnvelope<I>> {
504 loop {
505 let notified = self.inner.changed.notified();
506 tokio::pin!(notified);
507 notified.as_mut().enable();
508 if let Some(item) = self.inner.take(false) {
509 return item;
510 }
511 notified.await;
512 }
513 }
514
515 pub async fn next_for_callback(&self) -> Option<EventLease<I>> {
517 loop {
518 let notified = self.inner.changed.notified();
519 tokio::pin!(notified);
520 notified.as_mut().enable();
521 if let Some(item) = self.inner.take(true) {
522 return item.map(|event| {
523 let counted = event
524 .client
525 .as_ref()
526 .is_none_or(|client| client.kind() != EventKind::Lagged);
527 EventLease {
528 event,
529 subscription: self.inner.clone(),
530 counted,
531 }
532 });
533 }
534 notified.await;
535 }
536 }
537
538 pub fn drain(&self) -> Vec<EventEnvelope<I>> {
540 let mut items = Vec::new();
541 while let Some(Some(event)) = self.inner.take(false) {
542 items.push(event);
543 }
544 items
545 }
546}
547
548impl<I> Drop for Subscription<I> {
549 fn drop(&mut self) {
550 self.close();
551 }
552}
553
554pub struct EventLease<I> {
556 pub event: EventEnvelope<I>,
557 subscription: Arc<SubscriptionInner<I>>,
558 counted: bool,
559}
560
561impl<I> Drop for EventLease<I> {
562 fn drop(&mut self) {
563 let mut state = self.subscription.state.lock();
564 if self.counted {
565 state.in_flight -= 1;
566 }
567 drop(state);
568 self.subscription.changed.notify_one();
569 }
570}
571
572#[cfg(test)]
573mod tests;