summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/object/object.cpp1
-rw-r--r--core/object/object.h2
-rw-r--r--modules/mono/csharp_script.cpp27
3 files changed, 20 insertions, 10 deletions
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 2e5b897bce..40df13849b 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -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;
}
diff --git a/core/object/object.h b/core/object/object.h
index a444db0f70..f3c387594b 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -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 */
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 3cc32bff10..56e4fa53d0 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1989,24 +1989,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);