1
1
use std:: any:: TypeId ;
2
+ use std:: cell:: RefCell ;
2
3
use std:: marker:: PhantomData ;
3
- use std:: sync:: { Arc , Mutex , Weak } ;
4
4
5
5
use bevy_ptr:: { OwningPtr , Ptr } ;
6
6
@@ -12,6 +12,10 @@ use crate::storage::{ResourceData, Resources};
12
12
use crate :: system:: { Resource , SystemParam } ;
13
13
use crate :: world:: { unsafe_world_cell:: UnsafeWorldCell , World } ;
14
14
15
+ thread_local ! {
16
+ static TLS : RefCell <ThreadLocals > = RefCell :: new( ThreadLocals :: new( ) ) ;
17
+ }
18
+
15
19
/// A type that can be inserted into [`ThreadLocals`]. Unlike [`Resource`], this does not require
16
20
/// [`Send`] or [`Sync`].
17
21
pub trait ThreadLocalResource : ' static { }
@@ -322,32 +326,8 @@ impl ThreadLocals {
322
326
}
323
327
}
324
328
325
- /// A "scoped lock" on [`ThreadLocals`], which is protected by a mutex and can be accessed
326
- /// through this guard via its [`Deref`] and [`DerefMut`] implementations.
327
- ///
328
- /// When this guard is dropped, the lock will be unlocked.
329
- #[ doc( hidden) ]
330
- pub struct ThreadLocalsGuard < ' a > {
331
- guard : std:: sync:: MutexGuard < ' a , ThreadLocals > ,
332
- // needed to decrement the strong reference count once dropped
333
- _arc : Arc < Mutex < ThreadLocals > > ,
334
- }
335
-
336
- impl std:: ops:: Deref for ThreadLocalsGuard < ' _ > {
337
- type Target = ThreadLocals ;
338
- fn deref ( & self ) -> & Self :: Target {
339
- & self . guard
340
- }
341
- }
342
-
343
- impl std:: ops:: DerefMut for ThreadLocalsGuard < ' _ > {
344
- fn deref_mut ( & mut self ) -> & mut Self :: Target {
345
- & mut self . guard
346
- }
347
- }
348
-
349
329
/// Type alias for tasks that access thread-local data.
350
- pub type ThreadLocalTask = Box < dyn FnOnce ( & mut ThreadLocals ) + Send + ' static > ;
330
+ pub type ThreadLocalTask = Box < dyn FnOnce ( ) + Send + ' static > ;
351
331
352
332
/// An error returned from the [`ThreadLocalTaskSender::send_task`] function.
353
333
///
@@ -369,9 +349,8 @@ pub trait ThreadLocalTaskSender: Send + 'static {
369
349
/// A [`Resource`] that enables the use of the [`ThreadLocal`] system parameter.
370
350
#[ derive( Resource ) ]
371
351
struct ThreadLocalChannel {
372
- owning_thread : std:: thread:: ThreadId ,
373
- direct : Weak < Mutex < ThreadLocals > > ,
374
- indirect : Box < dyn ThreadLocalTaskSender > ,
352
+ thread : std:: thread:: ThreadId ,
353
+ sender : Box < dyn ThreadLocalTaskSender > ,
375
354
}
376
355
377
356
// SAFETY: The pointer to the thread-local storage is only dereferenced in its owning thread.
@@ -381,28 +360,18 @@ unsafe impl Send for ThreadLocalChannel {}
381
360
// Likewise, all operations require an exclusive reference, so there can be no races.
382
361
unsafe impl Sync for ThreadLocalChannel { }
383
362
384
- /// A mutex-guarded instance of [`ThreadLocals`].
363
+ /// A guard to access [`ThreadLocals`].
385
364
pub struct ThreadLocalStorage {
386
365
thread : std:: thread:: ThreadId ,
387
- locals : Arc < Mutex < ThreadLocals > > ,
366
+ // !Send + !Sync
367
+ _marker : PhantomData < * const ( ) > ,
388
368
}
389
369
390
370
impl Default for ThreadLocalStorage {
391
371
fn default ( ) -> Self {
392
372
Self {
393
373
thread : std:: thread:: current ( ) . id ( ) ,
394
- // Need a reference-counted pointer that can exist on multiple threads. Only the local
395
- // thread is able to deference it.
396
- #[ allow( clippy:: arc_with_non_send_sync) ]
397
- locals : Arc :: new ( Mutex :: new ( ThreadLocals :: new ( ) ) ) ,
398
- }
399
- }
400
- }
401
-
402
- impl Drop for ThreadLocalStorage {
403
- fn drop ( & mut self ) {
404
- if Arc :: strong_count ( & self . locals ) > 1 {
405
- panic ! ( "`ThreadLocalStorage` was dropped while there was an active borrow of `ThreadLocals`." )
374
+ _marker : PhantomData ,
406
375
}
407
376
}
408
377
}
@@ -413,16 +382,39 @@ impl ThreadLocalStorage {
413
382
Self :: default ( )
414
383
}
415
384
416
- /// Returns an exclusive reference to the underlying [`ThreadLocals`] .
385
+ /// Inserts a new resource with its default value .
417
386
///
418
- /// # Panics
387
+ /// If the resource already exists, nothing happens.
388
+ #[ inline]
389
+ pub fn init_resource < R : ThreadLocalResource + Default > ( & mut self ) {
390
+ TLS . with_borrow_mut ( |tls| {
391
+ tls. init_resource :: < R > ( ) ;
392
+ } ) ;
393
+ }
394
+
395
+ /// Inserts a new resource with the given `value`.
419
396
///
420
- /// This function will panic if an exclusive reference cannot be acquired.
421
- pub fn lock ( & self ) -> ThreadLocalsGuard < ' _ > {
422
- ThreadLocalsGuard {
423
- guard : self . locals . try_lock ( ) . unwrap ( ) ,
424
- _arc : self . locals . clone ( ) ,
425
- }
397
+ /// Resources are "unique" data of a given type. If you insert a resource of a type that already
398
+ /// exists, you will overwrite any existing data.
399
+ #[ inline]
400
+ pub fn insert_resource < R : ThreadLocalResource > ( & mut self , value : R ) {
401
+ TLS . with_borrow_mut ( |tls| {
402
+ tls. insert_resource ( value) ;
403
+ } ) ;
404
+ }
405
+
406
+ /// Removes the resource of a given type and returns it, if it exists.
407
+ #[ inline]
408
+ pub fn remove_resource < R : ThreadLocalResource > ( & mut self ) -> Option < R > {
409
+ TLS . with_borrow_mut ( |tls| tls. remove_resource :: < R > ( ) )
410
+ }
411
+
412
+ /// Temporarily removes `R` from the [`ThreadLocals`], then re-inserts it before returning.
413
+ pub fn resource_scope < R : ThreadLocalResource , T > (
414
+ & mut self ,
415
+ f : impl FnOnce ( & mut ThreadLocals , Mut < R > ) -> T ,
416
+ ) -> T {
417
+ TLS . with_borrow_mut ( |tls| tls. resource_scope ( f) )
426
418
}
427
419
428
420
/// Inserts a channel into `world` that systems in `world` (via [`ThreadLocal`]) can use to
@@ -432,9 +424,8 @@ impl ThreadLocalStorage {
432
424
S : ThreadLocalTaskSender ,
433
425
{
434
426
let channel = ThreadLocalChannel {
435
- owning_thread : self . thread ,
436
- direct : Arc :: downgrade ( & self . locals ) ,
437
- indirect : Box :: new ( sender) ,
427
+ thread : self . thread ,
428
+ sender : Box :: new ( sender) ,
438
429
} ;
439
430
440
431
world. insert_resource ( channel) ;
@@ -448,7 +439,7 @@ impl ThreadLocalStorage {
448
439
}
449
440
450
441
enum ThreadLocalAccess < ' a > {
451
- Direct ( ThreadLocalsGuard < ' a > ) ,
442
+ Direct ,
452
443
Indirect ( & ' a mut dyn ThreadLocalTaskSender ) ,
453
444
}
454
445
@@ -472,7 +463,7 @@ impl ThreadLocal<'_, '_> {
472
463
T : Send + ' static ,
473
464
{
474
465
match self . access {
475
- ThreadLocalAccess :: Direct ( _ ) => self . run_direct ( f) ,
466
+ ThreadLocalAccess :: Direct => self . run_direct ( f) ,
476
467
ThreadLocalAccess :: Indirect ( _) => self . run_indirect ( f) ,
477
468
}
478
469
}
@@ -482,18 +473,16 @@ impl ThreadLocal<'_, '_> {
482
473
F : FnOnce ( & mut ThreadLocals ) -> T + Send ,
483
474
T : Send + ' static ,
484
475
{
485
- let ThreadLocalAccess :: Direct ( ref mut tls) = self . access else {
486
- unreachable ! ( )
487
- } ;
488
-
489
- tls. update_change_tick ( ) ;
490
- let saved = std:: mem:: replace ( & mut tls. last_tick , * self . last_run ) ;
491
- let result = f ( & mut * tls) ;
492
- tls. last_tick = saved;
476
+ debug_assert ! ( matches!( self . access, ThreadLocalAccess :: Direct ) ) ;
493
477
494
- * self . last_run = tls. last_tick ;
495
-
496
- result
478
+ TLS . with_borrow_mut ( |tls| {
479
+ tls. update_change_tick ( ) ;
480
+ let saved = std:: mem:: replace ( & mut tls. last_tick , * self . last_run ) ;
481
+ let result = f ( tls) ;
482
+ tls. last_tick = saved;
483
+ * self . last_run = tls. curr_tick ;
484
+ result
485
+ } )
497
486
}
498
487
499
488
fn run_indirect < F , T > ( & mut self , f : F ) -> T
@@ -507,22 +496,23 @@ impl ThreadLocal<'_, '_> {
507
496
508
497
let system_tick = * self . last_run ;
509
498
let ( result_tx, result_rx) = std:: sync:: mpsc:: sync_channel ( 1 ) ;
510
- let task = move |tls : & mut ThreadLocals | {
511
- tls. update_change_tick ( ) ;
512
- let saved = std:: mem:: replace ( & mut tls. last_tick , system_tick) ;
513
- // we want to propagate to caller instead of panicking in the main thread
514
- let result =
515
- std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || ( f ( tls) , tls. last_tick ) ) ) ;
516
- tls. last_tick = saved;
517
-
518
- result_tx. send ( result) . unwrap ( ) ;
499
+ let task = move || {
500
+ TLS . with_borrow_mut ( |tls| {
501
+ tls. update_change_tick ( ) ;
502
+ let saved = std:: mem:: replace ( & mut tls. last_tick , system_tick) ;
503
+ // we want to propagate to caller instead of panicking in the main thread
504
+ let result = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
505
+ ( f ( tls) , tls. curr_tick )
506
+ } ) ) ;
507
+ tls. last_tick = saved;
508
+ result_tx. send ( result) . unwrap ( ) ;
509
+ } ) ;
519
510
} ;
520
511
521
- let task: Box < dyn FnOnce ( & mut ThreadLocals ) + Send > = Box :: new ( task) ;
522
- let task: Box < dyn FnOnce ( & mut ThreadLocals ) + Send + ' static > =
523
- // SAFETY: This function will block the calling thread until `f` completes,
524
- // so any captured references in `f` will remain valid until then.
525
- unsafe { std:: mem:: transmute ( task) } ;
512
+ let task: Box < dyn FnOnce ( ) + Send > = Box :: new ( task) ;
513
+ // SAFETY: This function will block the calling thread until `f` completes,
514
+ // so any captured references in `f` will remain valid until then.
515
+ let task: Box < dyn FnOnce ( ) + Send + ' static > = unsafe { std:: mem:: transmute ( task) } ;
526
516
527
517
// Send task to the main thread.
528
518
sender
@@ -531,8 +521,8 @@ impl ThreadLocal<'_, '_> {
531
521
532
522
// Wait to receive result back from the main thread.
533
523
match result_rx. recv ( ) . unwrap ( ) {
534
- Ok ( ( result, last_run ) ) => {
535
- * self . last_run = last_run ;
524
+ Ok ( ( result, tls_tick ) ) => {
525
+ * self . last_run = tls_tick ;
536
526
result
537
527
}
538
528
Err ( payload) => {
@@ -568,33 +558,17 @@ unsafe impl SystemParam for ThreadLocal<'_, '_> {
568
558
world : UnsafeWorldCell < ' world > ,
569
559
curr_tick : Tick ,
570
560
) -> Self :: Item < ' world , ' state > {
571
- let mut accessor = crate :: system:: ResMut :: < ThreadLocalChannel > :: get_param (
561
+ let accessor = crate :: system:: ResMut :: < ThreadLocalChannel > :: get_param (
572
562
& mut state. component_id ,
573
563
system_meta,
574
564
world,
575
565
curr_tick,
576
566
) ;
577
567
578
- let access = if std:: thread:: current ( ) . id ( ) == accessor. owning_thread {
579
- let arc = accessor
580
- . direct
581
- . upgrade ( )
582
- . expect ( "pointer to `ThreadLocals` should be valid" ) ;
583
-
584
- // Use a raw pointer so we can hold onto the `Arc` and satisfy the borrow checker.
585
- let ptr = Arc :: as_ptr ( & arc) ;
586
-
587
- // SAFETY: This pointer is valid since we're still holding the `Arc`.
588
- let mutex = unsafe { & * ptr } ;
589
- let guard = mutex
590
- . try_lock ( )
591
- . expect ( "lock on `ThreadLocals` should be available" ) ;
592
-
593
- ThreadLocalAccess :: Direct ( ThreadLocalsGuard { guard, _arc : arc } )
568
+ let access = if std:: thread:: current ( ) . id ( ) == accessor. thread {
569
+ ThreadLocalAccess :: Direct
594
570
} else {
595
- let ptr: * mut dyn ThreadLocalTaskSender = & mut * accessor. indirect ;
596
- // SAFETY: The pointer is valid. We have to do this to satisfy the borrow checker.
597
- ThreadLocalAccess :: Indirect ( unsafe { & mut * ptr } )
571
+ ThreadLocalAccess :: Indirect ( & mut * accessor. into_inner ( ) . sender )
598
572
} ;
599
573
600
574
ThreadLocal {
0 commit comments