1
#![cfg_attr(docsrs, feature(doc_cfg))]
2
#![doc = include_str!("../README.md")]
3
// @@ begin lint list maintained by maint/add_warning @@
4
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
6
#![warn(missing_docs)]
7
#![warn(noop_method_call)]
8
#![warn(unreachable_pub)]
9
#![warn(clippy::all)]
10
#![deny(clippy::await_holding_lock)]
11
#![deny(clippy::cargo_common_metadata)]
12
#![deny(clippy::cast_lossless)]
13
#![deny(clippy::checked_conversions)]
14
#![allow(clippy::cognitive_complexity)] // See arti#2556
15
#![deny(clippy::debug_assert_with_mut_call)]
16
#![deny(clippy::exhaustive_enums)]
17
#![deny(clippy::exhaustive_structs)]
18
#![deny(clippy::expl_impl_clone_on_copy)]
19
#![deny(clippy::fallible_impl_from)]
20
#![deny(clippy::implicit_clone)]
21
#![deny(clippy::large_stack_arrays)]
22
#![warn(clippy::manual_ok_or)]
23
#![deny(clippy::missing_docs_in_private_items)]
24
#![warn(clippy::needless_borrow)]
25
#![warn(clippy::needless_pass_by_value)]
26
#![warn(clippy::option_option)]
27
#![warn(clippy::or_fun_call)]
28
#![deny(clippy::print_stderr)]
29
#![deny(clippy::print_stdout)]
30
#![warn(clippy::rc_buffer)]
31
#![deny(clippy::ref_option_ref)]
32
#![warn(clippy::semicolon_if_nothing_returned)]
33
#![warn(clippy::trait_duplication_in_bounds)]
34
#![deny(clippy::unchecked_time_subtraction)]
35
#![deny(clippy::unnecessary_wraps)]
36
#![warn(clippy::unseparated_literal_suffix)]
37
#![deny(clippy::unwrap_used)]
38
#![deny(clippy::mod_module_files)]
39
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
40
#![allow(clippy::uninlined_format_args)]
41
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
42
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
43
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
44
#![allow(clippy::needless_lifetimes)] // See arti#1765
45
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
46
#![allow(clippy::collapsible_if)] // See arti#2342
47
#![deny(clippy::unused_async)]
48
#![deny(clippy::string_slice)] // See arti#2571
49
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
50
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
51

            
52
pub mod events;
53

            
54
use crate::events::{TorEvent, TorEventKind};
55
use async_broadcast::{InactiveReceiver, Receiver, Sender, TrySendError};
56
use futures::StreamExt;
57
use futures::channel::mpsc;
58
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
59
use futures::future::Either;
60
use std::pin::Pin;
61
use std::sync::OnceLock;
62
use std::sync::atomic::{AtomicUsize, Ordering};
63
use std::task::{Context, Poll};
64
use thiserror::Error;
65
use tracing::{error, warn};
66

            
67
/// Pointer to an `UnboundedSender`, used to send events into the `EventReactor`.
68
static EVENT_SENDER: OnceLock<UnboundedSender<TorEvent>> = OnceLock::new();
69
/// An inactive receiver for the currently active broadcast channel, if there is one.
70
static CURRENT_RECEIVER: OnceLock<InactiveReceiver<TorEvent>> = OnceLock::new();
71
/// The number of `TorEventKind`s there are.
72
const EVENT_KIND_COUNT: usize = 1;
73
/// An array containing one `AtomicUsize` for each `TorEventKind`, used to track subscriptions.
74
///
75
/// When a `TorEventReceiver` subscribes to a `TorEventKind`, it uses its `usize` value to index
76
/// into this array and increment the associated `AtomicUsize` (and decrements it to unsubscribe).
77
/// This lets event emitters check whether there are any subscribers, and avoid emitting events
78
/// if there aren't.
79
static EVENT_SUBSCRIBERS: [AtomicUsize; EVENT_KIND_COUNT] = [AtomicUsize::new(0); EVENT_KIND_COUNT];
80

            
81
/// The size of the internal broadcast channel used to implement event subscription.
82
pub static BROADCAST_CAPACITY: usize = 512;
83

            
84
/// A reactor used to forward events to make the event reporting system work.
85
///
86
/// # Note
87
///
88
/// Currently, this type is a singleton; there is one event reporting system used for the entire
89
/// program. This is not stable, and may change in future.
90
pub struct EventReactor {
91
    /// A receiver that the reactor uses to learn about incoming events.
92
    ///
93
    /// This is unbounded so that event publication doesn't have to be async.
94
    receiver: UnboundedReceiver<TorEvent>,
95
    /// A sender that the reactor uses to publish events.
96
    ///
97
    /// Events are only sent here if at least one subscriber currently wants them.
98
    broadcast: Sender<TorEvent>,
99
}
100

            
101
impl EventReactor {
102
    /// Initialize the event reporting system, returning a reactor that must be run for it to work,
103
    /// and a `TorEventReceiver` that can be used to extract events from the system. If the system
104
    /// has already been initialized, returns `None` instead of a reactor.
105
    ///
106
    /// # Warnings
107
    ///
108
    /// The returned reactor *must* be run with `EventReactor::run`, in a background async task.
109
    /// If it is not, the event system might consume unbounded amounts of memory.
110
4
    pub fn new() -> Option<Self> {
111
4
        let (tx, rx) = mpsc::unbounded();
112
4
        if EVENT_SENDER.set(tx).is_ok() {
113
1
            let (btx, brx) = async_broadcast::broadcast(BROADCAST_CAPACITY);
114
1
            CURRENT_RECEIVER
115
1
                .set(brx.deactivate())
116
1
                .expect("CURRENT_RECEIVER can't be set if EVENT_SENDER is unset!");
117
1
            Some(Self {
118
1
                receiver: rx,
119
1
                broadcast: btx,
120
1
            })
121
        } else {
122
3
            None
123
        }
124
4
    }
125
    /// Get a `TorEventReceiver` to receive events from, assuming an `EventReactor` is already
126
    /// running somewhere. (If it isn't, returns `None`.)
127
    ///
128
    /// As noted in the type-level documentation, this function might not always work this way.
129
4
    pub fn receiver() -> Option<TorEventReceiver> {
130
4
        CURRENT_RECEIVER
131
4
            .get()
132
4
            .map(|rx| TorEventReceiver::wrap(rx.clone()))
133
4
    }
134
    /// Run the event forwarding reactor.
135
    ///
136
    /// You *must* call this function once a reactor is created.
137
1
    pub async fn run(mut self) {
138
2
        while let Some(event) = self.receiver.next().await {
139
1
            match self.broadcast.try_broadcast(event) {
140
1
                Ok(_) => {}
141
                Err(TrySendError::Closed(_)) => break,
142
                Err(TrySendError::Full(event)) => {
143
                    // If the channel is full, do a blocking broadcast to wait for it to be
144
                    // not full, and log a warning about receivers lagging behind.
145
                    warn!("TorEventReceivers aren't receiving events fast enough!");
146
                    if self.broadcast.broadcast(event).await.is_err() {
147
                        break;
148
                    }
149
                }
150
                Err(TrySendError::Inactive(_)) => {
151
                    // no active receivers, so just drop the event on the floor.
152
                }
153
            }
154
        }
155
        // It shouldn't be possible to get here, since we have globals keeping the channels
156
        // open. Still, if we somehow do, log an error about it.
157
        error!("event reactor shutting down; this shouldn't ever happen");
158
    }
159
}
160

            
161
/// An error encountered when trying to receive a `TorEvent`.
162
#[derive(Clone, Debug, Error)]
163
#[non_exhaustive]
164
pub enum ReceiverError {
165
    /// The receiver isn't subscribed to anything, so wouldn't ever return any events.
166
    #[error("No event subscriptions")]
167
    NoSubscriptions,
168
    /// The internal broadcast channel was closed, which shouldn't ever happen.
169
    #[error("Internal event broadcast channel closed")]
170
    ChannelClosed,
171
}
172

            
173
/// A receiver for `TorEvent`s emitted by other users of this crate.
174
///
175
/// To use this type, first subscribe to some kinds of event by calling
176
/// `TorEventReceiver::subscribe`. Then, consume events using the implementation of
177
/// `futures::stream::Stream`.
178
///
179
/// # Warning
180
///
181
/// Once interest in events has been signalled with `subscribe`, events must be continuously
182
/// read from the receiver in order to avoid excessive memory consumption.
183
#[derive(Clone, Debug)]
184
pub struct TorEventReceiver {
185
    /// If no events have been subscribed to yet, this is an `InactiveReceiver`; otherwise,
186
    /// it's a `Receiver`.
187
    inner: Either<Receiver<TorEvent>, InactiveReceiver<TorEvent>>,
188
    /// Whether we're subscribed to each event kind (if `subscribed[kind]` is true, we're
189
    /// subscribed to `kind`).
190
    subscribed: [bool; EVENT_KIND_COUNT],
191
}
192

            
193
impl futures::stream::Stream for TorEventReceiver {
194
    type Item = TorEvent;
195

            
196
5
    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
197
5
        let this = self.get_mut();
198
5
        match this.inner {
199
4
            Either::Left(ref mut active) => loop {
200
4
                match Pin::new(&mut *active).poll_next(cx) {
201
1
                    Poll::Ready(Some(e)) => {
202
1
                        if this.subscribed[e.kind() as usize] {
203
1
                            return Poll::Ready(Some(e));
204
                        }
205
                        // loop, since we weren't subscribed to that event
206
                    }
207
3
                    x => return x,
208
                }
209
            },
210
            Either::Right(_) => {
211
1
                warn!("TorEventReceiver::poll_next() called without subscriptions!");
212
1
                Poll::Ready(None)
213
            }
214
        }
215
5
    }
