Skip to content

Commit 0fce7ec

Browse files
committed
add thread local resources
1 parent 53d6d10 commit 0fce7ec

File tree

6 files changed

+287
-40
lines changed

6 files changed

+287
-40
lines changed

crates/bevy_app/src/app_builder.rs

+18
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,14 @@ impl AppBuilder {
227227
self
228228
}
229229

230+
pub fn add_thread_local_resource<T>(&mut self, resource: T) -> &mut Self
231+
where
232+
T: 'static,
233+
{
234+
self.app.resources.insert_thread_local(resource);
235+
self
236+
}
237+
230238
pub fn init_resource<R>(&mut self) -> &mut Self
231239
where
232240
R: FromResources + Send + Sync + 'static,
@@ -237,6 +245,16 @@ impl AppBuilder {
237245
self
238246
}
239247

248+
pub fn init_thread_local_resource<R>(&mut self) -> &mut Self
249+
where
250+
R: FromResources + 'static,
251+
{
252+
let resource = R::from_resources(&self.app.resources);
253+
self.app.resources.insert_thread_local(resource);
254+
255+
self
256+
}
257+
240258
pub fn set_runner(&mut self, run_fn: impl Fn(App) + 'static) -> &mut Self {
241259
self.app.runner = Box::new(run_fn);
242260
self

crates/bevy_ecs/hecs/src/borrow.rs

+6
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ use core::{
2222

2323
use crate::{archetype::Archetype, Component, MissingComponent};
2424

25+
/// Atomically enforces Rust-style borrow checking at runtime
2526
#[derive(Debug)]
2627
pub struct AtomicBorrow(AtomicUsize);
2728

2829
impl AtomicBorrow {
30+
/// Creates a new AtomicBorrow
2931
pub const fn new() -> Self {
3032
Self(AtomicUsize::new(0))
3133
}
3234

35+
/// Starts a new immutable borrow. This can be called any number of times
3336
pub fn borrow(&self) -> bool {
3437
let value = self.0.fetch_add(1, Ordering::Acquire).wrapping_add(1);
3538
if value == 0 {
@@ -44,18 +47,21 @@ impl AtomicBorrow {
4447
}
4548
}
4649

50+
/// Starts a new mutable borrow. This must be unique. It cannot be done in parallel with other borrows or borrow_muts
4751
pub fn borrow_mut(&self) -> bool {
4852
self.0
4953
.compare_exchange(0, UNIQUE_BIT, Ordering::Acquire, Ordering::Relaxed)
5054
.is_ok()
5155
}
5256

57+
/// Release an immutable borrow.
5358
pub fn release(&self) {
5459
let value = self.0.fetch_sub(1, Ordering::Release);
5560
debug_assert!(value != 0, "unbalanced release");
5661
debug_assert!(value & UNIQUE_BIT == 0, "shared release of unique borrow");
5762
}
5863

64+
/// Release a mutable borrow.
5965
pub fn release_mut(&self) {
6066
let value = self.0.fetch_and(!UNIQUE_BIT, Ordering::Release);
6167
debug_assert_ne!(value & UNIQUE_BIT, 0, "unique release of shared borrow");

crates/bevy_ecs/hecs/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ mod serde;
7676
mod world;
7777

7878
pub use archetype::{Archetype, TypeState};
79-
pub use borrow::{Ref, RefMut};
79+
pub use borrow::{AtomicBorrow, Ref, RefMut};
8080
pub use bundle::{Bundle, DynamicBundle, MissingComponent};
8181
pub use entities::{Entity, EntityReserver, Location, NoSuchEntity};
8282
pub use entity_builder::{BuiltEntity, EntityBuilder};

crates/bevy_ecs/src/resource/resources.rs

+242-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
use super::{FetchResource, ResourceQuery};
22
use crate::system::SystemId;
3-
use bevy_hecs::{Archetype, Entity, Ref, RefMut, TypeInfo, TypeState};
3+
use bevy_hecs::{Archetype, AtomicBorrow, Entity, Ref, RefMut, TypeInfo, TypeState};
44
use bevy_utils::HashMap;
55
use core::any::TypeId;
6-
use std::ptr::NonNull;
6+
use downcast_rs::{impl_downcast, Downcast};
7+
use std::{
8+
fmt::Debug,
9+
ops::{Deref, DerefMut},
10+
ptr::NonNull,
11+
thread::ThreadId,
12+
};
713

814
/// A Resource type
915
pub trait Resource: Send + Sync + 'static {}
@@ -22,17 +28,103 @@ pub enum ResourceIndex {
2228
System(SystemId),
2329
}
2430

31+
// TODO: consider using this for normal resources (would require change tracking)
32+
trait ResourceStorage: Downcast {}
33+
impl_downcast!(ResourceStorage);
34+
35+
struct StoredResource<T: 'static> {
36+
value: T,
37+
atomic_borrow: AtomicBorrow,
38+
}
39+
40+
pub struct VecResourceStorage<T: 'static> {
41+
stored: Vec<StoredResource<T>>,
42+
}
43+
44+
impl<T: 'static> VecResourceStorage<T> {
45+
fn get(&self, index: usize) -> Option<ResourceRef<'_, T>> {
46+
self.stored
47+
.get(index)
48+
.map(|stored| ResourceRef::new(&stored.value, &stored.atomic_borrow))
49+
}
50+
51+
fn get_mut(&self, index: usize) -> Option<ResourceRefMut<'_, T>> {
52+
self.stored.get(index).map(|stored|
53+
// SAFE: ResourceRefMut ensures that this borrow is unique
54+
unsafe {
55+
let value = &stored.value as *const T as *mut T;
56+
ResourceRefMut::new(&mut *value, &stored.atomic_borrow)
57+
})
58+
}
59+
60+
fn push(&mut self, resource: T) {
61+
self.stored.push(StoredResource {
62+
atomic_borrow: AtomicBorrow::new(),
63+
value: resource,
64+
})
65+
}
66+
67+
fn set(&mut self, index: usize, resource: T) {
68+
self.stored[index].value = resource;
69+
}
70+
71+
fn is_empty(&self) -> bool {
72+
self.stored.is_empty()
73+
}
74+
}
75+
76+
impl<T: 'static> Default for VecResourceStorage<T> {
77+
fn default() -> Self {
78+
Self {
79+
stored: Default::default(),
80+
}
81+
}
82+
}
83+
84+
impl<T: 'static> ResourceStorage for VecResourceStorage<T> {}
85+
2586
/// A collection of resource instances identified by their type.
26-
#[derive(Debug, Default)]
2787
pub struct Resources {
2888
pub(crate) resource_data: HashMap<TypeId, ResourceData>,
89+
thread_local_data: HashMap<TypeId, Box<dyn ResourceStorage>>,
90+
main_thread_id: ThreadId,
91+
}
92+
93+
impl Default for Resources {
94+
fn default() -> Self {
95+
Resources {
96+
resource_data: Default::default(),
97+
thread_local_data: Default::default(),
98+
main_thread_id: std::thread::current().id(),
99+
}
100+
}
29101
}
30102

