Bevy 0.13 raw World resource access bypasses scheduler exclusivity enforcement
0 reputation · 17 Nov 2021, 08:24 UTC
0 reputation · 17 Nov 2021, 08:24 UTC
In Bevy 0.13 and later, the ECS scheduler enforces exclusive mutable access for resources when a system declares ResMut<T>. However, calling World::resource_mut<T>() directly outside a system—such as in startup code, custom threads, or async tasks—appears to fall outside that scheduler arbitration. The goal is to determine what diagnostics or synchronization guarantees apply to these raw accesses under the current stable release.
Because the scheduler does not track raw World accesses, concurrent mutable calls from multiple threads may proceed without the usual exclusivity checks. It is unclear whether the ECS verifier or any built-in lint detects this pattern, and whether the stricter parallelism checks introduced in 0.13 change the risk profile compared to earlier versions. Any guidance on safe usage would need to be validated against the specific Bevy version in use.
Does Bevy 0.13+ provide any compile-time or runtime detection for raw World::resource_mut calls that bypass ResMut scheduling? What synchronization mechanism is required when a custom thread mutates a resource already declared ResMut in a scheduled system? Has the Bevy team finalized any linting or API restriction to prevent accidental public mutable access through raw World methods?
33275 reputation · 17 Nov 2021, 19:59 UTC
Bevy 0.13+ does not provide compile-time or runtime detection for raw World::resource_mut calls that bypass the scheduler. Because the scheduler only tracks access declared via SystemParam (such as ResMut<T>), direct calls to the World API are invisible to the ECS exclusivity enforcement and the debug ResourceAccessTracker.
When a custom thread or async task mutates a resource that is also accessed by scheduled systems, you must implement your own synchronization. Bevy's internal scheduler cannot arbitrate access to the World once you have a raw reference to it outside of a system's execution context.
To safely mutate a resource from a custom thread while systems are running, use one of the following patterns:
std::sync::Mutex<T> or RwLock<T>. The system should then request Res<Mutex<T>> and lock it manually.ResMut access.&mut World, which ensures the scheduler has paused all other systems.The World API is designed to be the "source of truth" and provides the primitive methods that SystemParam eventually calls. While ResMut acts as a borrow-checker for the scheduler, World::resource_mut is a direct access method. In Bevy 0.13, the stricter parallelism checks apply to the scheduling of systems, not to the World object itself once it is accessed outside the scheduler.
Risk: Calling World::resource_mut concurrently with a system holding ResMut for the same type is a data race. This may result in undefined behavior or crashes, and it will not trigger a Bevy runtime panic because the ResourceAccessTracker only monitors the scheduler's requested access.
To verify your synchronization is working, you can use the following approach:
Mutex: struct MyResource(Mutex<Data>);TSAN on supported platforms) to ensure no concurrent unsynchronized access occurs.Diagnostic Detail Needed: Are you accessing the World via a NonSend resource or through a shared Arc<World> wrapper? This determines if the risk is limited to data races or extends to thread-safety violations.
Use comments to ask for clarification. Post a solution as an answer.
5,260 reputation · 17 Nov 2021, 12:42 UTC
In Bevy 0.13 the scheduler only enforces exclusivity for resources that a system declares with Res or ResMut. A direct call to World::resource_mut is outside that graph; it only needs a mutable reference to the World. If you hold &mut World you serialize all system execution, but in multi‑threaded code you must guard the mutation yourself.
Typical safe patterns:
World or the resource in a Mutex / RwLock when accessing it from another thread.There is currently no compile‑time lint or runtime panic that flags a raw resource_mut that conflicts with a scheduled ResMut. Users must rely on external synchronization.