216
}
217

            
218
impl TorEventReceiver {
219
    /// Create a `TorEventReceiver` from an `InactiveReceiver` handle.
220
4
    pub(crate) fn wrap(rx: InactiveReceiver<TorEvent>) -> Self {
221
4
        Self {
222
4
            inner: Either::Right(rx),
223
4
            subscribed: [false; EVENT_KIND_COUNT],
224
4
        }
225
4
    }
226
    /// Subscribe to a given kind of `TorEvent`.
227
    ///
228
    /// After calling this function, `TorEventReceiver::recv` will emit events of that kind.
229
    /// This function is idempotent (subscribing twice has the same effect as doing so once).
230
7
    pub fn subscribe(&mut self, kind: TorEventKind) {
231
7
        if !self.subscribed[kind as usize] {
232
5
            EVENT_SUBSCRIBERS[kind as usize].fetch_add(1, Ordering::SeqCst);
233
5
            self.subscribed[kind as usize] = true;
234
5
        }
235
        // FIXME(eta): cloning is ungood, but hard to avoid
236
7
        if let Either::Right(inactive) = self.inner.clone() {
237
5
            self.inner = Either::Left(inactive.activate());
238
5
        }
239
7
    }
240
    /// Unsubscribe from a given kind of `TorEvent`.
241
    ///
242
    /// After calling this function, `TorEventReceiver::recv` will no longer emit events of that
243
    /// kind.
244
    /// This function is idempotent (unsubscribing twice has the same effect as doing so once).
245
2
    pub fn unsubscribe(&mut self, kind: TorEventKind) {
246
2
        if self.subscribed[kind as usize] {
247
2
            EVENT_SUBSCRIBERS[kind as usize].fetch_sub(1, Ordering::SeqCst);
248
2
            self.subscribed[kind as usize] = false;
249
2
        }
250
        // If we're now not subscribed to anything, deactivate our channel.
251
2
        if self.subscribed.iter().all(|x| !*x) {
252
            // FIXME(eta): cloning is ungood, but hard to avoid
253
2
            if let Either::Left(active) = self.inner.clone() {
254
2
                self.inner = Either::Right(active.deactivate());
255
2
            }
256
        }
257
2
    }
258
}
259

            
260
impl Drop for TorEventReceiver {
261
4
    fn drop(&mut self) {
262
4
        for (i, subscribed) in self.subscribed.iter().enumerate() {
263
            // FIXME(eta): duplicates logic from Self::unsubscribe, because it's not possible
264
            //             to go from a `usize` to a `TorEventKind`
265
4
            if *subscribed {
266
3
                EVENT_SUBSCRIBERS[i].fetch_sub(1, Ordering::SeqCst);
267
3
            }
268
        }
269
4
    }
270
}
271

            
272
/// Returns a boolean indicating whether the event `kind` has any subscribers (as in,
273
/// whether `TorEventReceiver::subscribe` has been called with that event kind).
274
///
275
/// This is useful to avoid doing work to generate events that might be computationally expensive
276
/// to generate.
277
10
pub fn event_has_subscribers(kind: TorEventKind) -> bool {
278
10
    EVENT_SUBSCRIBERS[kind as usize].load(Ordering::SeqCst) > 0
279
10
}
280

            
281
/// Broadcast the given `TorEvent` to any interested subscribers.
282
///
283
/// As an optimization, does nothing if the event has no subscribers (`event_has_subscribers`
284
/// returns false). (also does nothing if the event subsystem hasn't been initialized yet)
285
///
286
/// This function isn't intended for use outside Arti crates (as in, library consumers of Arti
287
/// shouldn't broadcast events!).
288
2
pub fn broadcast(event: TorEvent) {
289
2
    if !event_has_subscribers(event.kind()) {
290
1
        return;
291
1
    }
292
1
    if let Some(sender) = EVENT_SENDER.get() {
293
1
        // If this fails, there isn't much we can really do about it!
294
1
        let _ = sender.unbounded_send(event);
295
1
    }
296
2
}
297

            
298
#[cfg(test)]
299
mod test {
300
    // @@ begin test lint list maintained by maint/add_warning @@
301
    #![allow(clippy::bool_assert_comparison)]
302
    #![allow(clippy::clone_on_copy)]
303
    #![allow(clippy::dbg_macro)]
304
    #![allow(clippy::mixed_attributes_style)]
305
    #![allow(clippy::print_stderr)]
306
    #![allow(clippy::print_stdout)]
307
    #![allow(clippy::single_char_pattern)]
308
    #![allow(clippy::unwrap_used)]
309
    #![allow(clippy::unchecked_time_subtraction)]
310
    #![allow(clippy::useless_vec)]
311
    #![allow(clippy::needless_pass_by_value)]
312
    #![allow(clippy::string_slice)] // See arti#2571
313
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
314
    use crate::{
315
        EventReactor, StreamExt, TorEvent, TorEventKind, broadcast, event_has_subscribers,
316
    };
317
    use std::sync::{Mutex, MutexGuard, OnceLock};
318
    use std::time::Duration;
319
    use tokio::runtime::Runtime;
320

            
321
    // HACK(eta): these tests need to run effectively singlethreaded, since they mutate global
322
    //            state. They *also* need to share the same tokio runtime, which the
323
    //            #[tokio::test] thing doesn't do (it makes a new runtime per test), because of
324
    //            the need to have a background singleton EventReactor.
325
    //
326
    //            To hack around this, we just have a global runtime protected by a mutex!
327
    static TEST_MUTEX: OnceLock<Mutex<Runtime>> = OnceLock::new();
328

            
329
    /// Locks the mutex, and makes sure the event reactor is initialized.
330
    fn test_setup() -> MutexGuard<'static, Runtime> {
331
        let mutex = TEST_MUTEX.get_or_init(|| Mutex::new(Runtime::new().unwrap()));
332
        let runtime = mutex
333
            .lock()
334
            .expect("mutex poisoned, probably by other failing tests");
335
        if let Some(reactor) = EventReactor::new() {
336
            runtime.handle().spawn(reactor.run());
337
        }
338
        runtime
339
    }
