Skip to content

Commit b5068e5

Browse files
committed
remove more panics in macros
1 parent a45688a commit b5068e5

File tree

4 files changed

+117
-104
lines changed

4 files changed

+117
-104
lines changed

crates/bevy_mod_scripting_core/src/bindings/access_map.rs

Lines changed: 76 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
use std::{sync::atomic::AtomicBool, thread::ThreadId};
1+
use std::thread::ThreadId;
22

33
use bevy::{
44
ecs::{component::ComponentId, world::unsafe_world_cell::UnsafeWorldCell},
55
prelude::Resource,
66
};
77
use dashmap::{DashMap, Entry};
8+
use parking_lot::RwLock;
89
use smallvec::SmallVec;
910

1011
use crate::error::InteropError;
@@ -56,8 +57,14 @@ impl AccessCount {
5657
}
5758
}
5859

60+
/// For structs which can be mapped to a u64 index
5961
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
6065
fn as_index(&self) -> u64;
66+
67+
/// Convert an index back to the original struct
6168
fn from_index(value: u64) -> Self;
6269
}
6370

@@ -76,6 +83,7 @@ impl AccessMapKey for u64 {
7683
pub enum ReflectAccessKind {
7784
ComponentOrResource,
7885
Allocation,
86+
Global,
7987
}
8088

8189
/// 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 {
8896

8997
impl AccessMapKey for ReflectAccessId {
9098
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
94102
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,
97106
}
98107
}
99108

100109
fn from_index(value: u64) -> Self {
101110
// retrieve the kind of range based on if the value is odd or even
102111
// y1 if even, y2 if odd
103112
// 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)
108120
} else {
109-
(ReflectAccessKind::Allocation, (value - 1) / 2)
121+
(ReflectAccessKind::Allocation, ((value - 1) / 2) - 1)
110122
};
111123
Self { kind, id }
112124
}
113125
}
114126

115127
impl ReflectAccessId {
128+
pub fn for_global() -> Self {
129+
Self {
130+
kind: ReflectAccessKind::Global,
131+
id: 0,
132+
}
133+
}
134+
116135
pub fn for_resource<R: Resource>(cell: &UnsafeWorldCell) -> Result<Self, InteropError> {
117136
let resource_id = cell.components().resource_id::<R>().ok_or_else(|| {
118137
InteropError::unregistered_component_or_resource_type(std::any::type_name::<R>())
@@ -190,16 +209,27 @@ impl From<ReflectAccessId> for ReflectAllocationId {
190209
#[derive(Debug, Default)]
191210
pub struct AccessMap {
192211
individual_accesses: DashMap<u64, AccessCount>,
193-
global_lock: AtomicBool,
212+
global_lock: RwLock<AccessCount>,
194213
}
195214

196215
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+
197226
/// Tries to claim read access, will return false if somebody else is writing to the same key, or holding a global lock
198227
#[track_caller]
199228
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() {
201230
return false;
202231
}
232+
203233
let key = key.as_index();
204234
let access = self.individual_accesses.try_entry(key);
205235
match access.map(Entry::or_default) {
@@ -217,7 +247,7 @@ impl AccessMap {
217247
#[track_caller]
218248
/// Tries to claim write access, will return false if somebody else is reading or writing to the same key, or holding a global lock
219249
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() {
221251
return false;
222252
}
223253
let key = key.as_index();
@@ -237,17 +267,19 @@ impl AccessMap {
237267

238268
/// Tries to claim global access. This type of access prevents any other access from happening simulatenously
239269
/// Will return false if anybody else is currently accessing any part of the map
270+
#[track_caller]
240271
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
251283
}
252284

253285
/// Releases an access
@@ -278,8 +310,15 @@ impl AccessMap {
278310

279311
/// Releases a global access
280312
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+
}
283322
}
284323

285324
pub fn list_accesses<K: AccessMapKey>(&self) -> Vec<(K, AccessCount)> {
@@ -295,14 +334,17 @@ impl AccessMap {
295334

296335
pub fn release_all_accesses(&self) {
297336
self.individual_accesses.clear();
298-
self.global_lock
299-
.store(false, std::sync::atomic::Ordering::Relaxed);
337+
self.release_global_access();
300338
}
301339

302340
pub fn access_location<K: AccessMapKey>(
303341
&self,
304342
key: K,
305343
) -> Option<std::panic::Location<'static>> {
344+
if key.as_index() == 0 {
345+
return self.global_access_location();
346+
}
347+
306348
self.individual_accesses
307349
.try_get(&key.as_index())
308350
.try_unwrap()
@@ -371,18 +413,17 @@ macro_rules! with_access_write {
371413
macro_rules! with_global_access {
372414
($access_map:expr, $msg:expr, $body:block) => {
373415
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()),
376420
$msg,
377-
$crate::bindings::access_map::DisplayCodeLocation::display_location(
378-
$access_map.access_first_location()
379-
)
380-
);
421+
))
381422
} else {
382423
#[allow(clippy::redundant_closure_call)]
383424
let result = (|| $body)();
384425
$access_map.release_global_access();
385-
result
426+
Ok(result)
386427
}
387428
};
388429
}

crates/bevy_mod_scripting_core/src/bindings/pretty_print.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ impl DisplayWithWorld for ReflectAccessId {
394394
super::access_map::ReflectAccessKind::Allocation => {
395395
format!("Allocation({})", self.id)
396396
}
397+
super::access_map::ReflectAccessKind::Global => "Global".to_owned(),
397398
}
398399
}
399400

@@ -410,6 +411,7 @@ impl DisplayWithWorld for ReflectAccessId {
410411
let type_id = allocator.get_type_id(&allocation_id).or_fake_id();
411412
format!("Allocation({})", type_id.display_with_world(world))
412413
}
414+
super::access_map::ReflectAccessKind::Global => "Global".to_owned(),
413415
}
414416
}
415417
}

crates/bevy_mod_scripting_core/src/bindings/query.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,6 @@ impl WorldAccessGuard<'_> {
208208
}
209209
})
210210
.collect())
211-
})
211+
})?
212212
}
213213
}

0 commit comments

Comments
 (0)