bevyengine / bevyengine/bevy

Crash when second window is closed first in multiple_windows example.

Open
#19,424 2 comments 0 reactions 0 assignees View on GitHub
A-Windowing C-Bug O-MacOS S-Needs-Reproduction
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 22h
Merged PRs (30d)
161

Description

## Bevy version

0.16.0

## Relevant system information

- Rust version: `rustc 1.85.0 (4d91de4e4 2025-02-17)`
- Operating system: macOS Sequoia 15.5 (24F74)
- Graphics adapter info:
```ignore
`AdapterInfo { name: "Apple M1", vendor: 0, device: 0, device_type: IntegratedGpu, driver: "", driver_info: "", backend: Metal }`
```

## What I did

Ran the `multiple_windows` example provided in the bevy repository.

Steps to reproduce:
1. Run the example.
2. Close the second window before the primary window.

## What went wrong

- Expected behavior:
The application should handle the second window closing gracefully and continue running with the first window.
- Actual behavior:
The application crashes immediately when the second window is closed first. No crash occurs when the first window is closed before the second.

## Additional information

- After investigation, I tracked the crash down to the `AccessKitAdapter` removal logic in the `window_closed` system of the `accessibility` module in the `bevy_winit` crate.
- As a workaround, I delayed the `AccessKitAdapter` removal in the `window_closed` system by 1 frame, which prevented the crash. However, this is admittedly a hacky solution and likely not ideal for upstream.

```rust
fn window_closed(
// Removed
// mut adapters: NonSendMut,

// Added
mut removal_queue: ResMut,

mut handlers: ResMut,
mut events: EventReader,
) {
for WindowClosed { window, .. } in events.read() {
// Removed
// adapters.remove(window);

// Added
removal_queue.to_remove.push(*window);
removal_queue.frame_delay = 1;

handlers.remove(window);
}
}

impl Plugin for AccessKitPlugin {
fn build(&self, app: &mut App) {
app.init_non_send_resource::()
.init_resource::()

// Added
.init_resource::()

.add_event::()
.add_systems(
PostUpdate,
(
poll_receivers,
update_accessibility_nodes.run_if(should_update_accessibility_nodes),
window_closed
.before(poll_receivers)
.before(update_accessibility_nodes),

// Added
delayed_adapter_removal.after(window_closed),

)
.in_set(AccessibilitySystem::Update),
);
}
}

// Added
#[derive(Resource, Default)]
struct AdapterRemovalQueue {
to_remove: Vec,
frame_delay: usize,
}

// Added
fn delayed_adapter_removal(
mut adapters: NonSendMut,
mut removal_queue: ResMut,
) {
if removal_queue.frame_delay > 0 {
removal_queue.frame_delay -= 1;
return;
}

for window in removal_queue.to_remove.drain(..) {
println!("Removing adapter after delay for {:?}", window);
adapters.remove(&window);
}
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.