1
1
use std:: {
2
2
any:: Any ,
3
- sync:: { Arc , Mutex } ,
3
+ sync:: { Arc , Mutex , MutexGuard } ,
4
4
} ;
5
5
6
6
use bevy_tasks:: { ComputeTaskPool , Scope , TaskPool , ThreadExecutor } ;
@@ -30,7 +30,7 @@ use super::__rust_begin_short_backtrace;
30
30
struct Environment < ' env , ' sys > {
31
31
executor : & ' env MultiThreadedExecutor ,
32
32
systems : & ' sys [ SyncUnsafeCell < BoxedSystem > ] ,
33
- conditions : Mutex < Conditions < ' sys > > ,
33
+ conditions : SyncUnsafeCell < Conditions < ' sys > > ,
34
34
world_cell : UnsafeWorldCell < ' env > ,
35
35
}
36
36
@@ -50,7 +50,7 @@ impl<'env, 'sys> Environment<'env, 'sys> {
50
50
Environment {
51
51
executor,
52
52
systems : SyncUnsafeCell :: from_mut ( schedule. systems . as_mut_slice ( ) ) . as_slice_of_cells ( ) ,
53
- conditions : Mutex :: new ( Conditions {
53
+ conditions : SyncUnsafeCell :: new ( Conditions {
54
54
system_conditions : & mut schedule. system_conditions ,
55
55
set_conditions : & mut schedule. set_conditions ,
56
56
sets_with_conditions_of_systems : & schedule. sets_with_conditions_of_systems ,
@@ -77,7 +77,6 @@ struct SystemTaskMetadata {
77
77
/// The result of running a system that is sent across a channel.
78
78
struct SystemResult {
79
79
system_index : usize ,
80
- success : bool ,
81
80
}
82
81
83
82
/// Runs the schedule using a thread pool. Non-conflicting systems can run in parallel.
@@ -90,6 +89,7 @@ pub struct MultiThreadedExecutor {
90
89
apply_final_deferred : bool ,
91
90
/// When set, tells the executor that a thread has panicked.
92
91
panic_payload : Mutex < Option < Box < dyn Any + Send > > > ,
92
+ starting_systems : FixedBitSet ,
93
93
/// Cached tracing span
94
94
#[ cfg( feature = "trace" ) ]
95
95
executor_span : Span ,
@@ -105,12 +105,8 @@ pub struct ExecutorState {
105
105
local_thread_running : bool ,
106
106
/// Returns `true` if an exclusive system is running.
107
107
exclusive_running : bool ,
108
- /// The number of systems expected to run.
109
- num_systems : usize ,
110
108
/// The number of systems that are running.
111
109
num_running_systems : usize ,
112
- /// The number of systems that have completed.
113
- num_completed_systems : usize ,
114
110
/// The number of dependencies each system has that have not completed.
115
111
num_dependencies_remaining : Vec < usize > ,
116
112
/// System sets whose conditions have been evaluated.
@@ -127,8 +123,6 @@ pub struct ExecutorState {
127
123
completed_systems : FixedBitSet ,
128
124
/// Systems that have run but have not had their buffers applied.
129
125
unapplied_systems : FixedBitSet ,
130
- /// When set, stops the executor from running any more systems.
131
- stop_spawning : bool ,
132
126
}
133
127
134
128
/// References to data required by the executor.
@@ -159,6 +153,7 @@ impl SystemExecutor for MultiThreadedExecutor {
159
153
let set_count = schedule. set_ids . len ( ) ;
160
154
161
155
self . system_completion = ConcurrentQueue :: bounded ( sys_count. max ( 1 ) ) ;
156
+ self . starting_systems = FixedBitSet :: with_capacity ( sys_count) ;
162
157
state. evaluated_sets = FixedBitSet :: with_capacity ( set_count) ;
163
158
state. ready_systems = FixedBitSet :: with_capacity ( sys_count) ;
164
159
state. ready_systems_copy = FixedBitSet :: with_capacity ( sys_count) ;
@@ -175,6 +170,9 @@ impl SystemExecutor for MultiThreadedExecutor {
175
170
is_send : schedule. systems [ index] . is_send ( ) ,
176
171
is_exclusive : schedule. systems [ index] . is_exclusive ( ) ,
177
172
} ) ;
173
+ if schedule. system_dependencies [ index] == 0 {
174
+ self . starting_systems . insert ( index) ;
175
+ }
178
176
}
179
177
180
178
state. num_dependencies_remaining = Vec :: with_capacity ( sys_count) ;
@@ -188,23 +186,14 @@ impl SystemExecutor for MultiThreadedExecutor {
188
186
) {
189
187
let state = self . state . get_mut ( ) . unwrap ( ) ;
190
188
// reset counts
191
- state. num_systems = schedule. systems . len ( ) ;
192
- if state. num_systems == 0 {
189
+ if schedule. systems . is_empty ( ) {
193
190
return ;
194
191
}
195
192
state. num_running_systems = 0 ;
196
- state. num_completed_systems = 0 ;
197
- state. num_dependencies_remaining . clear ( ) ;
198
193
state
199
194
. num_dependencies_remaining
200
- . extend_from_slice ( & schedule. system_dependencies ) ;
201
-
202
- for ( system_index, dependencies) in state. num_dependencies_remaining . iter_mut ( ) . enumerate ( )
203
- {
204
- if * dependencies == 0 {
205
- state. ready_systems . insert ( system_index) ;
206
- }
207
- }
195
+ . clone_from ( & schedule. system_dependencies ) ;
196
+ state. ready_systems . clone_from ( & self . starting_systems ) ;
208
197
209
198
// If stepping is enabled, make sure we skip those systems that should
210
199
// not be run.
@@ -213,13 +202,12 @@ impl SystemExecutor for MultiThreadedExecutor {
213
202
debug_assert_eq ! ( skipped_systems. len( ) , state. completed_systems. len( ) ) ;
214
203
// mark skipped systems as completed
215
204
state. completed_systems |= skipped_systems;
216
- state. num_completed_systems = state. completed_systems . count_ones ( ..) ;
217
205
218
206
// signal the dependencies for each of the skipped systems, as
219
207
// though they had run
220
208
for system_index in skipped_systems. ones ( ) {
221
209
state. signal_dependents ( system_index) ;
222
- state. ready_systems . set ( system_index, false ) ;
210
+ state. ready_systems . remove ( system_index) ;
223
211
}
224
212
}
225
213
@@ -251,15 +239,14 @@ impl SystemExecutor for MultiThreadedExecutor {
251
239
// Commands should be applied while on the scope's thread, not the executor's thread
252
240
let res = apply_deferred ( & state. unapplied_systems , systems, world) ;
253
241
if let Err ( payload) = res {
254
- let mut panic_payload = self . panic_payload . lock ( ) . unwrap ( ) ;
242
+ let panic_payload = self . panic_payload . get_mut ( ) . unwrap ( ) ;
255
243
* panic_payload = Some ( payload) ;
256
244
}
257
245
state. unapplied_systems . clear ( ) ;
258
- debug_assert ! ( state. unapplied_systems. is_clear( ) ) ;
259
246
}
260
247
261
248
// check to see if there was a panic
262
- let mut payload = self . panic_payload . lock ( ) . unwrap ( ) ;
249
+ let payload = self . panic_payload . get_mut ( ) . unwrap ( ) ;
263
250
if let Some ( payload) = payload. take ( ) {
264
251
std:: panic:: resume_unwind ( payload) ;
265
252
}
@@ -288,10 +275,7 @@ impl<'scope, 'env: 'scope, 'sys> Context<'scope, 'env, 'sys> {
288
275
self . environment
289
276
. executor
290
277
. system_completion
291
- . push ( SystemResult {
292
- system_index,
293
- success : res. is_ok ( ) ,
294
- } )
278
+ . push ( SystemResult { system_index } )
295
279
. unwrap_or_else ( |error| unreachable ! ( "{}" , error) ) ;
296
280
if let Err ( payload) = res {
297
281
eprintln ! ( "Encountered a panic in system `{}`!" , & * system. name( ) ) ;
@@ -304,17 +288,25 @@ impl<'scope, 'env: 'scope, 'sys> Context<'scope, 'env, 'sys> {
304
288
self . tick_executor ( ) ;
305
289
}
306
290
291
+ fn try_lock < ' a > ( & ' a self ) -> Option < ( & ' a mut Conditions < ' sys > , MutexGuard < ' a , ExecutorState > ) > {
292
+ let guard = self . environment . executor . state . try_lock ( ) . ok ( ) ?;
293
+ // SAFETY: This is an exclusive access as no other location fetches conditions mutably, and
294
+ // is synchronized by the lock on the executor state.
295
+ let conditions = unsafe { & mut * self . environment . conditions . get ( ) } ;
296
+ Some ( ( conditions, guard) )
297
+ }
298
+
307
299
fn tick_executor ( & self ) {
308
300
// Ensure that the executor handles any events pushed to the system_completion queue by this thread.
309
301
// If this thread acquires the lock, the exector runs after the push() and they are processed.
310
302
// If this thread does not acquire the lock, then the is_empty() check on the other thread runs
311
303
// after the lock is released, which is after try_lock() failed, which is after the push()
312
304
// on this thread, so the is_empty() check will see the new events and loop.
313
305
loop {
314
- let Ok ( mut guard) = self . environment . executor . state . try_lock ( ) else {
306
+ let Some ( ( conditions , mut guard) ) = self . try_lock ( ) else {
315
307
return ;
316
308
} ;
317
- guard. tick ( self ) ;
309
+ guard. tick ( self , conditions ) ;
318
310
// Make sure we drop the guard before checking system_completion.is_empty(), or we could lose events.
319
311
drop ( guard) ;
320
312
if self . environment . executor . system_completion . is_empty ( ) {
@@ -332,6 +324,7 @@ impl MultiThreadedExecutor {
332
324
Self {
333
325
state : Mutex :: new ( ExecutorState :: new ( ) ) ,
334
326
system_completion : ConcurrentQueue :: unbounded ( ) ,
327
+ starting_systems : FixedBitSet :: new ( ) ,
335
328
apply_final_deferred : true ,
336
329
panic_payload : Mutex :: new ( None ) ,
337
330
#[ cfg( feature = "trace" ) ]
@@ -344,9 +337,7 @@ impl ExecutorState {
344
337
fn new ( ) -> Self {
345
338
Self {
346
339
system_task_metadata : Vec :: new ( ) ,
347
- num_systems : 0 ,
348
340
num_running_systems : 0 ,
349
- num_completed_systems : 0 ,
350
341
num_dependencies_remaining : Vec :: new ( ) ,
351
342
active_access : default ( ) ,
352
343
local_thread_running : false ,
@@ -358,11 +349,10 @@ impl ExecutorState {
358
349
skipped_systems : FixedBitSet :: new ( ) ,
359
350
completed_systems : FixedBitSet :: new ( ) ,
360
351
unapplied_systems : FixedBitSet :: new ( ) ,
361
- stop_spawning : false ,
362
352
}
363
353
}
364
354
365
- fn tick ( & mut self , context : & Context ) {
355
+ fn tick ( & mut self , context : & Context , conditions : & mut Conditions ) {
366
356
#[ cfg( feature = "trace" ) ]
367
357
let _span = context. environment . executor . executor_span . enter ( ) ;
368
358
@@ -376,7 +366,7 @@ impl ExecutorState {
376
366
// - `finish_system_and_handle_dependents` has updated the currently running systems.
377
367
// - `rebuild_active_access` locks access for all currently running systems.
378
368
unsafe {
379
- self . spawn_system_tasks ( context) ;
369
+ self . spawn_system_tasks ( context, conditions ) ;
380
370
}
381
371
}
382
372
@@ -385,17 +375,11 @@ impl ExecutorState {
385
375
/// have been mutably borrowed (such as the systems currently running).
386
376
/// - `world_cell` must have permission to access all world data (not counting
387
377
/// any world data that is claimed by systems currently running on this executor).
388
- unsafe fn spawn_system_tasks ( & mut self , context : & Context ) {
378
+ unsafe fn spawn_system_tasks ( & mut self , context : & Context , conditions : & mut Conditions ) {
389
379
if self . exclusive_running {
390
380
return ;
391
381
}
392
382
393
- let mut conditions = context
394
- . environment
395
- . conditions
396
- . try_lock ( )
397
- . expect ( "Conditions should only be locked while owning the executor state" ) ;
398
-
399
383
// can't borrow since loop mutably borrows `self`
400
384
let mut ready_systems = std:: mem:: take ( & mut self . ready_systems_copy ) ;
401
385
@@ -405,19 +389,18 @@ impl ExecutorState {
405
389
while check_for_new_ready_systems {
406
390
check_for_new_ready_systems = false ;
407
391
408
- ready_systems. clear ( ) ;
409
- ready_systems. union_with ( & self . ready_systems ) ;
392
+ ready_systems. clone_from ( & self . ready_systems ) ;
410
393
411
394
for system_index in ready_systems. ones ( ) {
412
- assert ! ( !self . running_systems. contains( system_index) ) ;
395
+ debug_assert ! ( !self . running_systems. contains( system_index) ) ;
413
396
// SAFETY: Caller assured that these systems are not running.
414
397
// Therefore, no other reference to this system exists and there is no aliasing.
415
398
let system = unsafe { & mut * context. environment . systems [ system_index] . get ( ) } ;
416
399
417
400
if !self . can_run (
418
401
system_index,
419
402
system,
420
- & mut conditions,
403
+ conditions,
421
404
context. environment . world_cell ,
422
405
) {
423
406
// NOTE: exclusive systems with ambiguities are susceptible to
@@ -427,7 +410,7 @@ impl ExecutorState {
427
410
continue ;
428
411
}
429
412
430
- self . ready_systems . set ( system_index, false ) ;
413
+ self . ready_systems . remove ( system_index) ;
431
414
432
415
// SAFETY: `can_run` returned true, which means that:
433
416
// - It must have called `update_archetype_component_access` for each run condition.
@@ -436,7 +419,7 @@ impl ExecutorState {
436
419
!self . should_run (
437
420
system_index,
438
421
system,
439
- & mut conditions,
422
+ conditions,
440
423
context. environment . world_cell ,
441
424
)
442
425
} {
@@ -523,11 +506,9 @@ impl ExecutorState {
523
506
return false ;
524
507
}
525
508
526
- // PERF: use an optimized clear() + extend() operation
527
- let meta_access =
528
- & mut self . system_task_metadata [ system_index] . archetype_component_access ;
529
- meta_access. clear ( ) ;
530
- meta_access. extend ( system. archetype_component_access ( ) ) ;
509
+ self . system_task_metadata [ system_index]
510
+ . archetype_component_access
511
+ . clone_from ( system. archetype_component_access ( ) ) ;
531
512
}
532
513
533
514
true
@@ -666,10 +647,7 @@ impl ExecutorState {
666
647
}
667
648
668
649
fn finish_system_and_handle_dependents ( & mut self , result : SystemResult ) {
669
- let SystemResult {
670
- system_index,
671
- success,
672
- } = result;
650
+ let SystemResult { system_index, .. } = result;
673
651
674
652
if self . system_task_metadata [ system_index] . is_exclusive {
675
653
self . exclusive_running = false ;
@@ -681,20 +659,14 @@ impl ExecutorState {
681
659
682
660
debug_assert ! ( self . num_running_systems >= 1 ) ;
683
661
self . num_running_systems -= 1 ;
684
- self . num_completed_systems += 1 ;
685
- self . running_systems . set ( system_index, false ) ;
662
+ self . running_systems . remove ( system_index) ;
686
663
self . completed_systems . insert ( system_index) ;
687
664
self . unapplied_systems . insert ( system_index) ;
688
665
689
666
self . signal_dependents ( system_index) ;
690
-
691
- if !success {
692
- self . stop_spawning_systems ( ) ;
693
- }
694
667
}
695
668
696
669
fn skip_system_and_signal_dependents ( & mut self , system_index : usize ) {
697
- self . num_completed_systems += 1 ;
698
670
self . completed_systems . insert ( system_index) ;
699
671
self . signal_dependents ( system_index) ;
700
672
}
@@ -710,13 +682,6 @@ impl ExecutorState {
710
682
}
711
683
}
712
684
713
- fn stop_spawning_systems ( & mut self ) {
714
- if !self . stop_spawning {
715
- self . num_systems = self . num_completed_systems + self . num_running_systems ;
716
- self . stop_spawning = true ;
717
- }
718
- }
719
-
720
685
fn rebuild_active_access ( & mut self ) {
721
686
self . active_access . clear ( ) ;
722
687
for index in self . running_systems . ones ( ) {
0 commit comments