diff --git a/modules/mono/SdkPackageVersions.props b/modules/mono/SdkPackageVersions.props
index 2bd60bb7195..9947335b2ff 100644
--- a/modules/mono/SdkPackageVersions.props
+++ b/modules/mono/SdkPackageVersions.props
@@ -2,6 +2,6 @@
4.0.*-*
4.0.0-dev7
- 4.0.0-dev6
+ 4.0.0-dev7
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index ade436d3e08..99bd3edb9e3 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -104,7 +104,7 @@ Error CSharpLanguage::execute_file(const String &p_path) {
return OK;
}
-extern void *godotsharp_pinvoke_funcs[178];
+extern void *godotsharp_pinvoke_funcs[179];
[[maybe_unused]] volatile void **do_not_strip_godotsharp_pinvoke_funcs;
#ifdef TOOLS_ENABLED
extern void *godotsharp_editor_pinvoke_funcs[30];
@@ -1263,17 +1263,24 @@ void CSharpLanguage::release_script_gchandle(MonoGCHandleData &p_gchandle) {
}
}
-void CSharpLanguage::release_script_gchandle(void *p_expected_mono_obj_unused, MonoGCHandleData &p_gchandle) {
-#warning KNOWN BUG. DO NOT USE THIS IN PRODUCTION
- // KNOWN BUG:
- // I removed the patch from commit e558e1ec09aa27852426bbd24dfa21e9b60cfbfc.
- // This may cause data races. Re-implementing it without the Mono embedding API would be
- // too painful and would make the code even more of a mess than it already was.
- // We will switch from scripts to the new extension system before a release with .NET 6 support.
- // The problem the old patch was working around won't be present at all with the new extension system.
+void CSharpLanguage::release_script_gchandle_thread_safe(GCHandleIntPtr p_gchandle_to_free, MonoGCHandleData &r_gchandle) {
+ if (!r_gchandle.is_released() && r_gchandle.get_intptr() == p_gchandle_to_free) { // Do not lock unnecessarily
+ MutexLock lock(get_singleton()->script_gchandle_release_mutex);
+ if (!r_gchandle.is_released() && r_gchandle.get_intptr() == p_gchandle_to_free) {
+ r_gchandle.release();
+ }
+ }
+}
- (void)p_expected_mono_obj_unused;
- return release_script_gchandle(p_gchandle);
+void CSharpLanguage::release_binding_gchandle_thread_safe(GCHandleIntPtr p_gchandle_to_free, CSharpScriptBinding &r_script_binding) {
+ MonoGCHandleData &gchandle = r_script_binding.gchandle;
+ if (!gchandle.is_released() && gchandle.get_intptr() == p_gchandle_to_free) { // Do not lock unnecessarily
+ MutexLock lock(get_singleton()->script_gchandle_release_mutex);
+ if (!gchandle.is_released() && gchandle.get_intptr() == p_gchandle_to_free) {
+ gchandle.release();
+ r_script_binding.inited = false; // Here too, to be thread safe
+ }
+ }
}
CSharpLanguage::CSharpLanguage() {
@@ -1309,6 +1316,10 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b
ERR_FAIL_COND_V_MSG(!parent_is_object_class, false,
"Type inherits from native type '" + type_name + "', so it can't be instantiated in object of type: '" + p_object->get_class() + "'.");
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!r_script_binding.gchandle.is_released());
+#endif
+
GCHandleIntPtr strong_gchandle =
GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectBinding(&type_name, p_object);
@@ -1419,9 +1430,9 @@ GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token,
// Release the current weak handle and replace it with a strong handle.
GCHandleIntPtr old_gchandle = gchandle.get_intptr();
- gchandle.handle = GCHandleIntPtr(); // No longer owns the handle (released by swap function)
+ gchandle.handle = { nullptr }; // No longer owns the handle (released by swap function)
- GCHandleIntPtr new_gchandle;
+ GCHandleIntPtr new_gchandle = { nullptr };
bool create_weak = false;
bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType(
old_gchandle, &new_gchandle, create_weak);
@@ -1443,9 +1454,9 @@ GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token,
// Release the current strong handle and replace it with a weak handle.
GCHandleIntPtr old_gchandle = gchandle.get_intptr();
- gchandle.handle = GCHandleIntPtr(); // No longer owns the handle (released by swap function)
+ gchandle.handle = { nullptr }; // No longer owns the handle (released by swap function)
- GCHandleIntPtr new_gchandle;
+ GCHandleIntPtr new_gchandle = { nullptr };
bool create_weak = true;
bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType(
old_gchandle, &new_gchandle, create_weak);
@@ -1569,10 +1580,10 @@ void CSharpLanguage::tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_int
Ref script = p_script;
- CSharpScript::initialize_for_managed_type(script);
-
CRASH_COND(script.is_null());
+ CSharpScript::initialize_for_managed_type(script);
+
CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(p_unmanaged, script.ptr(), gchandle);
p_unmanaged->set_script_and_instance(script, csharp_instance);
@@ -1920,7 +1931,7 @@ bool CSharpInstance::_internal_new_managed() {
return true;
}
-void CSharpInstance::mono_object_disposed() {
+void CSharpInstance::mono_object_disposed(GCHandleIntPtr p_gchandle_to_free) {
// Must make sure event signals are not left dangling
disconnect_event_signals();
@@ -1928,10 +1939,10 @@ void CSharpInstance::mono_object_disposed() {
CRASH_COND(base_ref_counted);
CRASH_COND(gchandle.is_released());
#endif
- CSharpLanguage::get_singleton()->release_script_gchandle(nullptr, gchandle);
+ CSharpLanguage::get_singleton()->release_script_gchandle_thread_safe(p_gchandle_to_free, gchandle);
}
-void CSharpInstance::mono_object_disposed_baseref(bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) {
+void CSharpInstance::mono_object_disposed_baseref(GCHandleIntPtr p_gchandle_to_free, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) {
#ifdef DEBUG_ENABLED
CRASH_COND(!base_ref_counted);
CRASH_COND(gchandle.is_released());
@@ -1947,7 +1958,7 @@ void CSharpInstance::mono_object_disposed_baseref(bool p_is_finalizer, bool &r_d
r_delete_owner = true;
} else {
r_delete_owner = false;
- CSharpLanguage::get_singleton()->release_script_gchandle(nullptr, gchandle);
+ CSharpLanguage::get_singleton()->release_script_gchandle_thread_safe(p_gchandle_to_free, gchandle);
if (!p_is_finalizer) {
// If the native instance is still alive and Dispose() was called
@@ -2000,9 +2011,9 @@ void CSharpInstance::refcount_incremented() {
// Release the current weak handle and replace it with a strong handle.
GCHandleIntPtr old_gchandle = gchandle.get_intptr();
- gchandle.handle = GCHandleIntPtr(); // No longer owns the handle (released by swap function)
+ gchandle.handle = { nullptr }; // No longer owns the handle (released by swap function)
- GCHandleIntPtr new_gchandle;
+ GCHandleIntPtr new_gchandle = { nullptr };
bool create_weak = false;
bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType(
old_gchandle, &new_gchandle, create_weak);
@@ -2032,9 +2043,9 @@ bool CSharpInstance::refcount_decremented() {
// Release the current strong handle and replace it with a weak handle.
GCHandleIntPtr old_gchandle = gchandle.get_intptr();
- gchandle.handle = GCHandleIntPtr(); // No longer owns the handle (released by swap function)
+ gchandle.handle = { nullptr }; // No longer owns the handle (released by swap function)
- GCHandleIntPtr new_gchandle;
+ GCHandleIntPtr new_gchandle = { nullptr };
bool create_weak = true;
bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType(
old_gchandle, &new_gchandle, create_weak);
@@ -2207,62 +2218,6 @@ void CSharpScript::_update_exports_values(HashMap &values,
base_cache->_update_exports_values(values, propnames);
}
}
-
-void CSharpScript::_update_member_info_no_exports() {
- if (exports_invalidated) {
- exports_invalidated = false;
-
- member_info.clear();
-
-#warning TODO
-#if 0
- GDMonoClass *top = script_class;
- List props;
-
- while (top && top != native) {
- PropertyInfo prop_info;
- bool exported;
-
- const Vector &fields = top->get_all_fields();
-
- for (int i = fields.size() - 1; i >= 0; i--) {
- GDMonoField *field = fields[i];
-
- if (_get_member_export(field, /* inspect export: */ false, prop_info, exported)) {
- StringName member_name = field->get_name();
-
- member_info[member_name] = prop_info;
- props.push_front(prop_info);
- exported_members_defval_cache[member_name] = Variant();
- }
- }
-
- const Vector &properties = top->get_all_properties();
-
- for (int i = properties.size() - 1; i >= 0; i--) {
- GDMonoProperty *property = properties[i];
-
- if (_get_member_export(property, /* inspect export: */ false, prop_info, exported)) {
- StringName member_name = property->get_name();
-
- member_info[member_name] = prop_info;
- props.push_front(prop_info);
- exported_members_defval_cache[member_name] = Variant();
- }
- }
-
- exported_members_cache.push_back(PropertyInfo(Variant::NIL, top->get_name(), PROPERTY_HINT_NONE, get_path(), PROPERTY_USAGE_CATEGORY));
- for (const PropertyInfo &E : props) {
- exported_members_cache.push_back(E);
- }
-
- props.clear();
-
- top = top->get_parent_class();
- }
-#endif
- }
-}
#endif
bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_update) {
@@ -2282,170 +2237,61 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda
if (exports_invalidated)
#endif
{
-#warning TODO
-#if 0
- GD_MONO_SCOPE_THREAD_ATTACH;
+ exports_invalidated = false;
changed = true;
member_info.clear();
#ifdef TOOLS_ENABLED
- MonoObject *tmp_object = nullptr;
- Object *tmp_native = nullptr;
- uint32_t tmp_pinned_gchandle = 0;
-
- if (is_editor) {
- exports_invalidated = false;
-
- exported_members_cache.clear();
- exported_members_defval_cache.clear();
-
- // Here we create a temporary managed instance of the class to get the initial values
- tmp_object = mono_object_new(mono_domain_get(), script_class->get_mono_ptr());
-
- if (!tmp_object) {
- ERR_PRINT("Failed to allocate temporary MonoObject.");
- return false;
- }
-
- tmp_pinned_gchandle = GDMonoUtils::new_strong_gchandle_pinned(tmp_object); // pin it (not sure if needed)
-
- GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
-
- ERR_FAIL_NULL_V_MSG(ctor, false,
- "Cannot construct temporary MonoObject because the class does not define a parameterless constructor: '" + get_path() + "'.");
-
- MonoException *ctor_exc = nullptr;
- ctor->invoke(tmp_object, nullptr, &ctor_exc);
-
- tmp_native = GDMonoMarshal::unbox