1
- use std:: { sync :: atomic :: AtomicBool , thread:: ThreadId } ;
1
+ use std:: thread:: ThreadId ;
2
2
3
3
use bevy:: {
4
4
ecs:: { component:: ComponentId , world:: unsafe_world_cell:: UnsafeWorldCell } ,
5
5
prelude:: Resource ,
6
6
} ;
7
7
use dashmap:: { DashMap , Entry } ;
8
+ use parking_lot:: RwLock ;
8
9
use smallvec:: SmallVec ;
9
10
10
11
use crate :: error:: InteropError ;
@@ -56,8 +57,14 @@ impl AccessCount {
56
57
}
57
58
}
58
59
60
+ /// For structs which can be mapped to a u64 index
59
61
pub trait AccessMapKey {
62
+ /// Convert the key to an index
63
+ ///
64
+ /// The key 0 must not be be used as it's reserved for global access
60
65
fn as_index ( & self ) -> u64 ;
66
+
67
+ /// Convert an index back to the original struct
61
68
fn from_index ( value : u64 ) -> Self ;
62
69
}
63
70
@@ -76,6 +83,7 @@ impl AccessMapKey for u64 {
76
83
pub enum ReflectAccessKind {
77
84
ComponentOrResource ,
78
85
Allocation ,
86
+ Global ,
79
87
}
80
88
81
89
/// Describes the id pointing to the base value we are accessing via reflection, for components and resources this is the ComponentId
@@ -88,31 +96,42 @@ pub struct ReflectAccessId {
88
96
89
97
impl AccessMapKey for ReflectAccessId {
90
98
fn as_index ( & self ) -> u64 {
91
- // project two linear non-negative ranges to a single linear non-negative range
92
- // y1 = 2x - 0
93
- // y2 = 2x - 1
99
+ // project two linear non-negative ranges to a single linear non-negative range, offset by 1 to avoid 0
100
+ // y1 = 2x - 0 + 1
101
+ // y2 = 2x - 1 + 1
94
102
match self . kind {
95
- ReflectAccessKind :: ComponentOrResource => self . id * 2 ,
96
- ReflectAccessKind :: Allocation => self . id * 2 + 1 ,
103
+ ReflectAccessKind :: ComponentOrResource => ( self . id * 2 ) + 1 ,
104
+ ReflectAccessKind :: Allocation => ( self . id * 2 ) + 1 ,
105
+ ReflectAccessKind :: Global => 0 ,
97
106
}
98
107
}
99
108
100
109
fn from_index ( value : u64 ) -> Self {
101
110
// retrieve the kind of range based on if the value is odd or even
102
111
// y1 if even, y2 if odd
103
112
// to retrieve value of x:
104
- // x1 = y / 2
105
- // x2 = (y - 1) / 2
106
- let ( kind, id) = if value % 2 == 0 {
107
- ( ReflectAccessKind :: ComponentOrResource , value / 2 )
113
+ // x1 = (y / 2) - 1
114
+ // x2 = ((y - 1) / 2) - 1
115
+
116
+ let ( kind, id) = if value == 0 {
117
+ ( ReflectAccessKind :: Global , 0 )
118
+ } else if value % 2 == 0 {
119
+ ( ReflectAccessKind :: ComponentOrResource , ( value / 2 ) - 1 )
108
120
} else {
109
- ( ReflectAccessKind :: Allocation , ( value - 1 ) / 2 )
121
+ ( ReflectAccessKind :: Allocation , ( ( value - 1 ) / 2 ) - 1 )
110
122
} ;
111
123
Self { kind, id }
112
124
}
113
125
}
114
126
115
127
impl ReflectAccessId {
128
+ pub fn for_global ( ) -> Self {
129
+ Self {
130
+ kind : ReflectAccessKind :: Global ,
131
+ id : 0 ,
132
+ }
133
+ }
134
+
116
135
pub fn for_resource < R : Resource > ( cell : & UnsafeWorldCell ) -> Result < Self , InteropError > {
117
136
let resource_id = cell. components ( ) . resource_id :: < R > ( ) . ok_or_else ( || {
118
137
InteropError :: unregistered_component_or_resource_type ( std:: any:: type_name :: < R > ( ) )
@@ -190,16 +209,27 @@ impl From<ReflectAccessId> for ReflectAllocationId {
190
209
#[ derive( Debug , Default ) ]
191
210
pub struct AccessMap {
192
211
individual_accesses : DashMap < u64 , AccessCount > ,
193
- global_lock : AtomicBool ,
212
+ global_lock : RwLock < AccessCount > ,
194
213
}
195
214
196
215
impl AccessMap {
216
+ pub fn is_locked_exclusively ( & self ) -> bool {
217
+ let global_lock = self . global_lock . read ( ) ;
218
+ !global_lock. can_write ( )
219
+ }
220
+
221
+ pub fn global_access_location ( & self ) -> Option < std:: panic:: Location < ' static > > {
222
+ let global_lock = self . global_lock . read ( ) ;
223
+ global_lock. as_location ( )
224
+ }
225
+
197
226
/// Tries to claim read access, will return false if somebody else is writing to the same key, or holding a global lock
198
227
#[ track_caller]
199
228
pub fn claim_read_access < K : AccessMapKey > ( & self , key : K ) -> bool {
200
- if self . global_lock . load ( std :: sync :: atomic :: Ordering :: Relaxed ) {
229
+ if self . is_locked_exclusively ( ) {
201
230
return false ;
202
231
}
232
+
203
233
let key = key. as_index ( ) ;
204
234
let access = self . individual_accesses . try_entry ( key) ;
205
235
match access. map ( Entry :: or_default) {
@@ -217,7 +247,7 @@ impl AccessMap {
217
247
#[ track_caller]
218
248
/// Tries to claim write access, will return false if somebody else is reading or writing to the same key, or holding a global lock
219
249
pub fn claim_write_access < K : AccessMapKey > ( & self , key : K ) -> bool {
220
- if self . global_lock . load ( std :: sync :: atomic :: Ordering :: Relaxed ) {
250
+ if self . is_locked_exclusively ( ) {
221
251
return false ;
222
252
}
223
253
let key = key. as_index ( ) ;
@@ -237,17 +267,19 @@ impl AccessMap {
237
267
238
268
/// Tries to claim global access. This type of access prevents any other access from happening simulatenously
239
269
/// Will return false if anybody else is currently accessing any part of the map
270
+ #[ track_caller]
240
271
pub fn claim_global_access ( & self ) -> bool {
241
- self . individual_accesses . is_empty ( )
242
- && self
243
- . global_lock
244
- . compare_exchange (
245
- false ,
246
- true ,
247
- std:: sync:: atomic:: Ordering :: Relaxed ,
248
- std:: sync:: atomic:: Ordering :: Relaxed ,
249
- )
250
- . is_ok ( )
272
+ let mut global_lock = self . global_lock . write ( ) ;
273
+
274
+ if !self . individual_accesses . is_empty ( ) || !global_lock. can_write ( ) {
275
+ return false ;
276
+ }
277
+ global_lock. read_by . push ( ClaimOwner {
278
+ id : std:: thread:: current ( ) . id ( ) ,
279
+ location : * std:: panic:: Location :: caller ( ) ,
280
+ } ) ;
281
+ global_lock. written = true ;
282
+ true
251
283
}
252
284
253
285
/// Releases an access
@@ -278,8 +310,15 @@ impl AccessMap {
278
310
279
311
/// Releases a global access
280
312
pub fn release_global_access ( & self ) {
281
- self . global_lock
282
- . store ( false , std:: sync:: atomic:: Ordering :: Relaxed ) ;
313
+ let mut global_lock = self . global_lock . write ( ) ;
314
+ global_lock. written = false ;
315
+ if let Some ( p) = global_lock. read_by . pop ( ) {
316
+ assert ! (
317
+ p. id == std:: thread:: current( ) . id( ) ,
318
+ "Access released from wrong thread, claimed at {}" ,
319
+ p. location. display_location( )
320
+ ) ;
321
+ }
283
322
}
284
323
285
324
pub fn list_accesses < K : AccessMapKey > ( & self ) -> Vec < ( K , AccessCount ) > {
@@ -295,14 +334,17 @@ impl AccessMap {
295
334
296
335
pub fn release_all_accesses ( & self ) {
297
336
self . individual_accesses . clear ( ) ;
298
- self . global_lock
299
- . store ( false , std:: sync:: atomic:: Ordering :: Relaxed ) ;
337
+ self . release_global_access ( ) ;
300
338
}
301
339
302
340
pub fn access_location < K : AccessMapKey > (
303
341
& self ,
304
342
key : K ,
305
343
) -> Option < std:: panic:: Location < ' static > > {
344
+ if key. as_index ( ) == 0 {
345
+ return self . global_access_location ( ) ;
346
+ }
347
+
306
348
self . individual_accesses
307
349
. try_get ( & key. as_index ( ) )
308
350
. try_unwrap ( )
@@ -371,18 +413,17 @@ macro_rules! with_access_write {
371
413
macro_rules! with_global_access {
372
414
( $access_map: expr, $msg: expr, $body: block) => {
373
415
if !$access_map. claim_global_access( ) {
374
- panic!(
375
- "{}. Another access is held somewhere else preventing locking the world: {}" ,
416
+ Err ( $crate:: error:: InteropError :: cannot_claim_access(
417
+ $crate:: bindings:: access_map:: ReflectAccessId :: for_global( ) ,
418
+ $access_map
419
+ . access_location( $crate:: bindings:: access_map:: ReflectAccessId :: for_global( ) ) ,
376
420
$msg,
377
- $crate:: bindings:: access_map:: DisplayCodeLocation :: display_location(
378
- $access_map. access_first_location( )
379
- )
380
- ) ;
421
+ ) )
381
422
} else {
382
423
#[ allow( clippy:: redundant_closure_call) ]
383
424
let result = ( || $body) ( ) ;
384
425
$access_map. release_global_access( ) ;
385
- result
426
+ Ok ( result)
386
427
}
387
428
} ;
388
429
}
0 commit comments