Skip to content

Commit c859eac

Browse files
authored
Fix bug where events are not being dropped (#11528)
# Objective Fix an issue where events are not being dropped after being read. I believe #10077 introduced this issue. The code currently works as follows: 1. `EventUpdateSignal` is **shared for all event types** 2. During the fixed update phase, `EventUpdateSignal` is set to true 3. `event_update_system`, **unique per event type**, runs to update Events<T> 4. `event_update_system` reads value of `EventUpdateSignal` to check if it should update, and then **resets** the value to false If there are multiple event types, the first `event_update_system` run will reset the shared `EventUpdateSignal` signal, preventing other events from being cleared. ## Solution I've updated the code to have separate signals per event type and added a shared signal to notify all systems that the time plugin is installed. ## Changelog - Fixed bug where events were not being dropped
1 parent 041731b commit c859eac

File tree

3 files changed

+89
-7
lines changed

3 files changed

+89
-7
lines changed

crates/bevy_app/src/app.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ pub enum PluginsState {
213213

214214
// Dummy plugin used to temporary hold the place in the plugin registry
215215
struct PlaceholderPlugin;
216+
216217
impl Plugin for PlaceholderPlugin {
217218
fn build(&self, _app: &mut App) {}
218219
}
@@ -505,6 +506,7 @@ impl App {
505506
self.init_resource::<Events<T>>().add_systems(
506507
First,
507508
bevy_ecs::event::event_update_system::<T>
509+
.in_set(bevy_ecs::event::EventUpdates)
508510
.run_if(bevy_ecs::event::event_update_condition::<T>),
509511
);
510512
}

crates/bevy_ecs/src/event.rs

Lines changed: 19 additions & 5 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,33 @@ 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 {
819+
if let Some(signal) = update_signal {
806820
// If we haven't got a signal to update the events, but we *could* get such a signal
807821
// return early and update the events later.
808-
if !std::mem::replace(&mut s.0, false) {
822+
if !signal.0 {
809823
return;
810824
}
811825
}

crates/bevy_time/src/lib.rs

Lines changed: 68 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(
65+
First,
66+
bevy_ecs::event::reset_event_update_signal_system.after(EventUpdates),
67+
)
68+
.add_systems(FixedPostUpdate, signal_event_update_system);
6569

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

0 commit comments

Comments
 (0)