31103
impl Resources {
32104
pub fn insert<T: Resource>(&mut self, resource: T) {
33105
self.insert_resource(resource, ResourceIndex::Global);
34106
}
35107

108+
pub fn insert_thread_local<T: 'static>(&mut self, resource: T) {
109+
self.check_thread_local();
110+
let entry = self
111+
.thread_local_data
112+
.entry(TypeId::of::<T>())
113+
.or_insert_with(|| Box::new(VecResourceStorage::<T>::default()));
114+
let resources = entry.downcast_mut::<VecResourceStorage<T>>().unwrap();
115+
if resources.is_empty() {
116+
resources.push(resource);
117+
} else {
118+
resources.set(0, resource);
119+
}
120+
}
121+
122+
fn check_thread_local(&self) {
123+
if std::thread::current().id() != self.main_thread_id {
124+
panic!("Attempted to access a thread local resource off of the main thread.")
125+
}
126+
}
127+
36128
pub fn contains<T: Resource>(&self) -> bool {
37129
self.get_resource::<T>(ResourceIndex::Global).is_some()
38130
}
@@ -45,6 +137,26 @@ impl Resources {
45137
self.get_resource_mut(ResourceIndex::Global)
46138
}
47139

140+
pub fn get_thread_local<T: 'static>(&self) -> Option<ResourceRef<'_, T>> {
141+
self.check_thread_local();
142+
self.thread_local_data
143+
.get(&TypeId::of::<T>())
144+
.and_then(|storage| {
145+
let resources = storage.downcast_ref::<VecResourceStorage<T>>().unwrap();
146+
resources.get(0)
147+
})
148+
}
149+
150+
pub fn get_thread_local_mut<T: 'static>(&self) -> Option<ResourceRefMut<'_, T>> {
151+
self.check_thread_local();
152+
self.thread_local_data
153+
.get(&TypeId::of::<T>())
154+
.and_then(|storage| {
155+
let resources = storage.downcast_ref::<VecResourceStorage<T>>().unwrap();
156+
resources.get_mut(0)
157+
})
158+
}
159+
48160
/// Returns a clone of the underlying resource, this is helpful when borrowing something
49161
/// cloneable (like a task pool) without taking a borrow on the resource map
50162
pub fn get_cloned<T: Resource + Clone>(&self) -> Option<T> {
@@ -281,6 +393,93 @@ where
281393
}
282394
}
283395

