@@ -21,7 +21,7 @@ use crate::{
21
21
is_apply_system_buffers, BoxedCondition , ExecutorKind , SystemExecutor , SystemSchedule ,
22
22
} ,
23
23
system:: BoxedSystem ,
24
- world:: World ,
24
+ world:: { unsafe_world_cell :: UnsafeWorldCell , World } ,
25
25
} ;
26
26
27
27
use crate as bevy_ecs;
@@ -184,7 +184,6 @@ impl SystemExecutor for MultiThreadedExecutor {
184
184
. map ( |e| e. 0 . clone ( ) ) ;
185
185
let thread_executor = thread_executor. as_deref ( ) ;
186
186
187
- let world = SyncUnsafeCell :: from_mut ( world) ;
188
187
let SyncUnsafeSchedule {
189
188
systems,
190
189
mut conditions,
@@ -197,10 +196,13 @@ impl SystemExecutor for MultiThreadedExecutor {
197
196
// the executor itself is a `Send` future so that it can run
198
197
// alongside systems that claim the local thread
199
198
let executor = async {
199
+ let world_cell = world. as_unsafe_world_cell ( ) ;
200
200
while self . num_completed_systems < self . num_systems {
201
- // SAFETY: self.ready_systems does not contain running systems
201
+ // SAFETY:
202
+ // - self.ready_systems does not contain running systems.
203
+ // - `world_cell` has mutable access to the entire world.
202
204
unsafe {
203
- self . spawn_system_tasks ( scope, systems, & mut conditions, world ) ;
205
+ self . spawn_system_tasks ( scope, systems, & mut conditions, world_cell ) ;
204
206
}
205
207
206
208
if self . num_running_systems > 0 {
@@ -231,7 +233,7 @@ impl SystemExecutor for MultiThreadedExecutor {
231
233
if self . apply_final_buffers {
232
234
// Do one final apply buffers after all systems have completed
233
235
// Commands should be applied while on the scope's thread, not the executor's thread
234
- let res = apply_system_buffers ( & self . unapplied_systems , systems, world. get_mut ( ) ) ;
236
+ let res = apply_system_buffers ( & self . unapplied_systems , systems, world) ;
235
237
if let Err ( payload) = res {
236
238
let mut panic_payload = self . panic_payload . lock ( ) . unwrap ( ) ;
237
239
* panic_payload = Some ( payload) ;
@@ -283,14 +285,16 @@ impl MultiThreadedExecutor {
283
285
}
284
286
285
287
/// # Safety
286
- /// Caller must ensure that `self.ready_systems` does not contain any systems that
287
- /// have been mutably borrowed (such as the systems currently running).
288
+ /// - Caller must ensure that `self.ready_systems` does not contain any systems that
289
+ /// have been mutably borrowed (such as the systems currently running).
290
+ /// - `world_cell` must have permission to access all world data (not counting
291
+ /// any world data that is claimed by systems currently running on this executor).
288
292
unsafe fn spawn_system_tasks < ' scope > (
289
293
& mut self ,
290
294
scope : & Scope < ' _ , ' scope , ( ) > ,
291
295
systems : & ' scope [ SyncUnsafeCell < BoxedSystem > ] ,
292
296
conditions : & mut Conditions ,
293
- cell : & ' scope SyncUnsafeCell < World > ,
297
+ world_cell : UnsafeWorldCell < ' scope > ,
294
298
) {
295
299
if self . exclusive_running {
296
300
return ;
@@ -307,10 +311,7 @@ impl MultiThreadedExecutor {
307
311
// Therefore, no other reference to this system exists and there is no aliasing.
308
312
let system = unsafe { & mut * systems[ system_index] . get ( ) } ;
309
313
310
- // SAFETY: No exclusive system is running.
311
- // Therefore, there is no existing mutable reference to the world.
312
- let world = unsafe { & * cell. get ( ) } ;
313
- if !self . can_run ( system_index, system, conditions, world) {
314
+ if !self . can_run ( system_index, system, conditions, world_cell) {
314
315
// NOTE: exclusive systems with ambiguities are susceptible to
315
316
// being significantly displaced here (compared to single-threaded order)
316
317
// if systems after them in topological order can run
@@ -320,9 +321,10 @@ impl MultiThreadedExecutor {
320
321
321
322
self . ready_systems . set ( system_index, false ) ;
322
323
323
- // SAFETY: Since `self.can_run` returned true earlier, it must have called
324
- // `update_archetype_component_access` for each run condition.
325
- if !self . should_run ( system_index, system, conditions, world) {
324
+ // SAFETY: `can_run` returned true, which means that:
325
+ // - It must have called `update_archetype_component_access` for each run condition.
326
+ // - There can be no systems running whose accesses would conflict with any conditions.
327
+ if !self . should_run ( system_index, system, conditions, world_cell) {
326
328
self . skip_system_and_signal_dependents ( system_index) ;
327
329
continue ;
328
330
}
@@ -331,20 +333,23 @@ impl MultiThreadedExecutor {
331
333
self . num_running_systems += 1 ;
332
334
333
335
if self . system_task_metadata [ system_index] . is_exclusive {
334
- // SAFETY: `can_run` confirmed that no systems are running.
335
- // Therefore, there is no existing reference to the world.
336
+ // SAFETY: `can_run` returned true for this system, which means
337
+ // that no other systems currently have access to the world.
338
+ let world = unsafe { world_cell. world_mut ( ) } ;
339
+ // SAFETY: `can_run` returned true for this system,
340
+ // which means no systems are currently borrowed.
336
341
unsafe {
337
- let world = & mut * cell. get ( ) ;
338
342
self . spawn_exclusive_system_task ( scope, system_index, systems, world) ;
339
343
}
340
344
break ;
341
345
}
342
346
343
347
// SAFETY:
344
348
// - No other reference to this system exists.
345
- // - `self.can_run` has been called, which calls `update_archetype_component_access` with this system.
349
+ // - `can_run` has been called, which calls `update_archetype_component_access` with this system.
350
+ // - `can_run` returned true, so no systems with conflicting world access are running.
346
351
unsafe {
347
- self . spawn_system_task ( scope, system_index, systems, world ) ;
352
+ self . spawn_system_task ( scope, system_index, systems, world_cell ) ;
348
353
}
349
354
}
350
355
@@ -357,7 +362,7 @@ impl MultiThreadedExecutor {
357
362
system_index : usize ,
358
363
system : & mut BoxedSystem ,
359
364
conditions : & mut Conditions ,
360
- world : & World ,
365
+ world : UnsafeWorldCell ,
361
366
) -> bool {
362
367
let system_meta = & self . system_task_metadata [ system_index] ;
363
368
if system_meta. is_exclusive && self . num_running_systems > 0 {
@@ -413,15 +418,17 @@ impl MultiThreadedExecutor {
413
418
}
414
419
415
420
/// # Safety
416
- ///
417
- /// `update_archetype_component` must have been called with `world`
418
- /// for each run condition in `conditions`.
421
+ /// * `world` must have permission to read any world data required by
422
+ /// the system's conditions: this includes conditions for the system
423
+ /// itself, and conditions for any of the system's sets.
424
+ /// * `update_archetype_component` must have been called with `world`
425
+ /// for each run condition in `conditions`.
419
426
unsafe fn should_run (
420
427
& mut self ,
421
428
system_index : usize ,
422
429
_system : & BoxedSystem ,
423
430
conditions : & mut Conditions ,
424
- world : & World ,
431
+ world : UnsafeWorldCell ,
425
432
) -> bool {
426
433
let mut should_run = !self . skipped_systems . contains ( system_index) ;
427
434
for set_idx in conditions. sets_with_conditions_of_systems [ system_index] . ones ( ) {
@@ -430,7 +437,10 @@ impl MultiThreadedExecutor {
430
437
}
431
438
432
439
// Evaluate the system set's conditions.
433
- // SAFETY: `update_archetype_component_access` has been called for each run condition.
440
+ // SAFETY:
441
+ // - The caller ensures that `world` has permission to read any data
442
+ // required by the conditions.
443
+ // - `update_archetype_component_access` has been called for each run condition.
434
444
let set_conditions_met =
435
445
evaluate_and_fold_conditions ( & mut conditions. set_conditions [ set_idx] , world) ;
436
446
@@ -444,7 +454,10 @@ impl MultiThreadedExecutor {
444
454
}
445
455
446
456
// Evaluate the system's conditions.
447
- // SAFETY: `update_archetype_component_access` has been called for each run condition.
457
+ // SAFETY:
458
+ // - The caller ensures that `world` has permission to read any data
459
+ // required by the conditions.
460
+ // - `update_archetype_component_access` has been called for each run condition.
448
461
let system_conditions_met =
449
462
evaluate_and_fold_conditions ( & mut conditions. system_conditions [ system_index] , world) ;
450
463
@@ -459,14 +472,16 @@ impl MultiThreadedExecutor {
459
472
460
473
/// # Safety
461
474
/// - Caller must not alias systems that are running.
475
+ /// - `world` must have permission to access the world data
476
+ /// used by the specified system.
462
477
/// - `update_archetype_component_access` must have been called with `world`
463
478
/// on the system assocaited with `system_index`.
464
479
unsafe fn spawn_system_task < ' scope > (
465
480
& mut self ,
466
481
scope : & Scope < ' _ , ' scope , ( ) > ,
467
482
system_index : usize ,
468
483
systems : & ' scope [ SyncUnsafeCell < BoxedSystem > ] ,
469
- world : & ' scope World ,
484
+ world : UnsafeWorldCell < ' scope > ,
470
485
) {
471
486
// SAFETY: this system is not running, no other reference exists
472
487
let system = unsafe { & mut * systems[ system_index] . get ( ) } ;
@@ -483,7 +498,8 @@ impl MultiThreadedExecutor {
483
498
let system_guard = system_span. enter ( ) ;
484
499
let res = std:: panic:: catch_unwind ( AssertUnwindSafe ( || {
485
500
// SAFETY:
486
- // - Access: TODO.
501
+ // - The caller ensures that we have permission to
502
+ // access the world data used by the system.
487
503
// - `update_archetype_component_access` has been called.
488
504
unsafe { system. run_unsafe ( ( ) , world) } ;
489
505
} ) ) ;
@@ -688,18 +704,23 @@ fn apply_system_buffers(
688
704
}
689
705
690
706
/// # Safety
691
- ///
692
- /// `update_archetype_component_access` must have been called
693
- /// with `world` for each condition in `conditions`.
694
- unsafe fn evaluate_and_fold_conditions ( conditions : & mut [ BoxedCondition ] , world : & World ) -> bool {
707
+ /// - `world` must have permission to read any world data
708
+ /// required by `conditions`.
709
+ /// - `update_archetype_component_access` must have been called
710
+ /// with `world` for each condition in `conditions`.
711
+ unsafe fn evaluate_and_fold_conditions (
712
+ conditions : & mut [ BoxedCondition ] ,
713
+ world : UnsafeWorldCell ,
714
+ ) -> bool {
695
715
// not short-circuiting is intentional
696
716
#[ allow( clippy:: unnecessary_fold) ]
697
717
conditions
698
718
. iter_mut ( )
699
719
. map ( |condition| {
700
720
#[ cfg( feature = "trace" ) ]
701
721
let _condition_span = info_span ! ( "condition" , name = & * condition. name( ) ) . entered ( ) ;
702
- // SAFETY: caller ensures system access is compatible
722
+ // SAFETY: The caller ensures that `world` has permission to
723
+ // access any data required by the condition.
703
724
unsafe { condition. run_unsafe ( ( ) , world) }
704
725
} )
705
726
. fold ( true , |acc, res| acc && res)
0 commit comments