Compare commits

..

3 commits

Author SHA1 Message Date
Daniel Griffen
fedb32d242
collate cancelled 2018-10-28 19:55:37 -07:00
Daniel Griffen
3c3e8754ad
add multi-parenting 2018-10-28 19:43:48 -07:00
Daniel Griffen
5b7b4f1e12
make cancellation a type property 2018-10-28 19:32:00 -07:00
11 changed files with 102 additions and 232 deletions

View file

@ -1,6 +1,6 @@
[package]
name = "eventbus"
version = "0.5.1"
version = "0.4.0"
authors = ["SoniEx2 <endermoneymod@gmail.com>"]
description = "Safe, fast and concurrent event system, inspired by the MinecraftForge event bus."
keywords = ["event", "safe", "fast", "concurrent", "bus"]
@ -10,14 +10,10 @@ repository = "https://cybre.tech/SoniEx2/rust.eventbus"
[dependencies]
lazy_static = "1.1.0"
anymap = "0.12.1"
itertools = "0.7.11"
[dev-dependencies]
criterion = "0.2.5"
[workspace]
members = ["compiletest"]
[[bench]]
name = "post_single_benchmark"
harness = false

View file

@ -1,2 +0,0 @@
/target
Cargo.lock

View file

@ -1,9 +0,0 @@
[package]
name = "eventbus-compiletest"
version = "0.0.0"
authors = ["SoniEx2 <endermoneymod@gmail.com>"]
publish = false
[dependencies]
compiletest_rs = { version = "0.3", features = ["stable"] }
eventbus = { path = ".." }

View file

@ -1,3 +0,0 @@
$ cargo test
boilerplate from: https://github.com/dtolnay/ref-cast/tree/ea05783b5754fc01baf671f97edf120a3f4afc7f/compiletest

View file

@ -1,17 +0,0 @@
#[macro_use]
extern crate eventbus;
use eventbus::{Event, EventBus};
struct MyEvent {
i: i32
}
impl Event for MyEvent {
}
fn test_hygiene() {
let bus = [EventBus::new()];
let mut event = MyEvent { i: 3 };
post_event!(&bus[CANCELLABLE.load(::std::sync::atomic::Ordering::Relaxed)], &mut event, MyEvent); //~ ERROR cannot find value `CANCELLABLE` in this scope
}

View file

@ -1,23 +0,0 @@
#![cfg(test)]
extern crate compiletest_rs as compiletest;
use std::env;
fn run_mode(mode: &'static str) {
let mut config = compiletest::Config::default();
config.mode = mode.parse().expect("invalid mode");
config.target_rustcflags = Some("-L ../target/debug/deps".to_owned());
if let Ok(name) = env::var("TESTNAME") {
config.filter = Some(name);
}
config.src_base = mode.into();
compiletest::run_tests(&config);
}
#[test]
fn compile_fail() {
run_mode("compile-fail");
}

View file