340

            
341
    #[test]
342
    fn subscriptions() {
343
        let rt = test_setup();
344

            
345
        rt.block_on(async move {
346
            // shouldn't have any subscribers at the start
347
            assert!(!event_has_subscribers(TorEventKind::Empty));
348

            
349
            let mut rx = EventReactor::receiver().unwrap();
350
            // creating a receiver shouldn't result in any subscriptions
351
            assert!(!event_has_subscribers(TorEventKind::Empty));
352

            
353
            rx.subscribe(TorEventKind::Empty);
354
            // subscription should work
355
            assert!(event_has_subscribers(TorEventKind::Empty));
356

            
357
            rx.unsubscribe(TorEventKind::Empty);
358
            // unsubscribing should work
359
            assert!(!event_has_subscribers(TorEventKind::Empty));
360

            
361
            // subscription should be idempotent
362
            rx.subscribe(TorEventKind::Empty);
363
            rx.subscribe(TorEventKind::Empty);
364
            rx.subscribe(TorEventKind::Empty);
365
            assert!(event_has_subscribers(TorEventKind::Empty));
366

            
367
            rx.unsubscribe(TorEventKind::Empty);
368
            assert!(!event_has_subscribers(TorEventKind::Empty));
369

            
370
            rx.subscribe(TorEventKind::Empty);
371
            assert!(event_has_subscribers(TorEventKind::Empty));
372

            
373
            std::mem::drop(rx);
374
            // dropping the receiver should auto-unsubscribe
375
            assert!(!event_has_subscribers(TorEventKind::Empty));
376
        });
377
    }
