Skip to content

Commit 41a32c0

Browse files
committed
Fix bug where events where not being dropped
1 parent b66f2fd commit 41a32c0

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
@@ -790,22 +793,31 @@ impl<'a, E: Event> ExactSizeIterator for EventIteratorWithId<'a, E> {
790793
#[derive(Resource, Default)]
791794
pub struct EventUpdateSignal(bool);
792795

793-
/// A system that queues a call to [`Events::update`].
794-
pub fn event_queue_update_system(signal: Option<ResMut<EventUpdateSignal>>) {
796+
#[doc(hidden)]
797+
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
798+
pub struct EventUpdates;
799+
800+
/// Signals the [`event_update_system`] to run after `FixedUpdate` systems.
801+
pub fn signal_event_update_system(signal: Option<ResMut<EventUpdateSignal>>) {
795802
if let Some(mut s) = signal {
796803
s.0 = true;
797804
}
798805
}
799806

807+
/// Resets the `EventUpdateSignal`
808+
pub fn reset_event_update_signal_system(signal: Option<ResMut<EventUpdateSignal>>) {
809+
if let Some(mut s) = signal {
810+
s.0 = false;
811+
}
812+
}
813+
800814
/// A system that calls [`Events::update`].
801815
pub fn event_update_system<T: Event>(
802-
signal: Option<ResMut<EventUpdateSignal>>,
816+
update_signal: Option<Res<EventUpdateSignal>>,
803817
mut events: ResMut<Events<T>>,
804818
) {
805-
if let Some(mut s) = signal {
806-
// If we haven't got a signal to update the events, but we *could* get such a signal
807-
// return early and update the events later.
808-
if !std::mem::replace(&mut s.0, false) {
819+
if let Some(signal) = update_signal {
820+
if !signal.0 {
809821
return;
810822
}
811823
}

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)