Skip to content

Commit 2b6fce2

Browse files
authored
Merge pull request #377 from lilizoey/fix/unsafe-memory-functions
Improve API soundness wrt reference counted `Gd`
2 parents bdb243f + e031e8e commit 2b6fce2

File tree

2 files changed

+19
-6
lines changed

2 files changed

+19
-6
lines changed

godot-core/src/obj/gd.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,8 @@ impl<T: GodotClass> Drop for Gd<T> {
684684
// No-op for manually managed objects
685685

686686
out!("Gd::drop <{}>", std::any::type_name::<T>());
687-
let is_last = T::Mem::maybe_dec_ref(self); // may drop
687+
// SAFETY: This `Gd` wont be dropped again after this.
688+
let is_last = unsafe { T::Mem::maybe_dec_ref(self) }; // may drop
688689
if is_last {
689690
unsafe {
690691
interface_fn!(object_destroy)(self.obj_sys());

godot-core/src/obj/traits.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,21 @@ pub mod mem {
320320
#[doc(hidden)]
321321
fn maybe_inc_ref<T: GodotClass>(obj: &Gd<T>);
322322

323-
/// If ref-counted, then decrement count
323+
/// If ref-counted, then decrement count. Returns `true` if the count hit 0 and the object can be
324+
/// safely freed.
325+
///
326+
/// This behavior can be overriden by a script, making it possible for the function to return `false`
327+
/// even when the reference count hits 0. This is meant to be used to have a separate reference count
328+
/// from Godot's internal reference count, or otherwise stop the object from being freed when the
329+
/// reference count hits 0.
330+
///
331+
/// # Safety
332+
///
333+
/// If this method is used on a [`Gd`] that inherits from [`RefCounted`](crate::engine::RefCounted)
334+
/// then the reference count must either be incremented before it hits 0, or some [`Gd`] referencing
335+
/// this object must be forgotten.
324336
#[doc(hidden)]
325-
fn maybe_dec_ref<T: GodotClass>(obj: &Gd<T>) -> bool;
337+
unsafe fn maybe_dec_ref<T: GodotClass>(obj: &Gd<T>) -> bool;
326338

327339
/// Check if ref-counted, return `None` if information is not available (dynamic and obj dead)
328340
#[doc(hidden)]
@@ -362,7 +374,7 @@ pub mod mem {
362374
});
363375
}
364376

365-
fn maybe_dec_ref<T: GodotClass>(obj: &Gd<T>) -> bool {
377+
unsafe fn maybe_dec_ref<T: GodotClass>(obj: &Gd<T>) -> bool {
366378
out!(" Stat::dec <{}>", std::any::type_name::<T>());
367379
obj.as_ref_counted(|refc| {
368380
let is_last = refc.unreference();
@@ -407,7 +419,7 @@ pub mod mem {
407419
}
408420
}
409421

410-
fn maybe_dec_ref<T: GodotClass>(obj: &Gd<T>) -> bool {
422+
unsafe fn maybe_dec_ref<T: GodotClass>(obj: &Gd<T>) -> bool {
411423
out!(" Dyn::dec <{}>", std::any::type_name::<T>());
412424
if obj
413425
.instance_id_or_none()
@@ -435,7 +447,7 @@ pub mod mem {
435447
impl Memory for ManualMemory {
436448
fn maybe_init_ref<T: GodotClass>(_obj: &Gd<T>) {}
437449
fn maybe_inc_ref<T: GodotClass>(_obj: &Gd<T>) {}
438-
fn maybe_dec_ref<T: GodotClass>(_obj: &Gd<T>) -> bool {
450+
unsafe fn maybe_dec_ref<T: GodotClass>(_obj: &Gd<T>) -> bool {
439451
false
440452
}
441453
fn is_ref_counted<T: GodotClass>(_obj: &Gd<T>) -> Option<bool> {

0 commit comments

Comments
 (0)