concurrent_event/
lib.rs

1//! This crate implements an event which executes registered event handlers
2//! concurrently. Additionally, each handler is assigned an ID with which it
3//! can be referenced after creation. This allows for associated state for each
4//! event handler.
5//!
6//! The system was setup to be resistant to adversarial code. In particular,
7//! IDs are assigned randomly with sufficiently many bits to be unfeasable to
8//! guess. As a consequence, efficiency may suffer. Particularly the IDs are
9//! larger than they would need to be if assigned sequentially.
10//!
11//! # Important Note
12//!
13//! We provide no guarantees as the security properties were not rigorously
14//! verified. Do not use in critical systems without further investigation!
15//!
16//! # Example
17//!
18//! This is a simple usage scenarion with a custom event handler.
19//!
20//! ```
21//! use concurrent_event::Event;
22//! use concurrent_event::handler::EventHandler; 
23//!
24//! struct Printer;
25//!
26//! impl EventHandler<&str> for Printer {
27//!     fn on_event(&mut self, arg: &str) {
28//!         print!("{}", arg);
29//!     }
30//! }
31//!
32//! let mut event = Event::<&str, Printer>::new();
33//! event.emit("Hello, World!");
34//! ```
35//!
36//! In the `handler` package, default implementation for stateless and stateful
37//! event handlers can be found, which take a closure at construction.
38
39use std::collections::HashMap;
40use std::marker::PhantomData;
41
42use crossbeam::thread;
43
44use crate::id::HandlerId;
45use crate::handler::EventHandler;
46
47pub mod id;
48pub mod handler;
49
50/// An event manages multiple handlers which can be registered.
51///
52/// # Type Parameters
53///
54/// * `A`: The type of event arguments which are distributed to the handlers.
55/// * `H`: The type of event handlers which can be registered with this event.
56/// To allow for different types, use `Box<dyn EventHandler<...>>`.
57pub struct Event<A: Copy + Send, H: EventHandler<A>> {
58    arg_type: PhantomData<A>,
59    handlers: HashMap<HandlerId, H>
60}
61
62impl<A: Copy + Send, H: EventHandler<A>> Event<A, H> {
63
64    /// Creates a new event without handlers.
65    pub fn new() -> Event<A, H> {
66        Event {
67            arg_type: PhantomData,
68            handlers: HashMap::new()
69        }
70    }
71
72    /// Emits an event, invoking all currently registered handlers in parallel.
73    /// If all event handlers terminated without panicking, `true` is returned.
74    /// If any event handler panics, `false` is returned.
75    ///
76    /// # Parameters
77    ///
78    /// * `arg`: The event argument to dispatch.
79    pub fn emit(&mut self, arg: A) -> bool {
80        thread::scope(|s| {
81            for handler in self.handlers.values_mut() {
82                s.spawn(move |_| handler.on_event(arg));
83            }
84        }).is_ok()
85    }
86
87    /// Adds an event handler to notify for future events. A handler ID is
88    /// returned, which can be used to identify the handler later.
89    ///
90    /// # Parameters
91    ///
92    /// * `handler`: The event handler to register.
93    pub fn add_handler(&mut self, handler: H) -> HandlerId {
94        let id = HandlerId::new();
95        self.handlers.insert(id, handler);
96        id
97    }
98
99    /// Gets a reference to the event handler registered under the given ID
100    /// wrapped in a `Some` option variant. If no such handler is registered,
101    /// `None` is returned.
102    ///
103    /// # Parameters
104    ///
105    /// * `id`: The handler ID for which to get the associated event handler.
106    pub fn get_handler(&self, id: HandlerId) -> Option<&H> {
107        self.handlers.get(&id)
108    }
109}
110
111impl<'a, A: Copy + Send> Event<A, Box<dyn EventHandler<A> + 'a>> {
112
113    /// Adds an event handler wrapped into a box to this event. This is mainly
114    /// syntactic sugar for `event.add_handler(Box::new(handler))`.
115    ///
116    /// # Parameters
117    ///
118    /// * `handler`: The event handler to wrap in a box and register with this
119    /// event.
120    pub fn add_handler_boxed(&'a mut self, handler: impl EventHandler<A> + 'a) -> HandlerId {
121        self.add_handler(Box::new(handler))
122    }
123}
124
125#[cfg(test)]
126mod test;