@ -31,7 +31,7 @@ macro_rules! register_hook {
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;
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);
});
@ -44,10 +44,11 @@ macro_rules! register_hook {
/// Posts an event.
///
/// Usage: `post_event!(bus, event, type, type, ...)`
/// Usage: `post_event!(bus, type, event)`
#[macro_export]
macro_rules! post_event {
// prefer (bus, ty, ty, ty, ..., evt) but rust is just bad at parsing
// TODO allow multiple $t:ty at once and re-#[doc(hidden)] the low-level get_post_targets and
// get_event_id
($b:expr, $e:expr, $($t:ty),+) => {
{
// hygiene
@ -55,46 +56,69 @@ macro_rules! post_event {
// 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);
#[allow(unused_mut)]
let mut event_handlers = ::std::iter::empty::<(i32, Box<dyn Fn(&mut _)>)>();
$(
let handlers;
#[allow(unused_mut)]
let mut event_handlers = {
let _ = ($({
// 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;
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);
let handlers = $crate::get_post_targets::<$t>(bus, event, id);
// handler retrieval and invokation
{
let subevent: &mut $t = event;
handlers = $crate::get_post_targets::<$t>(bus, subevent, id);
for (_pri, fun) in handlers.iter() {
fun(event);
}
$crate::type_hint_trick(event, event_handlers, handlers.iter().cloned(), |f| move |evt| f(evt))
};
)+
},)+); // big tuple of (Handlers<type>, Handlers<type>, Handlers<type>, Handlers<type>, ...)
}
}
}
}
/// Posts an event.
///
/// Usage: `post_event!(bus, type, event)`
#[macro_export]
macro_rules! post_event_cancellable {
// TODO allow multiple $t:ty at once and re-#[doc(hidden)] the low-level get_post_targets and
// get_event_id
($b:expr, $e:expr, $($t:ty),+) => {
{
// hygiene
let bus: &$crate::EventBus = $b;
// event setup, this may evaluate to any type.
let event: &mut _ = $e;
{
fn test<C: Cancellable>(e: &C) {
for (_pri, fun) in event_handlers {
fun(event);
if cancellable && <_ as $crate::Event>::cancelled(event) {
break;
}
}
cancellable && <_ as $crate::Event>::cancelled(event)
test(event);
let cancelled = [$({
// 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);
let handlers = $crate::get_post_targets::<$t>(bus, event, id);
for (_pri, fun) in handlers.iter() {
if Cancellable::cancelled(event) {
break;
}
fun(event as &mut $t);
}
event.cancelled()
},)+]; // big tuple of (Handlers<type>, Handlers<type>, Handlers<type>, Handlers<type>, ...)
cancelled.iter().fold(false, |n, i| n || *i)
}
}
}
@ -112,6 +136,10 @@ pub trait Event: 'static {
fn cancellable(&self) -> bool {
false
}
}
/// Basic trait for defining an event.
pub trait Cancellable: 'static {
/// Returns whether this event has been cancelled.
fn cancelled(&self) -> bool {
@ -135,25 +163,9 @@ pub struct EventBus {
dropper: Mutex<LinkedList<Box<dyn Fn(usize) + Send + Sync>>>,
}
#[doc(hidden)]
pub fn type_hint_trick<T: Event + Sized, A, B, C: Event + ?Sized, E>(_event: &mut T, event_handlers: A, handlers: B, convert: fn(::std::sync::Arc<dyn Fn(&mut C) + Send + Sync>) -> E) -> impl ::std::iter::Iterator<Item=(i32, Box<dyn for<'a> Fn(&'a mut T) + 'static>)> where
A: ::std::iter::Iterator<Item=(i32, Box<dyn Fn(&mut T)>)>,
B: ::std::iter::Iterator<Item=(i32, ::std::sync::Arc<dyn Fn(&mut C) + Send + Sync>)>,
E: Fn(&mut T) + 'static,
{
itertools::merge_join_by(event_handlers, handlers, |l, r| l.0.cmp(&r.0))
.map(move |eob: itertools::EitherOrBoth<(i32, Box<dyn Fn(&mut T)>), (i32, ::std::sync::Arc<dyn Fn(&mut C) + Send + Sync>)>| ({
eob.as_ref().left().map(|v| v.0).or_else(|| eob.as_ref().right().map(|v| v.0)).unwrap_or(0)
}, Box::new(move |evt: &mut _| {
eob.as_ref().left().map(|x| (x.1)(evt));
eob.as_ref().right().map(|x| convert(x.1.clone())(evt));
}) as Box<_>))
}
#[macro_use]
extern crate lazy_static;
extern crate anymap;
extern crate itertools;
//use std::marker::PhantomData;
use std::sync::atomic::AtomicUsize;
@ -186,11 +198,10 @@ mod id_map {
pub fn get_event_id<T: Event + ?Sized>() -> usize {
static EVENT_ID_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
EVENT_ID_MAP.lock().expect("failed to allocate event id").entry::<EventId<T>>().or_insert_with(|| {
let handlers: Arc<Handlers<T>> = Default::default();
Arc::make_mut(&mut super::HANDLERS.write().expect("???")).push(handlers);
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();
Arc::make_mut(&mut super::HANDLERS.write().expect("???")).push(handlers);
id
}
}
@ -294,50 +305,3 @@ impl Drop for EventBus {
// TODO
}
}
// test with good error messages
#[cfg(test)]
mod useful_test {
use super::{Event, EventBus};
struct MyEvent {
i: i32
}
trait DynEvent : Event {
}
impl DynEvent for MyEvent {
}
impl Event for MyEvent {
}
fn add_handler(e: &mut MyEvent) {
/* adds 1 */
e.i += 1;
}
fn no_handler(e: &mut MyEvent) {
/* does nothing */
e.i += 0;
}
#[test]
fn test_usage_internal() {
let event_bus = EventBus::new();
//let handler_id = event_bus.register(add_handler, 0);
register_hook!(&event_bus, 0, MyEvent, add_handler);
let mut event = MyEvent { i: 3 };
assert_eq!(event.i, 3);
post_event!(&event_bus, &mut event, MyEvent);
post_event!(&event_bus, &mut event, dyn DynEvent);
assert_eq!(event.i, 4);
register_hook!(&event_bus, 1, MyEvent, no_handler);
post_event!(&event_bus, &mut event, MyEvent);
assert_eq!(event.i, 5);
//event_bus.unregister(handler_id);
post_event!(&event_bus, &mut event, MyEvent);
//assert_eq!(event.i, 5);
}
}

View file

@ -0,0 +1 @@
These are currently not being automatically tested because compiletest_rs doesn't work on stable.

View file

@ -10,8 +10,9 @@ struct MyEvent {
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)], &mut event, MyEvent); //~ ERROR cannot find value `EVENT_ID` in this scope
post_event!(&bus[EVENT_ID.load(::std::sync::atomic::Ordering::Relaxed)], MyEvent, &mut event);
}

View file

@ -1,53 +1,60 @@
#[macro_use]
extern crate eventbus;
use eventbus::{Event, EventBus};
use std::fmt::Debug;
use eventbus::{Event, EventBus, Cancellable};
trait MyEvent : Event {
fn get_i(&self) -> i32;
fn set_i(&mut self, i32);
trait MyEvent : Event + Debug {
}
trait MyExtraEvent : MyEvent + Cancellable {
}
#[derive(Debug)]
struct MyEventImpl {
i: i32
}
#[derive(Debug)]
struct MyExtraEventImpl {
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);
impl Event for MyExtraEventImpl {
}
fn no_handler(e: &mut MyEvent) {
/* does nothing */
let i = e.get_i();
e.set_i(i);
impl MyEvent for MyExtraEventImpl {
}
impl MyExtraEvent for MyExtraEventImpl {
}
impl Cancellable for MyExtraEventImpl {
}
fn on_myevent(evt: &mut dyn MyEvent) {
println!("Got event for inspection and manipulation: {:?}", evt);
}
fn on_myextraevent(evt: &mut dyn MyExtraEvent) {
println!("Got event for inspection, manipulation, and cancellation: {:?}", evt);
}
#[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);
let mut extraEvent = MyExtraEventImpl { i: 4 };
//let handler_id = event_bus.register(add_handler, 0);
register_hook!(&event_bus, 0, dyn MyEvent, on_myevent);
register_hook!(&event_bus, 1, dyn MyExtraEvent, on_myextraevent);
post_event!(&event_bus, &mut event, dyn MyEvent);
assert_eq!(event.i, 4);
register_hook!(&event_bus, 1, dyn MyEvent, no_handler);
post_event!(&event_bus, &mut event, dyn MyEvent);
assert_eq!(event.i, 5);
//event_bus.unregister(handler_id);
post_event!(&event_bus, &mut event, dyn MyEvent);
//assert_eq!(event.i, 5);
post_event_cancellable!(&event_bus, &mut extraEvent, dyn MyExtraEvent, dyn MyEvent);
}

View file

@ -1,45 +0,0 @@
#[macro_use]
extern crate eventbus;
use eventbus::{Event, EventBus};
struct MyEvent {
i: i32
}
trait DynEvent : Event {
}
impl DynEvent for MyEvent {
}
impl Event for MyEvent {
}
fn add_handler(e: &mut MyEvent) {
/* adds 1 */
e.i += 1;
}
fn no_handler(e: &mut MyEvent) {
/* does nothing */
e.i += 0;
}
#[test]
fn test_usage_external() {
let event_bus = EventBus::new();
//let handler_id = event_bus.register(add_handler, 0);
register_hook!(&event_bus, 0, MyEvent, add_handler);
let mut event = MyEvent { i: 3 };
assert_eq!(event.i, 3);
post_event!(&event_bus, &mut event, MyEvent);
post_event!(&event_bus, &mut event, dyn DynEvent);
assert_eq!(event.i, 4);
register_hook!(&event_bus, 1, MyEvent, no_handler);
post_event!(&event_bus, &mut event, MyEvent);
assert_eq!(event.i, 5);
//event_bus.unregister(handler_id);
post_event!(&event_bus, &mut event, MyEvent);
//assert_eq!(event.i, 5);
}