From d21420b25724550ec8990bf9a9ed84708c2bd12c Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Sun, 19 May 2024 02:40:10 -0700 Subject: [PATCH] Expose bump allocator in `Sender` (#65) This PR removes the `'static` requirement from `Sender::send` and `Sender::send_to` and exposes the bump allocator by providing allocation methods on `Sender`. `Sender` is now entirely internally mutable (takes `&self` on all the methods). This partially addresses #44 but is missing a few things. - Doesn't allow received events with borrowed data to pass though `Sender::send` without a clone because `Receiver` and `Sender` have different lifetimes. Might still need a combined `Sender` and `Receiver` type. - Can't use the bump allocator from a `&mut World`. - No optional `collections` module. I think it would be easier to just add an unstable feature for the unstable allocator api for those who need it. --- CHANGELOG.md | 6 ++ benches/event.rs | 12 +-- src/entity.rs | 4 +- src/event.rs | 266 +++++++++++++++++++++++++++++++---------------- src/handler.rs | 7 -- src/world.rs | 156 ++++++++++++++++++--------- tutorial.md | 8 +- 7 files changed, 297 insertions(+), 162 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f62de00..dfe13f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## Unreleased + +- `Sender` now has allocation methods and can send events with data borrowed from the allocator. +- `Sender` is now entirely internally mutable and all methods take `&self`. +- Changed API of `UnsafeWorldCell`. + ## 0.6.0 - 2024-05-18 - Removed `Send + Sync` requirements from all data in the `World`, including components, handlers, and events. `World` is now `!Send + !Sync`. diff --git a/benches/event.rs b/benches/event.rs index 7b8f1c6..dfbc5b5 100644 --- a/benches/event.rs +++ b/benches/event.rs @@ -27,7 +27,7 @@ fn send_many_global_events(bencher: Bencher) { world.add_handler(get_b_send_c); world.add_handler(get_c_send_d); - fn get_a_send_b(r: Receiver, mut s: Sender) { + fn get_a_send_b(r: Receiver, s: Sender) { s.send(B(r.event.0)); s.send(B(r.event.0)); s.send(B(r.event.0)); @@ -35,7 +35,7 @@ fn send_many_global_events(bencher: Bencher) { s.send(B(r.event.0)); } - fn get_b_send_c(r: Receiver, mut s: Sender) { + fn get_b_send_c(r: Receiver, s: Sender) { s.send(C(r.event.0)); s.send(C(r.event.0)); s.send(C(r.event.0)); @@ -43,7 +43,7 @@ fn send_many_global_events(bencher: Bencher) { s.send(C(r.event.0)); } - fn get_c_send_d(r: Receiver, mut s: Sender) { + fn get_c_send_d(r: Receiver, s: Sender) { s.send(D(r.event.0)); s.send(D(r.event.0)); s.send(D(r.event.0)); @@ -83,7 +83,7 @@ fn send_many_targeted_events(bencher: Bencher) { world.add_handler(get_b_send_c); world.add_handler(get_c_send_d); - fn get_a_send_b(r: Receiver, mut s: Sender) { + fn get_a_send_b(r: Receiver, s: Sender) { s.send_to(r.query.0, B(r.query.1 .0)); s.send_to(r.query.0, B(r.query.1 .0)); s.send_to(r.query.0, B(r.query.1 .0)); @@ -91,7 +91,7 @@ fn send_many_targeted_events(bencher: Bencher) { s.send_to(r.query.0, B(r.query.1 .0)); } - fn get_b_send_c(r: Receiver, mut s: Sender) { + fn get_b_send_c(r: Receiver, s: Sender) { s.send_to(r.query.0, C(r.query.1 .0)); s.send_to(r.query.0, C(r.query.1 .0)); s.send_to(r.query.0, C(r.query.1 .0)); @@ -99,7 +99,7 @@ fn send_many_targeted_events(bencher: Bencher) { s.send_to(r.query.0, C(r.query.1 .0)); } - fn get_c_send_d(r: Receiver, mut s: Sender) { + fn get_c_send_d(r: Receiver, s: Sender) { s.send_to(r.query.0, D(r.query.1 .0)); s.send_to(r.query.0, D(r.query.1 .0)); s.send_to(r.query.0, D(r.query.1 .0)); diff --git a/src/entity.rs b/src/entity.rs index fa81624..4290db2 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -267,14 +267,14 @@ mod tests { b: EntityId, } - world.add_handler(|_: Receiver, mut s: Sender<(Despawn, E2)>| { + world.add_handler(|_: Receiver, s: Sender<(Despawn, E2)>| { let a = s.spawn(); let b = s.spawn(); s.despawn(b); s.send(E2 { a, b }); }); - world.add_handler(|r: Receiver, mut s: Sender<()>| { + world.add_handler(|r: Receiver, s: Sender<()>| { let c = s.spawn(); assert_ne!(r.event.a, c); assert_ne!(r.event.b, c); diff --git a/src/event.rs b/src/event.rs index 912f9b1..475b7a9 100644 --- a/src/event.rs +++ b/src/event.rs @@ -4,17 +4,13 @@ mod global; mod targeted; use alloc::borrow::Cow; -#[cfg(not(feature = "std"))] -use alloc::{vec, vec::Vec}; use core::alloc::Layout; use core::any::TypeId; use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; -use core::panic::{RefUnwindSafe, UnwindSafe}; -use core::ptr::NonNull; -use core::{any, fmt}; +use core::ptr::{self, NonNull}; +use core::{any, fmt, slice, str}; -use bumpalo::Bump; use evenio_macros::all_tuples; pub use global::*; pub use targeted::*; @@ -194,76 +190,6 @@ mod event_idx_marker { impl Sealed for TargetedEventIdx {} } -#[derive(Debug)] -pub(crate) struct EventQueue { - items: Vec, - bump: Bump, -} - -impl EventQueue { - pub(crate) fn new() -> Self { - Self { - items: vec![], - bump: Bump::new(), - } - } - - pub(crate) fn pop_front(&mut self) -> Option { - self.items.pop() - } - - #[inline] - pub(crate) unsafe fn push_front_global(&mut self, event: E, idx: GlobalEventIdx) { - let meta = EventMeta::Global { idx }; - let event = NonNull::from(self.bump.alloc(event)).cast::(); - self.items.push(EventQueueItem { meta, event }); - } - - #[inline] - pub(crate) unsafe fn push_front_targeted( - &mut self, - target: EntityId, - event: E, - idx: TargetedEventIdx, - ) { - let meta = EventMeta::Targeted { idx, target }; - let event = NonNull::from(self.bump.alloc(event)).cast::(); - self.items.push(EventQueueItem { meta, event }); - } - - /// Reverses elements in the range `from..`. - /// - /// # Safety - /// - /// `from` must be in bounds. - pub(crate) unsafe fn reverse_from(&mut self, from: usize) { - self.items.get_unchecked_mut(from..).reverse(); - } - - pub(crate) fn iter(&self) -> impl Iterator { - self.items.iter() - } - - /// Clears the event queue and resets the internal bump allocator. - /// - /// Any remaining event pointers are invalidated. - pub(crate) fn clear(&mut self) { - self.items.clear(); - self.bump.reset(); - } - - pub(crate) fn len(&self) -> usize { - self.items.len() - } - - pub(crate) fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -impl UnwindSafe for EventQueue {} -impl RefUnwindSafe for EventQueue {} - #[derive(Clone, Copy, Debug)] pub(crate) struct EventQueueItem { pub(crate) meta: EventMeta, @@ -709,7 +635,7 @@ pub struct Sender<'a, T: EventSet> { world: UnsafeWorldCell<'a>, } -impl Sender<'_, T> { +impl<'a, ES: EventSet> Sender<'a, ES> { /// Add a [`GlobalEvent`] to the queue of events to send. /// /// The queue is flushed once all handlers for the current event have run. @@ -718,37 +644,45 @@ impl Sender<'_, T> { /// /// - Panics if `E` is not in the [`EventSet`] of this sender. #[track_caller] - pub fn send(&mut self, event: E) { + pub fn send(&self, event: E) { // The event type and event set are all compile time known, so the compiler // should be able to optimize this away. - let event_idx = T::find_index::(self.state).unwrap_or_else(|| { + let event_idx = ES::find_index::(self.state).unwrap_or_else(|| { panic!( "global event `{}` is not in the `EventSet` of this `Sender`", any::type_name::() ) }); - unsafe { self.world.send_global(event, GlobalEventIdx(event_idx)) } + let ptr = self.alloc_layout(Layout::new::()); + + unsafe { ptr::write::(ptr.as_ptr().cast(), event) }; + + unsafe { self.world.queue_global(ptr, GlobalEventIdx(event_idx)) }; } /// Add a [`TargetedEvent`] to the queue of events to send. /// /// The queue is flushed once all handlers for the current event have run. #[track_caller] - pub fn send_to(&mut self, target: EntityId, event: E) { + pub fn send_to(&self, target: EntityId, event: E) { // The event type and event set are all compile time known, so the compiler // should be able to optimize this away. - let event_idx = T::find_index::(self.state).unwrap_or_else(|| { + let event_idx = ES::find_index::(self.state).unwrap_or_else(|| { panic!( "targeted event `{}` is not in the `EventSet` of this `Sender`", any::type_name::() ) }); + let ptr = self.alloc_layout(Layout::new::()); + + unsafe { ptr::write::(ptr.as_ptr().cast(), event) }; + unsafe { self.world - .send_targeted(target, event, TargetedEventIdx(event_idx)) - } + .queue_targeted(target, ptr, TargetedEventIdx(event_idx)) + }; } /// Queue the creation of a new entity. @@ -764,7 +698,7 @@ impl Sender<'_, T> { /// /// Panics if `Spawn` is not in the [`EventSet`] of this sender. #[track_caller] - pub fn spawn(&mut self) -> EntityId { + pub fn spawn(&self) -> EntityId { let id = unsafe { self.world.queue_spawn() }; self.send(Spawn(id)); id @@ -786,7 +720,7 @@ impl Sender<'_, T> { /// /// Panics if `Insert` is not in the [`EventSet`] of this sender. #[track_caller] - pub fn insert(&mut self, target: EntityId, component: C) { + pub fn insert(&self, target: EntityId, component: C) { self.send_to(target, Insert(component)) } @@ -806,7 +740,7 @@ impl Sender<'_, T> { /// /// Panics if `Remove` is not in the [`EventSet`] of this sender. #[track_caller] - pub fn remove(&mut self, target: EntityId) { + pub fn remove(&self, target: EntityId) { self.send_to(target, Remove::) } @@ -825,9 +759,68 @@ impl Sender<'_, T> { /// /// Panics if `Despawn` is not in the [`EventSet`] of this sender. #[track_caller] - pub fn despawn(&mut self, target: EntityId) { + pub fn despawn(&self, target: EntityId) { self.send_to(target, Despawn) } + + /// Allocate an object into the bump allocator and return an exclusive + /// reference to it. + #[inline] + pub fn alloc(&self, value: T) -> &'a mut T { + let ptr = self.alloc_layout(Layout::new::()).cast::().as_ptr(); + + unsafe { ptr::write(ptr, value) }; + + unsafe { &mut *ptr } + } + + /// Allocate a slice into the bump allocator and return an exclusive + /// reference to it. + /// + /// The elements of the slice are initialized using the supplied closure. + /// The closure argument is the position in the slice. + #[inline] + pub fn alloc_slice(&self, len: usize, mut f: F) -> &'a mut [T] + where + F: FnMut(usize) -> T, + { + let layout = Layout::array::(len).expect("invalid slice length"); + let dst = self.alloc_layout(layout).cast::(); + + unsafe { + for i in 0..len { + ptr::write(dst.as_ptr().add(i), f(i)); + } + + let result = slice::from_raw_parts_mut(dst.as_ptr(), len); + debug_assert_eq!(Layout::for_value(result), layout); + result + } + } + + /// Copies the given string into the bump allocator and returns an exclusive + /// reference to it. + #[inline] + pub fn alloc_str(&self, str: &str) -> &'a mut str { + unsafe { + let ptr = self + .alloc_layout(Layout::from_size_align_unchecked(str.len(), 1)) + .as_ptr(); + + ptr::copy_nonoverlapping(str.as_ptr(), ptr, str.len()); + let slice = slice::from_raw_parts_mut(ptr, str.len()); + str::from_utf8_unchecked_mut(slice) + } + } + + /// Allocate space for an object in the bump allocator with the given + /// [`Layout`]. + /// + /// The returned pointer points to uninitialized memory. + #[inline] + pub fn alloc_layout(&self, layout: Layout) -> NonNull { + unsafe { self.world.alloc_layout(layout) } + } } unsafe impl HandlerParam for Sender<'_, T> { @@ -1152,7 +1145,7 @@ mod tests { #[derive(Component)] struct C(String); - world.add_handler(|r: Receiver, mut s: Sender>| { + world.add_handler(|r: Receiver, s: Sender>| { s.remove::(r.query); }); @@ -1178,12 +1171,12 @@ mod tests { #[derive(Component)] struct Result(Vec); - fn get_a_send_b(_: Receiver, mut sender: Sender) { + fn get_a_send_b(_: Receiver, sender: Sender) { sender.send(B(0)); sender.send(B(3)); } - fn get_b_send_c(r: Receiver, mut sender: Sender, res: Single<&mut Result>) { + fn get_b_send_c(r: Receiver, sender: Sender, res: Single<&mut Result>) { res.0 .0.push(r.event.0); sender.send(C(r.event.0 + 1)); sender.send(C(r.event.0 + 2)); @@ -1232,7 +1225,7 @@ mod tests { entities.shuffle(&mut rand::thread_rng()); - world.add_handler(move |_: Receiver, mut s: Sender| { + world.add_handler(move |_: Receiver, s: Sender| { for &e in &entities { s.despawn(e); } @@ -1265,4 +1258,93 @@ mod tests { world.send(A(&mut buf)); } + + #[test] + #[should_panic] + fn global_event_not_in_event_set() { + let mut world = World::new(); + + #[derive(GlobalEvent)] + struct A; + + #[derive(GlobalEvent)] + struct B; + + world.add_handler(|_: Receiver, s: Sender| { + s.send(A); + }); + + world.send(A); + } + + #[test] + #[should_panic] + fn targeted_event_not_in_event_set() { + let mut world = World::new(); + + #[derive(GlobalEvent)] + struct A; + + #[derive(TargetedEvent)] + struct B; + + world.add_handler(|_: Receiver, s: Sender| { + s.send_to(EntityId::NULL, B); + }); + + world.send(A); + } + + #[test] + fn send_event_from_sender_with_lifetime() { + let mut world = World::new(); + + #[derive(GlobalEvent)] + struct A; + + #[derive(GlobalEvent)] + struct B<'a> { + slice: &'a [i32], + string: &'a str, + array: &'a [u64; 4], + } + + fn get_a_send_b(_: Receiver, s: Sender) { + s.send(B { + slice: s.alloc_slice(5, |i| i as i32 + 1), + string: s.alloc_str("pineapple"), + array: s.alloc([10, 20, 30, 40]), + }); + } + + fn get_b(r: Receiver) { + assert_eq!(r.event.slice, &[1, 2, 3, 4, 5]); + assert_eq!(r.event.string, "pineapple"); + assert_eq!(r.event.array, &[10, 20, 30, 40]); + } + + world.add_handler(get_a_send_b); + world.add_handler(get_b); + world.send(A); + } + + #[test] + fn more_than_one_sender() { + let mut world = World::new(); + + #[derive(GlobalEvent)] + struct A(#[allow(dead_code)] u32); + + #[derive(GlobalEvent)] + struct B(#[allow(dead_code)] u32); + + fn send_b_x2(_: Receiver, s1: Sender, s2: Sender) { + s1.send(B(123)); + s2.send(B(456)); + } + + world.add_handler(send_b_x2); + + world.send(A(123)); + } } diff --git a/src/handler.rs b/src/handler.rs index 6b20325..12eb0c6 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -306,7 +306,6 @@ pub(crate) struct HandlerInfoInner { pub(crate) targeted_event_component_access: ComponentAccess, pub(crate) sent_untargeted_events: BitSet, pub(crate) sent_targeted_events: BitSet, - pub(crate) event_queue_access: Access, pub(crate) component_access: ComponentAccess, pub(crate) archetype_filter: ComponentAccess, pub(crate) referenced_components: BitSet, @@ -386,11 +385,6 @@ impl HandlerInfo { unsafe { &(*AliasedBox::as_ptr(&self.0)).sent_targeted_events } } - /// Gets this handler's [`Access`] to the event queue. - pub fn event_queue_access(&self) -> Access { - unsafe { (*AliasedBox::as_ptr(&self.0)).event_queue_access } - } - /// Gets the expression describing this handler's access pub fn component_access(&self) -> &ComponentAccess { unsafe { &(*AliasedBox::as_ptr(&self.0)).component_access } @@ -442,7 +436,6 @@ impl fmt::Debug for HandlerInfo { .field("targeted_event_component_access", &self.targeted_event_component_access()) .field("sent_global_events", &self.sent_global_events()) .field("sent_targeted_events", &self.sent_targeted_events()) - .field("event_queue_access", &self.event_queue_access()) .field("component_access", &self.component_access()) .field("archetype_filter", &self.archetype_filter()) .field("referenced_components", &self.referenced_components()) diff --git a/src/world.rs b/src/world.rs index 3124820..578254c 100644 --- a/src/world.rs +++ b/src/world.rs @@ -9,6 +9,8 @@ use core::marker::PhantomData; use core::mem; use core::ptr::NonNull; +use bumpalo::Bump; + use crate::access::ComponentAccess; use crate::archetype::Archetypes; use crate::component::{ @@ -18,10 +20,10 @@ use crate::component::{ use crate::drop::{drop_fn_of, DropFn}; use crate::entity::{Entities, EntityId, EntityLocation, ReservedEntities}; use crate::event::{ - AddGlobalEvent, AddTargetedEvent, Despawn, Event, EventDescriptor, EventKind, EventMeta, - EventPtr, EventQueue, GlobalEvent, GlobalEventId, GlobalEventIdx, GlobalEventInfo, - GlobalEvents, Insert, Remove, RemoveGlobalEvent, RemoveTargetedEvent, Spawn, TargetedEvent, - TargetedEventId, TargetedEventIdx, TargetedEventInfo, TargetedEvents, + AddGlobalEvent, AddTargetedEvent, Despawn, EventDescriptor, EventKind, EventMeta, EventPtr, + EventQueueItem, GlobalEvent, GlobalEventId, GlobalEventIdx, GlobalEventInfo, GlobalEvents, + Insert, Remove, RemoveGlobalEvent, RemoveTargetedEvent, Spawn, TargetedEvent, TargetedEventId, + TargetedEventIdx, TargetedEventInfo, TargetedEvents, }; use crate::handler::{ AddHandler, Handler, HandlerConfig, HandlerId, HandlerInfo, HandlerInfoInner, HandlerList, @@ -40,7 +42,8 @@ pub struct World { archetypes: Archetypes, global_events: GlobalEvents, targeted_events: TargetedEvents, - event_queue: EventQueue, + event_queue: Vec, + bump: Bump, /// So the world doesn't accidentally implement `Send` or `Sync`. _marker: PhantomData<*const ()>, } @@ -64,7 +67,8 @@ impl World { archetypes: Archetypes::new(), global_events: GlobalEvents::new(), targeted_events: TargetedEvents::new(), - event_queue: EventQueue::new(), + event_queue: vec![], + bump: Bump::new(), _marker: PhantomData, } } @@ -103,7 +107,10 @@ impl World { pub fn send(&mut self, event: E) { let idx = self.add_global_event::().index(); - unsafe { self.event_queue.push_front_global(event, idx) }; + self.event_queue.push(EventQueueItem { + meta: EventMeta::Global { idx }, + event: NonNull::from(self.bump.alloc(event)).cast(), + }); self.flush_event_queue(); } @@ -139,7 +146,10 @@ impl World { pub fn send_to(&mut self, target: EntityId, event: E) { let idx = self.add_targeted_event::().index(); - unsafe { self.event_queue.push_front_targeted(target, event, idx) }; + self.event_queue.push(EventQueueItem { + meta: EventMeta::Targeted { target, idx }, + event: NonNull::from(self.bump.alloc(event)).cast(), + }); self.flush_event_queue(); } @@ -350,15 +360,6 @@ impl World { } }; - let event_queue_access = match config.event_queue_access { - MaybeInvalidAccess::Ok(access) => access, - MaybeInvalidAccess::Invalid => { - return Err(format!( - "handler {handler_name} has conflicting access to the event queue" - )); - } - }; - let component_access_conjunction = config .component_accesses .iter() @@ -400,7 +401,6 @@ impl World { targeted_event_component_access: config.targeted_event_component_access, sent_untargeted_events: config.sent_global_events, sent_targeted_events: config.sent_targeted_events, - event_queue_access, component_access: component_access_conjunction, archetype_filter: component_access_disjunction, referenced_components: config.referenced_components, @@ -601,10 +601,13 @@ impl World { for arch in self.archetypes.iter() { if arch.column_of(component.index()).is_some() { for &entity_id in arch.entity_ids() { - unsafe { - self.event_queue - .push_front_targeted(entity_id, Despawn, despawn_idx) - }; + self.event_queue.push(EventQueueItem { + meta: EventMeta::Targeted { + idx: despawn_idx, + target: entity_id, + }, + event: NonNull::::dangling().cast(), + }); } } } @@ -940,7 +943,7 @@ impl World { /// Send all queued events to handlers. The event queue will be empty after /// this call. fn flush_event_queue(&mut self) { - 'next_event: while let Some(item) = self.event_queue.pop_front() { + 'next_event: while let Some(item) = self.event_queue.pop() { struct EventDropper<'a> { event: NonNull, drop: DropFn, @@ -997,7 +1000,7 @@ impl World { // Drop all events remaining in the event queue. // This must be done here instead of the World's destructor because events // could contain borrowed data. - for item in self.world.event_queue.iter() { + for item in &self.world.event_queue { let drop = match item.meta { EventMeta::Global { idx } => unsafe { self.world @@ -1081,14 +1084,23 @@ impl World { ctx.unpack(); // Reverse pushed events so they're handled in FIFO order. - unsafe { self.event_queue.reverse_from(events_before) }; + unsafe { + self.event_queue + .get_unchecked_mut(events_before..) + .reverse() + }; continue 'next_event; } } // Reverse pushed events so they're handled in FIFO order. - unsafe { ctx.world.event_queue.reverse_from(events_before) }; + unsafe { + ctx.world + .event_queue + .get_unchecked_mut(events_before..) + .reverse() + }; match event_kind { EventKind::Normal => { @@ -1164,7 +1176,8 @@ impl World { } } - self.event_queue.clear(); + self.bump.reset(); + debug_assert!(self.event_queue.is_empty()); } /// Returns a new [`UnsafeWorldCell`] with permission to _read_ all data in @@ -1193,8 +1206,8 @@ impl Default for World { } /// Reference to a [`World`] where all methods take `&self` and aliasing rules -/// are not checked. It is the caller's responsibility to ensure that -/// Rust's aliasing rules are not violated. +/// are not checked. It is the caller's responsibility to ensure that Rust's +/// aliasing rules are not violated. #[derive(Clone, Copy, Debug)] pub struct UnsafeWorldCell<'a> { world: NonNull, @@ -1202,38 +1215,63 @@ pub struct UnsafeWorldCell<'a> { } impl<'a> UnsafeWorldCell<'a> { + /// Allocate data in the world's bump allocator. + /// + /// This operation is not thread safe. + /// /// # Safety /// /// - Must be called from within a handler. - /// - Must have permission to access the event queue mutably. - /// - Event index must be correct for the given event. #[inline] - pub unsafe fn send_global(self, event: E, idx: GlobalEventIdx) { - unsafe { - (*self.world.as_ptr()) - .event_queue - .push_front_global(event, idx) - } + pub unsafe fn alloc_layout(self, layout: Layout) -> NonNull { + let bump = unsafe { &(*self.world.as_ptr()).bump }; + bump.alloc_layout(layout) } + /// Add a global event to the event queue. Ownership of the event is + /// transferred. + /// /// # Safety /// /// - Must be called from within a handler. - /// - Must have permission to access the event queue mutably. + /// - Event must outlive call to top level [`World::send`] or + /// [`World::send_to`]. /// - Event index must be correct for the given event. #[inline] - pub unsafe fn send_targeted(self, target: EntityId, event: E, idx: TargetedEventIdx) { - unsafe { - (*self.world.as_ptr()) - .event_queue - .push_front_targeted(target, event, idx) - } + pub unsafe fn queue_global(self, event: NonNull, idx: GlobalEventIdx) { + let event_queue = &mut (*self.world.as_ptr()).event_queue; + + event_queue.push(EventQueueItem { + meta: EventMeta::Global { idx }, + event, + }); + } + + /// Add a targeted event to the event queue. Ownership of the event is + /// transferred. + /// + /// # Safety + /// + /// - Must be called from within a handler. + /// - Must have permission to access the event queue + #[inline] + pub unsafe fn queue_targeted( + self, + target: EntityId, + event: NonNull, + idx: TargetedEventIdx, + ) { + let event_queue = &mut (*self.world.as_ptr()).event_queue; + + event_queue.push(EventQueueItem { + meta: EventMeta::Targeted { idx, target }, + event, + }); } /// # Safety /// /// - Must be called from within a handler. - /// - Must have permission to access the event queue mutably. pub unsafe fn queue_spawn(self) -> EntityId { let entity_id = (*self.world.as_ptr()) .reserved_entities @@ -1312,12 +1350,12 @@ mod tests { let mut world = World::new(); - world.add_handler(|r: Receiver, mut s: Sender| { + world.add_handler(|r: Receiver, s: Sender| { s.send(B(r.event.0.clone())); s.send(B(r.event.0.clone())); }); - world.add_handler(|r: Receiver, mut s: Sender| { + world.add_handler(|r: Receiver, s: Sender| { s.send(C(r.event.0.clone())); s.send(C(r.event.0.clone())); }); @@ -1353,14 +1391,14 @@ mod tests { let mut world = World::new(); - world.add_handler(|r: Receiver, mut s: Sender| { + world.add_handler(|r: Receiver, s: Sender| { s.send(B(r.event.0.clone())); s.send(B(r.event.0.clone())); }); - world.add_handler(|r: Receiver, mut sender: Sender| { - sender.send(C(r.event.0.clone())); - sender.send(C(r.event.0.clone())); + world.add_handler(|r: Receiver, s: Sender| { + s.send(C(r.event.0.clone())); + s.send(C(r.event.0.clone())); }); world.add_handler(|_: Receiver| panic!("oops!")); @@ -1376,4 +1414,20 @@ mod tests { assert_eq!(Rc::strong_count(&arc), 1); } + + #[test] + fn bump_allocator_is_reset() { + let mut world = World::new(); + + #[derive(GlobalEvent)] + struct Data(#[allow(dead_code)] u64); + + world.send(Data(123)); + + let ptr1 = world.bump.alloc(1_u8) as *const u8; + world.bump.reset(); + let ptr2 = world.bump.alloc(1_u8) as *const u8; + + assert_eq!(ptr1, ptr2); + } } diff --git a/tutorial.md b/tutorial.md index d0d75eb..9632d91 100644 --- a/tutorial.md +++ b/tutorial.md @@ -214,7 +214,7 @@ struct B; #[derive(GlobalEvent)] struct C; -world.add_handler(|_: Receiver, mut sender: Sender<(B, C)>| { +world.add_handler(|_: Receiver, sender: Sender<(B, C)>| { sender.send(B); sender.send(C); println!("sent B and C!"); @@ -250,13 +250,13 @@ struct B(i32); #[derive(GlobalEvent, Debug)] struct C(i32); -fn get_a_send_b(_: Receiver, mut sender: Sender) { +fn get_a_send_b(_: Receiver, sender: Sender) { sender.send(B(0)); sender.send(B(3)); println!("got A, sending B twice!"); } -fn get_b_send_c(r: Receiver, mut sender: Sender) { +fn get_b_send_c(r: Receiver, sender: Sender) { sender.send(C(r.event.0 + 1)); sender.send(C(r.event.0 + 2)); println!("got {:?}, sending C twice!", r.event); @@ -370,7 +370,7 @@ struct InitMonster { fn init_monster_handler( r: Receiver, - mut s: Sender<(Insert, Insert, Insert)> + s: Sender<(Insert, Insert, Insert)> ) { let InitMonster { entity,