Add support for trait objects, fix hygiene
This commit is contained in:
parent
0fc0b42f31
commit
cf0ddcb406
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "eventbus"
|
name = "eventbus"
|
||||||
version = "0.3.2"
|
version = "0.4.0"
|
||||||
authors = ["SoniEx2 <endermoneymod@gmail.com>"]
|
authors = ["SoniEx2 <endermoneymod@gmail.com>"]
|
||||||
description = "Safe, fast and concurrent event system, inspired by the MinecraftForge event bus."
|
description = "Safe, fast and concurrent event system, inspired by the MinecraftForge event bus."
|
||||||
keywords = ["event", "safe", "fast", "concurrent", "bus"]
|
keywords = ["event", "safe", "fast", "concurrent", "bus"]
|
||||||
|
|
96
src/lib.rs
96
src/lib.rs
|
@ -23,43 +23,68 @@
|
||||||
macro_rules! register_hook {
|
macro_rules! register_hook {
|
||||||
($b:expr, $p:expr, $t:ty, $h:expr) => {
|
($b:expr, $p:expr, $t:ty, $h:expr) => {
|
||||||
{
|
{
|
||||||
static EVENT_ID: ::std::sync::atomic::AtomicUsize = std::sync::atomic::ATOMIC_USIZE_INIT;
|
// hygiene
|
||||||
static EVENT_ID_INIT: ::std::sync::Once = std::sync::ONCE_INIT;
|
let bus: &$crate::EventBus = $b;
|
||||||
// no generics allowed.
|
let hook = $h;
|
||||||
static DUMMY: ::std::marker::PhantomData<$t> = ::std::marker::PhantomData;
|
let pri = $p;
|
||||||
EVENT_ID_INIT.call_once(|| {
|
{
|
||||||
EVENT_ID.store($crate::get_event_id::<$t>(), std::sync::atomic::Ordering::Relaxed);
|
static EVENT_ID: ::std::sync::atomic::AtomicUsize = std::sync::atomic::ATOMIC_USIZE_INIT;
|
||||||
});
|
static EVENT_ID_INIT: ::std::sync::Once = std::sync::ONCE_INIT;
|
||||||
let id = EVENT_ID.load(std::sync::atomic::Ordering::Relaxed);
|
// no generics allowed.
|
||||||
$crate::register::<$t, _>($b, $p, $h, id)
|
static DUMMY: ::std::marker::PhantomData<dyn Fn(&mut $t) + Send + Sync> = ::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.
|
/// Posts an event.
|
||||||
///
|
///
|
||||||
/// Usage: `post!(bus, type, event)`
|
/// Usage: `post_event!(bus, type, event)`
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! post_event {
|
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) => {
|
($b:expr, $t:ty, $e:expr) => {
|
||||||
{
|
{
|
||||||
static EVENT_ID: ::std::sync::atomic::AtomicUsize = std::sync::atomic::ATOMIC_USIZE_INIT;
|
// hygiene
|
||||||
static EVENT_ID_INIT: ::std::sync::Once = std::sync::ONCE_INIT;
|
let bus: &$crate::EventBus = $b;
|
||||||
// no generics allowed.
|
// event setup, this may evaluate to any type.
|
||||||
static DUMMY: ::std::marker::PhantomData<$t> = ::std::marker::PhantomData;
|
let event: &mut _ = $e;
|
||||||
EVENT_ID_INIT.call_once(|| {
|
{
|
||||||
EVENT_ID.store($crate::get_event_id::<$t>(), std::sync::atomic::Ordering::Relaxed);
|
// 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;
|
||||||
let id = EVENT_ID.load(std::sync::atomic::Ordering::Relaxed);
|
static CANCELLABLE_INIT: ::std::sync::Once = ::std::sync::ONCE_INIT;
|
||||||
let event: &mut $t = $e;
|
CANCELLABLE_INIT.call_once(|| {
|
||||||
let handlers = $crate::get_post_targets::<$t>($b, event, id);
|
CANCELLABLE.store(<$crate::Event>::cancellable(event), std::sync::atomic::Ordering::Relaxed);
|
||||||
for (_pri, fun) in handlers.iter() {
|
});
|
||||||
fun(event);
|
let cancellable = CANCELLABLE.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
if <$t as $crate::Event>::cancellable() && <$t as $crate::Event>::cancelled(event) {
|
|
||||||
break;
|
// 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<dyn Fn(&mut $t) + Send + Sync> = ::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.
|
/// Returns whether this event is cancellable.
|
||||||
///
|
///
|
||||||
/// When this is true, `cancelled` and `cancel` should also be implemented.
|
/// 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
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +113,7 @@ pub trait Event: 'static {
|
||||||
///
|
///
|
||||||
/// Panics if this event is not cancellable.
|
/// Panics if this event is not cancellable.
|
||||||
fn set_cancelled(&mut self, cancel: bool) {
|
fn set_cancelled(&mut self, cancel: bool) {
|
||||||
|
let _ = cancel;
|
||||||
panic!("not cancellable");
|
panic!("not cancellable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,12 +152,12 @@ mod id_map {
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref EVENT_ID_MAP: Mutex<::anymap::Map<::anymap::any::Any + Send + Sync>> = Mutex::new(::anymap::Map::new());
|
static ref EVENT_ID_MAP: Mutex<::anymap::Map<::anymap::any::Any + Send + Sync>> = Mutex::new(::anymap::Map::new());
|
||||||
}
|
}
|
||||||
struct EventId<T: Event> {
|
struct EventId<T: Event + ?Sized> {
|
||||||
id: usize,
|
id: usize,
|
||||||
_t: PhantomData<dyn Fn(&mut T) + Send + Sync>
|
_t: PhantomData<dyn Fn(&mut T) + Send + Sync>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_event_id<T: Event>() -> usize {
|
pub fn get_event_id<T: Event + ?Sized>() -> usize {
|
||||||
static EVENT_ID_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
|
static EVENT_ID_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||||
let id = EVENT_ID_MAP.lock().expect("failed to allocate event id").entry::<EventId<T>>().or_insert_with(|| EventId { id: EVENT_ID_COUNT.fetch_add(1, AtomicOrdering::SeqCst), _t: PhantomData }).id;
|
let id = EVENT_ID_MAP.lock().expect("failed to allocate event id").entry::<EventId<T>>().or_insert_with(|| EventId { id: EVENT_ID_COUNT.fetch_add(1, AtomicOrdering::SeqCst), _t: PhantomData }).id;
|
||||||
let handlers: Arc<Handlers<T>> = Default::default();
|
let handlers: Arc<Handlers<T>> = Default::default();
|
||||||
|
@ -139,15 +169,15 @@ mod id_map {
|
||||||
/// Low-level event handling function, returns the event ID for use with get_post_targets.
|
/// Low-level event handling function, returns the event ID for use with get_post_targets.
|
||||||
///
|
///
|
||||||
/// This function is (extremely) slow!
|
/// This function is (extremely) slow!
|
||||||
pub fn get_event_id<T: Event>() -> usize {
|
pub fn get_event_id<T: Event + ?Sized>() -> usize {
|
||||||
id_map::get_event_id::<T>()
|
id_map::get_event_id::<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
type RwArcVec<T> = RwLock<Arc<Vec<T>>>;
|
type RwArcVec<T> = RwLock<Arc<Vec<T>>>;
|
||||||
|
|
||||||
struct Handlers<T: Event>(RwArcVec<RwArcVec<(i32, Arc<dyn Fn(&mut T) + Send + Sync>)>>);
|
struct Handlers<T: Event + ?Sized>(RwArcVec<RwArcVec<(i32, Arc<dyn Fn(&mut T) + Send + Sync>)>>);
|
||||||
|
|
||||||
impl<T: Event> Default for Handlers<T> {
|
impl<T: Event + ?Sized> Default for Handlers<T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Handlers(Default::default())
|
Handlers(Default::default())
|
||||||
}
|
}
|
||||||
|
@ -165,7 +195,7 @@ lazy_static! {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `id` doesn't match the given `T` (but not if the given `id` doesn't exist).
|
/// Panics if `id` doesn't match the given `T` (but not if the given `id` doesn't exist).
|
||||||
pub fn get_post_targets<T: Event>(bus: &EventBus, event: &mut T, id: usize) -> Arc<Vec<(i32, Arc<dyn Fn(&mut T) + Send + Sync>)>> {
|
pub fn get_post_targets<T: Event + ?Sized>(bus: &EventBus, _event: &mut T, id: usize) -> Arc<Vec<(i32, Arc<dyn Fn(&mut T) + Send + Sync>)>> {
|
||||||
let target: Option<Arc<Vec<_>>> = HANDLERS.read().expect("failed to lock for reading").get(id).map(|arc| Arc::clone(arc))
|
let target: Option<Arc<Vec<_>>> = HANDLERS.read().expect("failed to lock for reading").get(id).map(|arc| Arc::clone(arc))
|
||||||
.map(|any| Arc::downcast::<Handlers<T>>(any).expect("Wrong id"))
|
.map(|any| Arc::downcast::<Handlers<T>>(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"))));
|
.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<T: Event>(bus: &EventBus, event: &mut T, id: usize) -> A
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `id` doesn't match the given `T`, or if `id` doesn't exist.
|
/// Panics if `id` doesn't match the given `T`, or if `id` doesn't exist.
|
||||||
pub fn register<T: Event, F: for<'a> Fn(&'a mut T) + Send + Sync + 'static>(bus: &EventBus, priority: i32, handler: F, id: usize) {
|
pub fn register<T: Event + ?Sized, F: for<'a> 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))
|
HANDLERS.read().expect("failed to lock for reading").get(id).map(|arc| Arc::clone(arc))
|
||||||
.map(|any| Arc::downcast::<Handlers<T>>(any).expect("Wrong id"))
|
.map(|any| Arc::downcast::<Handlers<T>>(any).expect("Wrong id"))
|
||||||
.map(|handlers| {
|
.map(|handlers| {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
These are currently not being automatically tested because compiletest_rs doesn't work on stable.
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue