2018-10-24 17:12:24 +02:00
/*
* 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 < https ://www.gnu.org/licenses/>.
* /
/// 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 ) = > {
{
2018-10-27 19:56:02 +02:00
// 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 < 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 )
}
2018-10-24 17:12:24 +02:00
}
}
}
/// Posts an event.
///
2018-10-27 19:56:02 +02:00
/// Usage: `post_event!(bus, type, event)`
2018-10-24 17:12:24 +02:00
#[ macro_export ]
macro_rules ! post_event {
2018-10-27 19:56:02 +02:00
// TODO allow multiple $t:ty at once and re-#[doc(hidden)] the low-level get_post_targets and
// get_event_id
2018-10-29 03:43:48 +01:00
( $b :expr , $e :expr , $( $t :ty ) , + ) = > {
2018-10-24 17:12:24 +02:00
{
2018-10-27 19:56:02 +02:00
// hygiene
let bus : & $crate ::EventBus = $b ;
// event setup, this may evaluate to any type.
let event : & mut _ = $e ;
{
2018-10-29 03:43:48 +01:00
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 ;
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 ( ) {
fun ( event ) ;
}
} , ) + ) ; // big tuple of (Handlers<type>, Handlers<type>, Handlers<type>, Handlers<type>, ...)
2018-10-29 03:32:00 +01:00
}
}
}
}
/// 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
2018-10-29 03:43:48 +01:00
( $b :expr , $e :expr , $( $t :ty ) , + ) = > {
2018-10-29 03:32:00 +01:00
{
// hygiene
let bus : & $crate ::EventBus = $b ;
// event setup, this may evaluate to any type.
let event : & mut _ = $e ;
{
fn test < C : Cancellable > ( e : & C ) {
}
test ( event ) ;
2018-10-29 03:55:37 +01:00
let cancelled = [ $( {
2018-10-29 03:43:48 +01:00
// 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 ) ;
2018-10-29 03:32:00 +01:00
2018-10-29 03:43:48 +01:00
for ( _pri , fun ) in handlers . iter ( ) {
if Cancellable ::cancelled ( event ) {
break ;
}
2018-10-29 03:55:37 +01:00
fun ( event as & mut $t ) ;
2018-10-27 19:56:02 +02:00
}
2018-10-29 03:43:48 +01:00
event . cancelled ( )
2018-10-29 03:55:37 +01:00
} , ) + ] ; // big tuple of (Handlers<type>, Handlers<type>, Handlers<type>, Handlers<type>, ...)
2018-10-29 03:43:48 +01:00
2018-10-29 03:55:37 +01:00
cancelled . iter ( ) . fold ( false , | n , i | n | | * i )
2018-10-24 17:12:24 +02:00
}
}
}
}
/// 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.
2018-10-27 19:56:02 +02:00
///
/// 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 {
2018-10-24 17:12:24 +02:00
false
}
2018-10-29 03:32:00 +01:00
}
/// Basic trait for defining an event.
pub trait Cancellable : 'static {
2018-10-24 17:12:24 +02:00
/// 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 ) {
2018-10-27 19:56:02 +02:00
let _ = cancel ;
2018-10-24 17:12:24 +02:00
panic! ( " not cancellable " ) ;
}
}
/// An event bus.
pub struct EventBus {
id : usize ,
dropper : Mutex < LinkedList < Box < dyn Fn ( usize ) + Send + Sync > > > ,
}
#[ 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 ( ) ) ;
}
2018-10-27 19:56:02 +02:00
struct EventId < T : Event + ? Sized > {
2018-10-24 17:12:24 +02:00
id : usize ,
_t : PhantomData < dyn Fn ( & mut T ) + Send + Sync >
}
2018-10-27 19:56:02 +02:00
pub fn get_event_id < T : Event + ? Sized > ( ) -> usize {
2018-10-24 17:12:24 +02:00
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 handlers : Arc < Handlers < T > > = 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!
2018-10-27 19:56:02 +02:00
pub fn get_event_id < T : Event + ? Sized > ( ) -> usize {
2018-10-24 17:12:24 +02:00
id_map ::get_event_id ::< T > ( )
}
type RwArcVec < T > = RwLock < Arc < Vec < T > > > ;
2018-10-27 19:56:02 +02:00
struct Handlers < T : Event + ? Sized > ( RwArcVec < RwArcVec < ( i32 , Arc < dyn Fn ( & mut T ) + Send + Sync > ) > > ) ;
2018-10-24 17:12:24 +02:00
2018-10-27 19:56:02 +02:00
impl < T : Event + ? Sized > Default for Handlers < T > {
2018-10-24 17:12:24 +02:00
fn default ( ) -> Self {
Handlers ( Default ::default ( ) )
}
}
lazy_static! {
static ref HANDLERS : RwArcVec < Arc < dyn Any + Send + Sync > > = 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).
2018-10-27 19:56:02 +02:00
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 > ) > > {
2018-10-24 17:12:24 +02:00
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 " ) )
. 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.
2018-10-27 19:56:02 +02:00
pub fn register < T : Event + ? Sized , F : for < ' a > Fn ( & ' a mut T ) + Send + Sync + 'static > ( bus : & EventBus , priority : i32 , handler : F , id : usize ) {
2018-10-24 17:12:24 +02:00
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 ( | 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 < dyn Fn ( & mut T ) + Send + Sync + 'static > ) = ( 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 < LinkedList < usize > > = 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
}
}