Skip to content

Commit dd23b53

Browse files
committed
Allow Deref/DerefMut for all Gd<T> instead of base/base_mut
More uniform and should be safe.
1 parent f7dd12b commit dd23b53

File tree

8 files changed

+84
-87
lines changed

8 files changed

+84
-87
lines changed

examples/dodge-the-creeps/rust/src/main_scene.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,12 @@ impl Main {
100100
rng.gen_range(mob.min_speed..mob.max_speed)
101101
};
102102

103-
let mut mob = mob.base_mut();
104103
mob.set_linear_velocity(Vector2::new(range, 0.0));
105104
let lin_vel = mob.get_linear_velocity().rotated(real::from_f32(direction));
106105
mob.set_linear_velocity(lin_vel);
107106

108107
let mut hud = self.base.get_node_as::<Hud>("Hud");
109-
hud.base_mut().connect(
108+
hud.connect(
110109
"start_game".into(),
111110
Callable::from_object_method(mob, "on_start_game"),
112111
);

godot-core/src/obj/gd.rs

Lines changed: 12 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -160,36 +160,6 @@ where
160160
GdMut::from_cell(self.storage().get_mut())
161161
}
162162

163-
/// Access base without locking the user object.
164-
///
165-
/// If you need mutations, use [`base_mut()`][Self::base_mut]. If you need a value copy, use [`base().share()`][Share::share].
166-
pub fn base(&self) -> &Gd<T::Base> {
167-
self.storage().base().deref()
168-
}
169-
170-
/// Access base mutably without locking the user object.
171-
///
172-
/// The idea is to support the `gd.base_mut().mutating_method()` pattern, which is not allowed with `gd.base()`.
173-
/// If you need a copy of a `Gd` object, use `base().share()`.
174-
///
175-
/// The return type is currently `Gd` and not `&mut Gd` because of `&mut` aliasing restrictions. This may change in the future.
176-
pub fn base_mut(&mut self) -> Gd<T::Base> {
177-
// Note: we cannot give safe mutable access to a base without RefCell, because multiple Gd pointers could call base_mut(),
178-
// leading to aliased &mut. And we don't want RefCell, as C++ objects (nodes etc.) don't underly Rust's exclusive-ref limitations.
179-
// The whole point of the Gd::base*() methods are to not require runtime-exclusive access to the Rust object.
180-
//
181-
// This is not a problem when accessing the `#[base]` field of a user struct directly, because `self` is guarded by the
182-
// RefCell/RwLock in the InstanceStorage.
183-
//
184-
// Here, we instead return a copy (for now), which for the user looks mostly the same. The idea is that:
185-
// - gd.base().mutating_method() fails
186-
// - gd.base_mut().mutating_method() works.
187-
//
188-
// Downside: small ref-counting overhead for `RefCounted` types. If this is an issue in a real game (highly unlikely),
189-
// the user could always cache Gd base pointers.
190-
self.storage().base().share()
191-
}
192-
193163
/// Storage object associated with the extension instance.
194164
pub(crate) fn storage(&self) -> &InstanceStorage<T> {
195165
// SAFETY: instance pointer belongs to this instance. We only get a shared reference, no exclusive access, so even
@@ -541,16 +511,16 @@ where
541511
}
542512
}
543513

