1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-04 12:00:25 +00:00

Merge pull request #102521 from HolonProduction/cancel-await

GDScript: Cancel suspended functions when reloading a script
This commit is contained in:
Thaddeus Crews
2025-04-08 12:32:52 -05:00
7 changed files with 51 additions and 15 deletions

View File

@@ -1623,6 +1623,27 @@ void GDScript::clear(ClearData *p_clear_data) {
}
}
void GDScript::cancel_pending_functions(bool warn) {
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) {
// Order matters since clearing the stack may already cause
// the GDScriptFunctionState to be destroyed and thus removed from the list.
pending_func_states.remove(E);
GDScriptFunctionState *state = E->self();
#ifdef DEBUG_ENABLED
if (warn) {
WARN_PRINT("Canceling suspended execution of \"" + state->get_readable_function() + "\" due to a script reload.");
}
#endif
ObjectID state_id = state->get_instance_id();
state->_clear_connections();
if (ObjectDB::get_instance(state_id)) {
state->_clear_stack();
}
}
}
GDScript::~GDScript() {
if (destructing) {
return;
@@ -1638,21 +1659,7 @@ GDScript::~GDScript() {
clear();
{
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) {
// Order matters since clearing the stack may already cause
// the GDScriptFunctionState to be destroyed and thus removed from the list.
pending_func_states.remove(E);
GDScriptFunctionState *state = E->self();
ObjectID state_id = state->get_instance_id();
state->_clear_connections();
if (ObjectDB::get_instance(state_id)) {
state->_clear_stack();
}
}
}
cancel_pending_functions(false);
{
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);

View File

@@ -243,6 +243,9 @@ public:
void clear(GDScript::ClearData *p_clear_data = nullptr);
// Cancels all functions of the script that are are waiting to be resumed after using await.
void cancel_pending_functions(bool warn);
virtual bool is_valid() const override { return valid; }
virtual bool is_abstract() const override { return false; } // GDScript does not support abstract classes.

View File

@@ -2659,6 +2659,8 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
p_script->clearing = true;
p_script->cancel_pending_functions(true);
p_script->native = Ref<GDScriptNativeClass>();
p_script->base = Ref<GDScript>();
p_script->_base = nullptr;

View File

@@ -615,6 +615,13 @@ public:
bool is_valid(bool p_extended_check = false) const;
Variant resume(const Variant &p_arg = Variant());
#ifdef DEBUG_ENABLED
// Returns a human-readable representation of the function.
String get_readable_function() {
return state.function_name;
}
#endif
void _clear_stack();
void _clear_connections();

View File

@@ -0,0 +1,12 @@
# TODO: This test is currently disabled since it triggers some complex memory leaks. Try enabling it again once GH-101830 is fixed.
signal finished
const scr: GDScript = preload("reload_suspended_function_helper.notest.gd")
func test():
@warning_ignore("UNSAFE_METHOD_ACCESS")
scr.test(self)
@warning_ignore("RETURN_VALUE_DISCARDED")
scr.reload(true)
finished.emit()

View File

@@ -0,0 +1,2 @@
GDTEST_RUNTIME_ERROR
>> WARNING: Canceling suspended execution of "test" due to a script reload.

View File

@@ -0,0 +1,3 @@
static func test(a):
await a.finished
pass