diff --git a/Cargo.toml b/Cargo.toml index 22bd919..e0a0ff7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "eventbus" -version = "0.3.2" +version = "0.4.0" authors = ["SoniEx2 "] description = "Safe, fast and concurrent event system, inspired by the MinecraftForge event bus." keywords = ["event", "safe", "fast", "concurrent", "bus"] diff --git a/src/lib.rs b/src/lib.rs index a737c1a..105b7ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,43 +23,68 @@ macro_rules! register_hook { ($b:expr, $p:expr, $t:ty, $h:expr) => { { - static EVENT_ID: ::std::sync::atomic::AtomicUsize = std::sync::atomic::ATOMIC_USIZE_INIT; - static EVENT_ID_INIT: ::std::sync::Once = std::sync::ONCE_INIT; - // no generics allowed. - static DUMMY: ::std::marker::PhantomData<$t> = ::std::marker::PhantomData; - EVENT_ID_INIT.call_once(|| { - EVENT_ID.store($crate::get_event_id::<$t>(), std::sync::atomic::Ordering::Relaxed); - }); - let id = EVENT_ID.load(std::sync::atomic::Ordering::Relaxed); - $crate::register::<$t, _>($b, $p, $h, id) + // hygiene + let bus: &$crate::EventBus = $b; + let hook = $h; + let pri = $p; + { + static EVENT_ID: ::std::sync::atomic::AtomicUsize = std::sync::atomic::ATOMIC_USIZE_INIT; + static EVENT_ID_INIT: ::std::sync::Once = std::sync::ONCE_INIT; + // no generics allowed. + static DUMMY: ::std::marker::PhantomData = ::std::marker::PhantomData; + EVENT_ID_INIT.call_once(|| { + EVENT_ID.store($crate::get_event_id::<$t>(), std::sync::atomic::Ordering::Relaxed); + }); + let id = EVENT_ID.load(std::sync::atomic::Ordering::Relaxed); + $crate::register::<$t, _>(bus, pri, hook, id) + } } } } /// Posts an event. /// -/// Usage: `post!(bus, type, event)` +/// Usage: `post_event!(bus, type, event)` #[macro_export] macro_rules! post_event { + // TODO allow multiple $t:ty at once and re-#[doc(hidden)] the low-level get_post_targets and + // get_event_id ($b:expr, $t:ty, $e:expr) => { { - static EVENT_ID: ::std::sync::atomic::AtomicUsize = std::sync::atomic::ATOMIC_USIZE_INIT; - static EVENT_ID_INIT: ::std::sync::Once = std::sync::ONCE_INIT; - // no generics allowed. - static DUMMY: ::std::marker::PhantomData<$t> = ::std::marker::PhantomData; - EVENT_ID_INIT.call_once(|| { - EVENT_ID.store($crate::get_event_id::<$t>(), std::sync::atomic::Ordering::Relaxed); - }); - let id = EVENT_ID.load(std::sync::atomic::Ordering::Relaxed); - let event: &mut $t = $e; - let handlers = $crate::get_post_targets::<$t>($b, event, id); - for (_pri, fun) in handlers.iter() { - fun(event); - if <$t as $crate::Event>::cancellable() && <$t as $crate::Event>::cancelled(event) { - break; + // hygiene + let bus: &$crate::EventBus = $b; + // event setup, this may evaluate to any type. + let event: &mut _ = $e; + { + // it is a logic error for an event's cancellability to change based on its value. + static CANCELLABLE: ::std::sync::atomic::AtomicBool = ::std::sync::atomic::ATOMIC_BOOL_INIT; + static CANCELLABLE_INIT: ::std::sync::Once = ::std::sync::ONCE_INIT; + CANCELLABLE_INIT.call_once(|| { + CANCELLABLE.store(<$crate::Event>::cancellable(event), std::sync::atomic::Ordering::Relaxed); + }); + let cancellable = CANCELLABLE.load(std::sync::atomic::Ordering::Relaxed); + + // event type setup + static EVENT_ID: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT; + static EVENT_ID_INIT: ::std::sync::Once = ::std::sync::ONCE_INIT; + // no generics allowed. + static DUMMY: ::std::marker::PhantomData = ::std::marker::PhantomData; + EVENT_ID_INIT.call_once(|| { + EVENT_ID.store($crate::get_event_id::<$t>(), std::sync::atomic::Ordering::Relaxed); + }); + let id = EVENT_ID.load(std::sync::atomic::Ordering::Relaxed); + + // handler retrieval and invokation + let event: &mut $t = event; + let handlers = $crate::get_post_targets::<$t>(bus, event, id); + for (_pri, fun) in handlers.iter() { + fun(event); + if cancellable && <$t as $crate::Event>::cancelled(event) { + break; + } } + cancellable && <$t as $crate::Event>::cancelled(event) } - <$t as $crate::Event>::cancellable() && <$t as $crate::Event>::cancelled(event) } } } @@ -69,7 +94,11 @@ pub trait Event: 'static { /// Returns whether this event is cancellable. /// /// When this is true, `cancelled` and `cancel` should also be implemented. - fn cancellable() -> bool { + /// + /// Note: While this method does take a `&self`, it's a logic error for an event's + /// cancellability to change based on its value. The `&self` is just for trait objectification. + /// You have been warned! + fn cancellable(&self) -> bool { false } @@ -84,6 +113,7 @@ pub trait Event: 'static { /// /// Panics if this event is not cancellable. fn set_cancelled(&mut self, cancel: bool) { + let _ = cancel; panic!("not cancellable"); } } @@ -122,12 +152,12 @@ mod id_map { lazy_static! { static ref EVENT_ID_MAP: Mutex<::anymap::Map<::anymap::any::Any + Send + Sync>> = Mutex::new(::anymap::Map::new()); } - struct EventId { + struct EventId { id: usize, _t: PhantomData } - pub fn get_event_id() -> usize { + pub fn get_event_id() -> usize { static EVENT_ID_COUNT: AtomicUsize = ATOMIC_USIZE_INIT; let id = EVENT_ID_MAP.lock().expect("failed to allocate event id").entry::>().or_insert_with(|| EventId { id: EVENT_ID_COUNT.fetch_add(1, AtomicOrdering::SeqCst), _t: PhantomData }).id; let handlers: Arc> = Default::default(); @@ -139,15 +169,15 @@ mod id_map { /// Low-level event handling function, returns the event ID for use with get_post_targets. /// /// This function is (extremely) slow! -pub fn get_event_id() -> usize { +pub fn get_event_id() -> usize { id_map::get_event_id::() } type RwArcVec = RwLock>>; -struct Handlers(RwArcVec)>>); +struct Handlers(RwArcVec)>>); -impl Default for Handlers { +impl Default for Handlers { fn default() -> Self { Handlers(Default::default()) } @@ -165,7 +195,7 @@ lazy_static! { /// # Panics /// /// Panics if `id` doesn't match the given `T` (but not if the given `id` doesn't exist). -pub fn get_post_targets(bus: &EventBus, event: &mut T, id: usize) -> Arc)>> { +pub fn get_post_targets(bus: &EventBus, _event: &mut T, id: usize) -> Arc)>> { let target: Option>> = HANDLERS.read().expect("failed to lock for reading").get(id).map(|arc| Arc::clone(arc)) .map(|any| Arc::downcast::>(any).expect("Wrong id")) .and_then(|handlers| handlers.0.read().expect("failed to lock for reading").get(bus.id).map(|hooks| Arc::clone(&*hooks.read().expect("failed to lock for reading")))); @@ -180,7 +210,7 @@ pub fn get_post_targets(bus: &EventBus, event: &mut T, id: usize) -> A /// # Panics /// /// Panics if `id` doesn't match the given `T`, or if `id` doesn't exist. -pub fn register Fn(&'a mut T) + Send + Sync + 'static>(bus: &EventBus, priority: i32, handler: F, id: usize) { +pub fn register Fn(&'a mut T) + Send + Sync + 'static>(bus: &EventBus, priority: i32, handler: F, id: usize) { HANDLERS.read().expect("failed to lock for reading").get(id).map(|arc| Arc::clone(arc)) .map(|any| Arc::downcast::>(any).expect("Wrong id")) .map(|handlers| { diff --git a/tests/compile-fail/README b/tests/compile-fail/README new file mode 100644 index 0000000..1da9fec --- /dev/null +++ b/tests/compile-fail/README @@ -0,0 +1 @@ +These are currently not being automatically tested because compiletest_rs doesn't work on stable. diff --git a/tests/compile-fail/hygiene.rs b/tests/compile-fail/hygiene.rs new file mode 100644 index 0000000..6fea17a --- /dev/null +++ b/tests/compile-fail/hygiene.rs @@ -0,0 +1,18 @@ +#[macro_use] +extern crate eventbus; + +use eventbus::{Event, EventBus}; + +struct MyEvent { + i: i32 +} + +impl Event for MyEvent { +} + +#[test] +fn test_hygiene() { + let bus = [EventBus::new()]; + let mut event = MyEvent { i: 3 }; + post_event!(&bus[EVENT_ID.load(::std::sync::atomic::Ordering::Relaxed)], MyEvent, &mut event); +} diff --git a/tests/dyn_events.rs b/tests/dyn_events.rs new file mode 100644 index 0000000..0407f7f --- /dev/null +++ b/tests/dyn_events.rs @@ -0,0 +1,53 @@ +#[macro_use] +extern crate eventbus; + +use eventbus::{Event, EventBus}; + +trait MyEvent : Event { + fn get_i(&self) -> i32; + fn set_i(&mut self, i32); +} + +struct MyEventImpl { + i: i32 +} + +impl Event for MyEventImpl { +} +impl MyEvent for MyEventImpl { + fn get_i(&self) -> i32 { + self.i + } + fn set_i(&mut self, i: i32) { + self.i = i; + } +} + +fn add_handler(e: &mut MyEvent) { + /* adds 1 */ + let i = e.get_i(); + e.set_i(i+1); +} + +fn no_handler(e: &mut MyEvent) { + /* does nothing */ + let i = e.get_i(); + e.set_i(i); +} + +#[test] +fn test_dyn_usage() { + let event_bus = EventBus::new(); + //let handler_id = event_bus.register(add_handler, 0); + register_hook!(&event_bus, 0, dyn MyEvent, add_handler); + let mut event = MyEventImpl { i: 3 }; + assert_eq!(event.i, 3); + post_event!(&event_bus, dyn MyEvent, &mut event); + assert_eq!(event.i, 4); + register_hook!(&event_bus, 1, dyn MyEvent, no_handler); + post_event!(&event_bus, dyn MyEvent, &mut event); + assert_eq!(event.i, 5); + //event_bus.unregister(handler_id); + post_event!(&event_bus, dyn MyEvent, &mut event); + //assert_eq!(event.i, 5); +}