378

            
379
    #[test]
380
    fn empty_recv() {
381
        let rt = test_setup();
382

            
383
        rt.block_on(async move {
384
            let mut rx = EventReactor::receiver().unwrap();
385
            // attempting to read from a receiver with no subscriptions should return None
386
            let result = rx.next().await;
387
            assert!(result.is_none());
388
        });
389
    }
390

            
391
    #[test]
392
    fn receives_events() {
393
        let rt = test_setup();
394

            
395
        rt.block_on(async move {
396
            let mut rx = EventReactor::receiver().unwrap();
397
            rx.subscribe(TorEventKind::Empty);
398
            // HACK(eta): give the event reactor time to run
399
            tokio::time::sleep(Duration::from_millis(100)).await;
400
            broadcast(TorEvent::Empty);
401

            
402
            let result = rx.next().await;
403
            assert_eq!(result, Some(TorEvent::Empty));
404
        });
405
    }
406

            
407
    #[test]
408
    fn does_not_send_to_no_subscribers() {
409
        let rt = test_setup();
410

            
411
        rt.block_on(async move {
412
            // this event should just get dropped on the floor, because no subscribers exist
413
            broadcast(TorEvent::Empty);
414

            
415
            let mut rx = EventReactor::receiver().unwrap();
416
            rx.subscribe(TorEventKind::Empty);
417

            
418
            // this shouldn't have an event to receive now
419
            let result = tokio::time::timeout(Duration::from_millis(100), rx.next()).await;
420
            assert!(result.is_err());
421
        });
422
    }
423
}