Skip to content

Handle object destruction during Godot->Rust call - #1671

Open
Bromeon wants to merge 3 commits into
masterfrom
bugfix/dead-object-in-calls
Open

Handle object destruction during Godot->Rust call #1671
Bromeon wants to merge 3 commits into
masterfrom
bugfix/dead-object-in-calls

Conversation

@Bromeon

@Bromeon Bromeon commented Aug 2, 2026

Copy link
Copy Markdown
Member

For RefCounted objects, user code can drop the last ref to itself mid-call (e.g. a signal handler nulling the emitter), which previously freed the storage under the active instance.

I did not go the route of GDScript to add a strong-ref to every Godot->Rust call, for multiple reasons:

  • The problem of rugpulling the own instance still exists for manually managed objects. A panic makes it clear.
  • The extra refcounts cost around ~40% overhead of an empty function call. This is a lot, considering that most Godot->Rust calls never face the rugpulling problem.
  • There are still some edge cases that aren't even covered with the strong-ref approach (e.g. when refcount is already 0 during NOTIFICATION_PREDELETE)

Instead, the storage now counts "claims" on itself (Godot's + 1 per call on the stack), and is freed by whoever releases the last one. This is a bit more complex but retains the benefits of the old version.

Fixes #1666, now covered via itest signal_emitter_destroyed_during_own_call.

@Bromeon Bromeon added bug c: ffi Low-level components and interaction with GDExtension API labels Aug 2, 2026
@Bromeon
Bromeon enabled auto-merge August 2, 2026 13:28
@GodotRust

Copy link
Copy Markdown

API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-1671

@Bromeon
Bromeon force-pushed the bugfix/dead-object-in-calls branch from a3350a9 to 286c85d Compare August 2, 2026 15:29
@Bromeon
Bromeon disabled auto-merge August 2, 2026 15:30
@Bromeon Bromeon changed the title Defer storage destruction during Godot->Rust call Handle object destruction during Godot->Rust call Aug 3, 2026
@Bromeon
Bromeon force-pushed the bugfix/dead-object-in-calls branch from 286c85d to baa608c Compare August 3, 2026 22:02
@greenfox1505

Copy link
Copy Markdown
Contributor

This prevents the full engine crash, but I still get this error message:

ERROR: Destroyed the Godot object during a Godot -> Rust call on it.
  The engine may access the object after the call returns, which is undefined behavior. Do not drop the last
  reference to an object inside a call on that same object; defer it past the call instead.
  object: Base { id: -9223372001454848550, class: InkVariableWatcher, refc: 0 }
   at: godot_core::storage::instance_storage::report_destruction_during_call (/home/greenfox/.cargo/git/checkouts/gdext-067f4b88e7bd088f/baa608c/godot-core/src/storage/instance_storage.rs:378)
   GDScript backtrace (most recent call first):
       [0] continue_story (res://addons/gdrs_ink/long_story_example.tscn::GDScript_l4es0:39)
       [1] choice_made (res://addons/gdrs_ink/long_story_example.tscn::GDScript_l4es0:70)

@Bromeon

Bromeon commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

Yes, this is an upstream issue which I'm currently investigating. The error message makes it loud rather than obscure, but ideally we can address this in Godot itself.

User code can drop the last reference to the receiver mid-call, e.g. a
signal handler nulling the emitter, or free() it. The storage was then
destroyed under an active bind guard, which crashed the process.

The storage now counts claims: Godot holds one from construction to
destruction, each ongoing call holds one. Removing the last claim
deallocates. The Godot object itself still dies; only the Rust part is
kept alive.
The Rust side survives such a destruction, but the engine may still
dereference `this` after the callback returns, which is UB. Print an
error naming the class and the way out, once per class since the pattern
usually repeats.
@Bromeon
Bromeon force-pushed the bugfix/dead-object-in-calls branch from b29e95d to ccce181 Compare August 10, 2026 23:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug c: ffi Low-level components and interaction with GDExtension API

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dropping an RC'd reference within it's own signal causes a hard crash.

3 participants