396+
/// Shared borrow of an entity's component
397+
#[derive(Clone)]
398+
pub struct ResourceRef<'a, T: 'static> {
399+
borrow: &'a AtomicBorrow,
400+
resource: &'a T,
401+
}
402+
403+
impl<'a, T: 'static> ResourceRef<'a, T> {
404+
/// Creates a new resource borrow
405+
pub fn new(resource: &'a T, borrow: &'a AtomicBorrow) -> Self {
406+
borrow.borrow();
407+
Self { resource, borrow }
408+
}
409+
}
410+
411+
unsafe impl<T: 'static> Send for ResourceRef<'_, T> {}
412+
unsafe impl<T: 'static> Sync for ResourceRef<'_, T> {}
413+
414+
impl<'a, T: 'static> Drop for ResourceRef<'a, T> {
415+
fn drop(&mut self) {
416+
self.borrow.release()
417+
}
418+
}
419+
420+
impl<'a, T: 'static> Deref for ResourceRef<'a, T> {
421+
type Target = T;
422+
423+
fn deref(&self) -> &T {
424+
self.resource
425+
}
426+
}
427+
428+
impl<'a, T: 'static> Debug for ResourceRef<'a, T>
429+
where
430+
T: Debug,
431+
{
432+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
433+
self.deref().fmt(f)
434+
}
435+
}
436+
437+
/// Unique borrow of a resource
438+
pub struct ResourceRefMut<'a, T: 'static> {
439+
borrow: &'a AtomicBorrow,
440+
resource: &'a mut T,
441+
}
442+
443+
impl<'a, T: 'static> ResourceRefMut<'a, T> {
444+
/// Creates a new entity component mutable borrow
445+
pub fn new(resource: &'a mut T, borrow: &'a AtomicBorrow) -> Self {
446+
borrow.borrow_mut();
447+
Self { resource, borrow }
448+
}
449+
}
450+
451+
unsafe impl<T: 'static> Send for ResourceRefMut<'_, T> {}
452+
unsafe impl<T: 'static> Sync for ResourceRefMut<'_, T> {}
453+
454+
impl<'a, T: 'static> Drop for ResourceRefMut<'a, T> {
455+
fn drop(&mut self) {
456+
self.borrow.release_mut();
457+
}
458+
}
459+
460+
impl<'a, T: 'static> Deref for ResourceRefMut<'a, T> {
461+
type Target = T;
462+
463+
fn deref(&self) -> &T {
464+
self.resource
465+
}
466+
}
467+
468+
impl<'a, T: 'static> DerefMut for ResourceRefMut<'a, T> {
469+
fn deref_mut(&mut self) -> &mut T {
470+
self.resource
471+
}
472+
}
473+
474+
impl<'a, T: 'static> Debug for ResourceRefMut<'a, T>
475+
where
476+
T: Debug,
477+
{
478+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
479+
self.deref().fmt(f)
480+
}
481+
}
482+
284483
#[cfg(test)]
285484
mod tests {
286485
use super::Resources;
@@ -335,4 +534,44 @@ mod tests {
335534
let _x = resources.get_mut::<i32>();
336535
let _y = resources.get_mut::<i32>();
337536
}
537+
538+
#[test]
539+
fn thread_local_resource() {
540+
let mut resources = Resources::default();
541+
resources.insert_thread_local(123i32);
542+
resources.insert_thread_local(456i64);
543+
assert_eq!(*resources.get_thread_local::<i32>().unwrap(), 123);
544+
assert_eq!(*resources.get_thread_local_mut::<i64>().unwrap(), 456);
545+
}
546+
547+
#[test]
548+
fn thread_local_resource_ref_aliasing() {
549+
let mut resources = Resources::default();
550+
resources.insert_thread_local(123i32);
551+
let a = resources.get_thread_local::<i32>().unwrap();
552+
let b = resources.get_thread_local::<i32>().unwrap();
553+
assert_eq!(*a, 123);
554+
assert_eq!(*b, 123);
555+
}
556+
557+
#[test]
558+
#[should_panic]
559+
fn thread_local_resource_mut_ref_aliasing() {
560+
let mut resources = Resources::default();
561+
resources.insert_thread_local(123i32);
562+
let _a = resources.get_thread_local::<i32>().unwrap();
563+
let _b = resources.get_thread_local_mut::<i32>().unwrap();
564+
}
565+
566+
#[test]
567+
#[should_panic]
568+
fn thread_local_resource_panic() {
569+
let mut resources = Resources::default();
570+
resources.insert_thread_local(0i32);
571+
std::thread::spawn(move || {
572+
let _ = resources.get_thread_local_mut::<i32>();
573+
})
574+
.join()
575+
.unwrap();
576+
}
338577
}

0 commit comments

Comments
 (0)