544-
impl<T> Deref for Gd<T>
545-
where
546-
T: GodotClass<Declarer = dom::EngineDomain>,
547-
{
548-
type Target = T;
514+
impl<T: GodotClass> Deref for Gd<T> {
515+
// Target is always an engine class:
516+
// * if T is an engine class => T
517+
// * if T is a user class => T::Base
518+
type Target = <<T as GodotClass>::Declarer as dom::Domain>::DerefTarget<T>;
549519

550520
fn deref(&self) -> &Self::Target {
551521
// SAFETY:
552522
//
553-
// This relies on `Gd<Node3D>` having the layout as `Node3D` (as an example),
523+
// This relies on `Gd<Node3D>.opaque` having the layout as `Node3D` (as an example),
554524
// which also needs #[repr(transparent)]:
555525
//
556526
// struct Gd<T: GodotClass> {
@@ -560,22 +530,21 @@ where
560530
// struct Node3D {
561531
// object_ptr: sys::GDExtensionObjectPtr,
562532
// }
563-
unsafe { std::mem::transmute::<&OpaqueObject, &T>(&self.opaque) }
533+
unsafe { std::mem::transmute::<&OpaqueObject, &Self::Target>(&self.opaque) }
564534
}
565535
}
566536

567-
impl<T> DerefMut for Gd<T>
568-
where
569-
T: GodotClass<Declarer = dom::EngineDomain>,
570-
{
571-
fn deref_mut(&mut self) -> &mut T {
537+
impl<T: GodotClass> DerefMut for Gd<T> {
538+
fn deref_mut(&mut self) -> &mut Self::Target {
572539
// SAFETY: see also Deref
573540
//
574541
// The resulting `&mut T` is transmuted from `&mut OpaqueObject`, i.e. a *pointer* to the `opaque` field.
575542
// `opaque` itself has a different *address* for each Gd instance, meaning that two simultaneous
576543
// DerefMut borrows on two Gd instances will not alias, *even if* the underlying Godot object is the
577544
// same (i.e. `opaque` has the same value, but not address).
578-
unsafe { std::mem::transmute::<&mut OpaqueObject, &mut T>(&mut self.opaque) }
545+
//
546+
// The `&mut self` guarantees that no other base access can take place for *the same Gd instance* (access to other Gds is OK).
547+
unsafe { std::mem::transmute::<&mut OpaqueObject, &mut Self::Target>(&mut self.opaque) }
579548
}
580549
}
581550

godot-core/src/obj/traits.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,8 @@ pub mod dom {
260260

261261
/// Trait that specifies who declares a given `GodotClass`.
262262
pub trait Domain: Sealed {
263+
type DerefTarget<T: GodotClass>;
264+
263265
#[doc(hidden)]
264266
fn scoped_mut<T, F, R>(obj: &mut Gd<T>, closure: F) -> R
265267
where
@@ -271,6 +273,8 @@ pub mod dom {
271273
pub enum EngineDomain {}
272274
impl Sealed for EngineDomain {}
273275
impl Domain for EngineDomain {
276+
type DerefTarget<T: GodotClass> = T;
277+
274278
fn scoped_mut<T, F, R>(obj: &mut Gd<T>, closure: F) -> R
275279
where
276280
T: GodotClass<Declarer = EngineDomain>,
@@ -284,6 +288,8 @@ pub mod dom {
284288
pub enum UserDomain {}
285289
impl Sealed for UserDomain {}
286290
impl Domain for UserDomain {
291+
type DerefTarget<T: GodotClass> = T::Base;
292+
287293
fn scoped_mut<T, F, R>(obj: &mut Gd<T>, closure: F) -> R
288294
where
289295
T: GodotClass<Declarer = Self>,

godot-core/src/storage.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,17 @@ use std::any::type_name;
1212

1313
#[derive(Copy, Clone, Debug)]
1414
pub enum Lifecycle {
15+
// Warning: when reordering/changing enumerators, update match in AtomicLifecycle below
1516
Alive,
1617
Destroying,
1718
Dead, // reading this would typically already be too late, only best-effort in case of UB
1819
}
1920

2021
#[cfg(not(feature = "threads"))]
21-
pub use single_thread::*;
22+
pub(crate) use single_thread::*;
2223

2324
#[cfg(feature = "threads")]
24-
pub use multi_thread::*;
25+
pub(crate) use multi_thread::*;
2526

2627
#[cfg(not(feature = "threads"))]
2728
mod single_thread {
@@ -115,21 +116,46 @@ mod single_thread {
115116
#[cfg(feature = "threads")]
116117
mod multi_thread {
117118
use std::any::type_name;
119+
use std::sync;
118120
use std::sync::atomic::{AtomicU32, Ordering};
119-
use std::{cell, sync};
120121

121122
use crate::obj::{Base, GodotClass};
122-
use crate::{out, sys};
123+
use crate::out;
123124

124125
use super::Lifecycle;
125126

127+
pub struct AtomicLifecycle {
128+
atomic: AtomicU32,
129+
}
130+
131+
impl AtomicLifecycle {
132+
pub fn new(value: Lifecycle) -> Self {
133+
Self {
134+
atomic: AtomicU32::new(value as u32),
135+
}
136+
}
137+
138+
pub fn get(&self) -> Lifecycle {
139+
match self.atomic.load(Ordering::Relaxed) {
140+
0 => Lifecycle::Alive,
141+
1 => Lifecycle::Dead,
142+
2 => Lifecycle::Destroying,
143+
other => panic!("Invalid lifecycle {other}"),
144+
}
145+
}
146+
147+
pub fn set(&self, value: Lifecycle) {
148+
self.atomic.store(value as u32, Ordering::Relaxed);
149+
}
150+
}
151+
126152
/// Manages storage and lifecycle of user's extension class instances.
127153
pub struct InstanceStorage<T: GodotClass> {
128154
user_instance: sync::RwLock<T>,
129155
cached_base: Base<T::Base>,
130156

131157
// Declared after `user_instance`, is dropped last
132-
pub lifecycle: cell::Cell<Lifecycle>,
158+
pub lifecycle: AtomicLifecycle,
133159
godot_ref_count: AtomicU32,
134160
}
135161

@@ -141,12 +167,12 @@ mod multi_thread {
141167
Self {
142168
user_instance: sync::RwLock::new(user_instance),
143169
cached_base,
144-
lifecycle: cell::Cell::new(Lifecycle::Alive),
170+
lifecycle: AtomicLifecycle::new(Lifecycle::Alive),
145171
godot_ref_count: AtomicU32::new(1),
146172
}
147173
}
148174

149-
pub(crate) fn on_inc_ref(&mut self) {
175+
pub(crate) fn on_inc_ref(&self) {
150176
self.godot_ref_count.fetch_add(1, Ordering::Relaxed);
151177
out!(
152178
" Storage::on_inc_ref (rc={}) <{}>", // -- {:?}",
@@ -156,7 +182,7 @@ mod multi_thread {
156182
);
157183
}
158184

159-
pub(crate) fn on_dec_ref(&mut self) {
185+
pub(crate) fn on_dec_ref(&self) {
160186
self.godot_ref_count.fetch_sub(1, Ordering::Relaxed);
161187
out!(
162188
" | Storage::on_dec_ref (rc={}) <{}>", // -- {:?}",
@@ -195,7 +221,15 @@ mod multi_thread {
195221
pub(super) fn godot_ref_count(&self) -> u32 {
196222
self.godot_ref_count.load(Ordering::Relaxed)
197223
}
224+
225+
// TODO enable once there is better thread support
226+
//fn __static_type_check() {
227+
// enforce_sync::<InstanceStorage<T>>();
228+
//}
198229
}
230+
231+
// Make sure storage is Sync in multi-threaded case, as it can be concurrently accessed through aliased Gd<T> pointers.
232+
//fn enforce_sync<T: Sync>() {}
199233
}
200234

201235
impl<T: GodotClass> InstanceStorage<T> {
@@ -267,6 +301,9 @@ pub unsafe fn destroy_storage<T: GodotClass>(instance_ptr: sys::GDExtensionClass
267301
let _drop = Box::from_raw(instance_ptr as *mut InstanceStorage<T>);
268302
}
269303

304+
// ----------------------------------------------------------------------------------------------------------------------------------------------
305+
// Callbacks
306+
270307
pub fn nop_instance_callbacks() -> sys::GDExtensionInstanceBindingCallbacks {
271308
// These could also be null pointers, if they are definitely not invoked (e.g. create_callback only passed to object_get_instance_binding(),
272309
// when there is already a binding). Current "empty but not null" impl corresponds to godot-cpp (wrapped.hpp).

itest/rust/src/base_test.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,9 @@ fn base_instance_id() {
2727
fn base_access_unbound() {
2828
let mut obj = Gd::<Based>::new_default();
2929

30-
{
31-
let pos = Vector2::new(-5.5, 7.0);
32-
obj.base_mut().set_position(pos);
33-
34-
assert_eq!(obj.base().get_position(), pos);
35-
}
30+
let pos = Vector2::new(-5.5, 7.0);
31+
obj.set_position(pos);
32+
assert_eq!(obj.get_position(), pos);
3633

3734
obj.free();
3835
}
@@ -42,12 +39,9 @@ fn base_access_unbound() {
4239
fn base_access_unbound_no_field() {
4340
let mut obj = Gd::<Baseless>::new_default();
4441

45-
{
46-
let pos = Vector2::new(-5.5, 7.0);
47-
obj.base_mut().set_position(pos);
48-
49-
assert_eq!(obj.base().get_position(), pos);
50-
}
42+
let pos = Vector2::new(-5.5, 7.0);
43+
obj.set_position(pos);
44+
assert_eq!(obj.get_position(), pos);
5145

5246
obj.free();
5347
}

itest/rust/src/object_test.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -763,9 +763,6 @@ pub mod object_test_gd {
763763
#[derive(GodotClass)]
764764
#[class(base=Object)]
765765
pub struct CustomConstructor {
766-
#[base]
767-
base: Base<Object>,
768-
769766
#[var]
770767
pub val: i64,
771768
}
@@ -774,7 +771,7 @@ pub mod object_test_gd {
774771
impl CustomConstructor {
775772
#[func]
776773
pub fn construct_object(val: i64) -> Gd<CustomConstructor> {
777-
Gd::with_base(|base| Self { base, val })
774+
Gd::with_base(|_base| Self { val })
778775
}
779776
}
780777
}
@@ -804,10 +801,7 @@ impl DoubleUse {
804801

805802
#[derive(GodotClass)]
806803
#[class(init, base=Object)]
807-
struct SignalEmitter {
808-
#[base]
809-
base: Base<Object>,
810-
}
804+
struct SignalEmitter {}
811805

812806
#[godot_api]
813807
impl SignalEmitter {

itest/rust/src/property_test.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,33 @@ use godot::{
1515
#[derive(GodotClass)]
1616
#[class(base=Node)]
1717
struct HasProperty {
18-
#[base]
19-
base: Base<Node>,
20-
2118
#[var]
2219
int_val: i32,
20+
2321
#[var(get = get_int_val_read)]
2422
int_val_read: i32,
23+
2524
#[var(set = set_int_val_write)]
2625
int_val_write: i32,
26+
2727
#[var(get = get_int_val_rw, set = set_int_val_rw)]
2828
int_val_rw: i32,
29+
2930
#[var(get = get_int_val_getter, set)]
3031
int_val_getter: i32,
32+
3133
#[var(get, set = set_int_val_setter)]
3234
int_val_setter: i32,
3335

3436
#[var(get = get_string_val, set = set_string_val)]
3537
string_val: GodotString,
38+
3639
#[var(get = get_object_val, set = set_object_val)]
3740
object_val: Option<Gd<Object>>,
41+
3842
#[var]
3943
texture_val: Gd<Texture>,
44+
4045
#[var(get = get_texture_val, set = set_texture_val, hint = PROPERTY_HINT_RESOURCE_TYPE, hint_string = "Texture")]
4146
texture_val_rw: Option<Gd<Texture>>,
4247
}
@@ -120,7 +125,7 @@ impl HasProperty {
120125

121126
#[godot_api]
122127
impl NodeVirtual for HasProperty {
123-
fn init(base: Base<Node>) -> Self {
128+
fn init(_base: Base<Node>) -> Self {
124129
HasProperty {
125130
int_val: 0,
126131
int_val_read: 2,
@@ -132,7 +137,6 @@ impl NodeVirtual for HasProperty {
132137
string_val: GodotString::new(),
133138
texture_val: Texture::new(),
134139
texture_val_rw: None,
135-
base,
136140
}
137141
}
138142
}

itest/rust/src/signal_test.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ use crate::itest;
1717

1818
#[derive(GodotClass)]
1919
#[class(init, base=Object)]
20-
struct Emitter {
21-
#[base]
22-
base: Base<Object>,
23-
}
20+
struct Emitter {}
2421

2522
#[godot_api]
2623
impl Emitter {
@@ -81,11 +78,8 @@ fn signals() {
8178
let signal_name = format!("signal_{i}_arg");
8279
let receiver_name = format!("receive_{i}_arg");
8380

84-
emitter
85-
.base_mut()
86-
.connect(signal_name.clone().into(), receiver.callable(receiver_name));
87-
88-
emitter.base_mut().emit_signal(signal_name.into(), arg);
81+
emitter.connect(signal_name.clone().into(), receiver.callable(receiver_name));
82+
emitter.emit_signal(signal_name.into(), arg);
8983

9084
assert!(receiver.bind().used[i].get());
9185
}

0 commit comments

Comments
 (0)