Skip to content

Commit 2d315b8

Browse files
committed
Fix bug where events where not being dropped
1 parent b592a72 commit 2d315b8

File tree

3 files changed

+93
-10
lines changed

3 files changed

+93
-10
lines changed

crates/bevy_app/src/app.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::{First, Main, MainSchedulePlugin, Plugin, Plugins, StateTransition};
1+
use crate::{
2+
First, FixedPostUpdate, Last, Main, MainSchedulePlugin, Plugin, Plugins, StateTransition,
3+
};
24
pub use bevy_derive::AppLabel;
35
use bevy_ecs::{
46
prelude::*,
@@ -213,6 +215,7 @@ pub enum PluginsState {
213215

214216
// Dummy plugin used to temporary hold the place in the plugin registry
215217
struct PlaceholderPlugin;
218+
216219
impl Plugin for PlaceholderPlugin {
217220
fn build(&self, _app: &mut App) {}
218221
}
@@ -505,6 +508,7 @@ impl App {
505508
self.init_resource::<Events<T>>().add_systems(
506509
First,
507510
bevy_ecs::event::event_update_system::<T>
511+
.in_set(bevy_ecs::event::EventUpdates)
508512
.run_if(bevy_ecs::event::event_update_condition::<T>),
509513
);
510514
}

crates/bevy_ecs/src/event.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use crate as bevy_ecs;
44
use crate::system::{Local, Res, ResMut, Resource, SystemParam};
55
pub use bevy_ecs_macros::Event;
6+
use bevy_ecs_macros::SystemSet;
67
use bevy_utils::detailed_trace;
78
use std::ops::{Deref, DerefMut};
89
use std::{
@@ -13,6 +14,7 @@ use std::{
1314
marker::PhantomData,
1415
slice::Iter,
1516
};
17+
1618
/// A type that can be stored in an [`Events<E>`] resource
1719
/// You can conveniently access events using the [`EventReader`] and [`EventWriter`] system parameter.
1820
///
@@ -33,6 +35,7 @@ pub struct EventId<E: Event> {
3335
}
3436

3537
impl<E: Event> Copy for EventId<E> {}
38+
3639
impl<E: Event> Clone for EventId<E> {
3740
fn clone(&self) -> Self {
3841
*self
@@ -751,22 +754,31 @@ impl<'a, E: Event> ExactSizeIterator for EventIteratorWithId<'a, E> {
751754
#[derive(Resource, Default)]
752755
pub struct EventUpdateSignal(bool);
753756

754-
/// A system that queues a call to [`Events::update`].
755-
pub fn event_queue_update_system(signal: Option<ResMut<EventUpdateSignal>>) {
757+
#[doc(hidden)]
758+
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
759+
pub struct EventUpdates;
760+
761+
/// Signals the [`event_update_system`] to run after `FixedUpdate` systems.
762+
pub fn signal_event_update_system(signal: Option<ResMut<EventUpdateSignal>>) {
756763
if let Some(mut s) = signal {
757764
s.0 = true;
758765
}
759766
}
760767

768+
/// Resets the `EventUpdateSignal`
769+
pub fn reset_event_update_signal_system(signal: Option<ResMut<EventUpdateSignal>>) {
770+
if let Some(mut s) = signal {
771+
s.0 = false;
772+
}
773+
}
774+
761775
/// A system that calls [`Events::update`].
762776
pub fn event_update_system<T: Event>(
763-
signal: Option<ResMut<EventUpdateSignal>>,
777+
fixed_update_signal: Option<Res<EventUpdateSignal>>,
764778
mut events: ResMut<Events<T>>,
765779
) {
766-
if let Some(mut s) = signal {
767-
// If we haven't got a signal to update the events, but we *could* get such a signal
768-
// return early and update the events later.
769-
if !std::mem::replace(&mut s.0, false) {
780+
if let Some(signal) = fixed_update_signal {
781+
if !signal.0 {
770782
return;
771783
}
772784
}

crates/bevy_time/src/lib.rs

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub mod prelude {
2525
}
2626

2727
use bevy_app::{prelude::*, RunFixedMainLoop};
28-
use bevy_ecs::event::{event_queue_update_system, EventUpdateSignal};
28+
use bevy_ecs::event::{signal_event_update_system, EventUpdateSignal, EventUpdates};
2929
use bevy_ecs::prelude::*;
3030
use bevy_utils::{tracing::warn, Duration, Instant};
3131
pub use crossbeam_channel::TrySendError;
@@ -61,7 +61,11 @@ impl Plugin for TimePlugin {
6161

6262
// ensure the events are not dropped until `FixedMain` systems can observe them
6363
app.init_resource::<EventUpdateSignal>()
64-
.add_systems(FixedPostUpdate, event_queue_update_system);
64+
.add_systems(FixedPostUpdate, signal_event_update_system)
65+
.add_systems(
66+
First,
67+
bevy_ecs::event::reset_event_update_signal_system.after(EventUpdates),
68+
);
6569

6670
#[cfg(feature = "bevy_ci_testing")]
6771
if let Some(ci_testing_config) = app
@@ -142,3 +146,66 @@ fn time_system(
142146
TimeUpdateStrategy::ManualDuration(duration) => time.update_with_duration(*duration),
143147
}
144148
}
149+
150+
#[cfg(test)]
151+
mod tests {
152+
use crate::{Fixed, Time, TimePlugin, TimeUpdateStrategy};
153+
use bevy_app::{App, Startup, Update};
154+
use bevy_ecs::event::{Event, EventReader, EventWriter};
155+
use std::error::Error;
156+
use std::time::Duration;
157+
158+
#[derive(Event)]
159+
struct TestEvent<T: Default> {
160+
sender: std::sync::mpsc::Sender<T>,
161+
}
162+
163+
impl<T: Default> Drop for TestEvent<T> {
164+
fn drop(&mut self) {
165+
self.sender
166+
.send(T::default())
167+
.expect("Failed to send drop signal");
168+
}
169+
}
170+
171+
#[test]
172+
fn events_get_dropped_regression_test_11528() -> Result<(), impl Error> {
173+
let (tx1, rx1) = std::sync::mpsc::channel();
174+
let (tx2, rx2) = std::sync::mpsc::channel();
175+
let mut app = App::new();
176+
app.add_plugins(TimePlugin)
177+
.add_event::<TestEvent<i32>>()
178+
.add_event::<TestEvent<()>>()
179+
.add_systems(Startup, move |mut ev2: EventWriter<TestEvent<()>>| {
180+
ev2.send(TestEvent {
181+
sender: tx2.clone(),
182+
});
183+
})
184+
.add_systems(Update, move |mut ev1: EventWriter<TestEvent<i32>>| {
185+
// Keep adding events so this event type is processed every update
186+
ev1.send(TestEvent {
187+
sender: tx1.clone(),
188+
});
189+
})
190+
.add_systems(
191+
Update,
192+
|mut ev1: EventReader<TestEvent<i32>>, mut ev2: EventReader<TestEvent<()>>| {
193+
// Read events so they can be dropped
194+
for _ in ev1.read() {}
195+
for _ in ev2.read() {}
196+
},
197+
)
198+
.insert_resource(TimeUpdateStrategy::ManualDuration(
199+
Time::<Fixed>::default().timestep(),
200+
));
201+
202+
for _ in 0..10 {
203+
app.update();
204+
}
205+
206+
// Check event type 1 as been dropped at least once
207+
let _drop_signal = rx1.recv_timeout(Duration::from_millis(1000));
208+
// Check event type 2 has been dropped
209+
rx2.recv_timeout(Duration::from_millis(1000))
210+
}
211+
}

0 commit comments

Comments
 (0)