Skip to content

Commit

Permalink
Fix refcount of callables being erroneously decremented when received…
Browse files Browse the repository at this point in the history
… as argument
  • Loading branch information
lilizoey committed Oct 13, 2023
1 parent a92164b commit cd82009
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 1 deletion.
17 changes: 16 additions & 1 deletion godot-core/src/builtin/callable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@ impl Callable {
pub fn as_inner(&self) -> inner::InnerCallable {
inner::InnerCallable::from_outer(self)
}

fn inc_ref(&self) {
std::mem::forget(self.clone())
}
}

impl_builtin_traits! {
Expand All @@ -270,7 +274,18 @@ impl_builtin_traits! {
// The `opaque` in `Callable` is just a pair of pointers, and requires no special initialization or cleanup
// beyond what is done in `from_opaque` and `drop`. So using `*mut Opaque` is safe.
unsafe impl GodotFfi for Callable {
ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; .. }
ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque;
fn from_sys;
fn sys;
fn from_sys_init;
fn move_return_ptr;
}

unsafe fn from_arg_ptr(ptr: sys::GDExtensionTypePtr, _call_type: sys::PtrcallType) -> Self {
let callable = Self::from_sys(ptr);
callable.inc_ref();
callable
}

unsafe fn from_sys_init_default(init_fn: impl FnOnce(sys::GDExtensionTypePtr)) -> Self {
let mut result = Self::invalid();
Expand Down
12 changes: 12 additions & 0 deletions itest/godot/ManualFfiTests.gd
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,15 @@ func test_gd_self_reference_succeeds():
gd_self_reference.update_internal_signal.connect(update_self_reference)

assert_eq(gd_self_reference.succeed_at_updating_internal_value(10), 10)

func sample_func():
pass

func test_callable_refcount():
var test_obj: CallableRefcountTest = CallableRefcountTest.new()
for i in range(10):
var method := Callable(self, "sample_func")
test_obj.accept_callable(method)
var method := Callable(self, "sample_func")
assert(method.is_valid())
test_obj.free()
12 changes: 12 additions & 0 deletions itest/rust/src/builtin_tests/containers/callable_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,18 @@ fn callable_call_engine() {
obj.free();
}

// Testing https://github.com/godot-rust/gdext/issues/410

#[derive(GodotClass)]
#[class(init, base = Node)]
pub struct CallableRefcountTest {}

#[godot_api]
impl CallableRefcountTest {
#[func]
fn accept_callable(&self, _call: Callable) {}
}

// ----------------------------------------------------------------------------------------------------------------------------------------------
// Tests and infrastructure for custom callables

Expand Down

0 comments on commit cd82009

Please sign in to comment.