/* * Event Bus * Copyright (C) 2018 Soni L. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ /// Registers an event hook. /// /// Usage: `register_hook!(bus, priority, type, handler)` #[macro_export] 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) } } } /// Posts an event. /// /// Usage: `post!(bus, type, event)` #[macro_export] macro_rules! post_event { ($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>::cancellable() && event.cancelled() { break; } } <$t>::cancellable() && event.cancelled() } } } /// Basic trait for defining an event. pub trait Event: 'static { /// Returns whether this event is cancellable. /// /// When this is true, `cancelled` and `cancel` should also be implemented. fn cancellable() -> bool { false } /// Returns whether this event has been cancelled. fn cancelled(&self) -> bool { false } /// Sets whether this event is cancelled. /// /// # Panics /// /// Panics if this event is not cancellable. fn set_cancelled(&mut self, cancel: bool) { panic!("not cancellable"); } } /// An event bus. pub struct EventBus { id: usize, dropper: Mutex>>, } #[macro_use] extern crate lazy_static; extern crate anymap; //use std::marker::PhantomData; use std::sync::atomic::AtomicUsize; use std::sync::atomic::ATOMIC_USIZE_INIT; use std::sync::atomic::Ordering as AtomicOrdering; use std::sync::Mutex; use std::collections::LinkedList; use std::any::Any; use std::sync::Arc; use std::sync::RwLock; use std::mem; /* only exists as a module to keep us from accidentally misusing these */ mod id_map { use std::marker::PhantomData; use std::sync::atomic::AtomicUsize; use std::sync::atomic::ATOMIC_USIZE_INIT; use std::sync::atomic::Ordering as AtomicOrdering; use std::sync::Mutex; use std::sync::Arc; use super::Event; use super::Handlers; lazy_static! { static ref EVENT_ID_MAP: Mutex<::anymap::Map<::anymap::any::Any + Send + Sync>> = Mutex::new(::anymap::Map::new()); } struct EventId { id: usize, _t: PhantomData } 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(); Arc::make_mut(&mut super::HANDLERS.write().expect("???")).push(handlers); id } } /// 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 { id_map::get_event_id::() } type RwArcVec = RwLock>>; struct Handlers(RwArcVec)>>); impl Default for Handlers { fn default() -> Self { Handlers(Default::default()) } } lazy_static! { static ref HANDLERS: RwArcVec> = Default::default(); } /// Low-level event handling function, returns the handlers that should be called to post the /// event. /// /// This is useful if you want to simulate inheritance. /// /// # 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)>> { 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")))); target.unwrap_or_else(|| Arc::new(Vec::new())) } /// Low-level event handling function, registers an event. /// /// This function is kinda slow. It doesn't block currently executing hooks (and, in fact, may be /// safely called from a running hook), but does block threads from starting hook execution. /// /// # 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) { 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| { let mut lock = handlers.0.read().expect("failed to lock for reading"); if lock.len() <= id { mem::drop(lock); let mut wlock = handlers.0.write().expect("failed to lock for writing"); if wlock.len() <= id { // another thread may have touched this let vec = Arc::get_mut(&mut wlock).expect("failed to mutate busses"); let count = id - vec.len() + 1; vec.reserve_exact(count); for _ in 0..count { vec.push(Default::default()); } } // there's no way to downgrade a write lock, but as long as we never shrink the // vecs it should be fine. mem::drop(wlock); lock = handlers.0.read().expect("failed to lock for reading"); } let option = lock.get(bus.id); // last RwLock let hooks = option.expect("failed to make vecs"); let hook: (i32, Arc) = (priority, Arc::new(handler)); let mut wlock = hooks.write().expect("failed to lock for writing"); let vec = Arc::make_mut(&mut wlock); // would be nice if Result had a .coalesce() let pos = match vec.binary_search_by(|probe| probe.0.cmp(&priority)) { Ok(p) => p, Err(p) => p }; vec.insert(pos, hook); }).expect("No such id"); } static BUS_ID: AtomicUsize = ATOMIC_USIZE_INIT; lazy_static! { // this is the closest to high-performance we can get with std // would be nicer if this didn't lock the whole list static ref FREED_BUS_IDS: Mutex> = Mutex::new(LinkedList::new()); } impl EventBus { /// Makes a new EventBus. pub fn new() -> EventBus { EventBus { id: FREED_BUS_IDS.lock().unwrap().pop_front().unwrap_or_else(|| BUS_ID.fetch_add(1, AtomicOrdering::SeqCst)), dropper: Default::default(), } } } impl Drop for EventBus { fn drop(&mut self) { // TODO } }