Add NOTIFICATION_PREDELETE_CLEANUP notification

New notification sent after `NOTIFICATION_PREDELETE` to let Objects cleanup at the very end, it should be the last notification sent.
This commit is contained in:
Raul Santos 2023-10-20 13:43:42 +02:00
parent f8818f85e6
commit 9750e49c57
No known key found for this signature in database
GPG Key ID: B532473AE3A803E4
3 changed files with 20 additions and 10 deletions

View File

@ -198,6 +198,7 @@ bool Object::_predelete() {
notification(NOTIFICATION_PREDELETE, true);
if (_predelete_ok) {
_class_name_ptr = nullptr; // Must restore, so constructors/destructors have proper class name access at each stage.
notification(NOTIFICATION_PREDELETE_CLEANUP, true);
}
return _predelete_ok;
}

View File

@ -801,6 +801,8 @@ public:
NOTIFICATION_POSTINITIALIZE = 0,
NOTIFICATION_PREDELETE = 1,
NOTIFICATION_EXTENSION_RELOADED = 2,
// Internal notification to send after NOTIFICATION_PREDELETE, not bound to scripting.
NOTIFICATION_PREDELETE_CLEANUP = 3,
};
/* TYPE API */

View File

@ -1985,24 +1985,31 @@ const Variant CSharpInstance::get_rpc_config() const {
void CSharpInstance::notification(int p_notification, bool p_reversed) {
if (p_notification == Object::NOTIFICATION_PREDELETE) {
// When NOTIFICATION_PREDELETE is sent, we also take the chance to call Dispose().
// It's safe to call Dispose() multiple times and NOTIFICATION_PREDELETE is guaranteed
if (base_ref_counted) {
// At this point, Dispose() was already called (manually or from the finalizer).
// The RefCounted wouldn't have reached 0 otherwise, since the managed side
// references it and Dispose() needs to be called to release it.
// However, this means C# RefCounted scripts can't receive NOTIFICATION_PREDELETE, but
// this is likely the case with GDScript as well: https://github.com/godotengine/godot/issues/6784
return;
}
} else if (p_notification == Object::NOTIFICATION_PREDELETE_CLEANUP) {
// When NOTIFICATION_PREDELETE_CLEANUP is sent, we also take the chance to call Dispose().
// It's safe to call Dispose() multiple times and NOTIFICATION_PREDELETE_CLEANUP is guaranteed
// to be sent at least once, which happens right before the call to the destructor.
predelete_notified = true;
if (base_ref_counted) {
// It's not safe to proceed if the owner derives RefCounted and the refcount reached 0.
// At this point, Dispose() was already called (manually or from the finalizer) so
// that's not a problem. The refcount wouldn't have reached 0 otherwise, since the
// managed side references it and Dispose() needs to be called to release it.
// However, this means C# RefCounted scripts can't receive NOTIFICATION_PREDELETE, but
// this is likely the case with GDScript as well: https://github.com/godotengine/godot/issues/6784
// At this point, Dispose() was already called (manually or from the finalizer).
// The RefCounted wouldn't have reached 0 otherwise, since the managed side
// references it and Dispose() needs to be called to release it.
return;
}
_call_notification(p_notification, p_reversed);
// NOTIFICATION_PREDELETE_CLEANUP is not sent to scripts.
// After calling Dispose() the C# instance can no longer be used,
// so it should be the last thing we do.
GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallDispose(
gchandle.get_intptr(), /* okIfNull */ false);