Skip to content

Commit 676e4c9

Browse files
GDScript: Cancel suspended functions when reloading a script
1 parent 134da37 commit 676e4c9

File tree

7 files changed

+51
-15
lines changed

7 files changed

+51
-15
lines changed

modules/gdscript/gdscript.cpp

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,6 +1625,27 @@ void GDScript::clear(ClearData *p_clear_data) {
16251625
}
16261626
}
16271627

1628+
void GDScript::cancel_pending_functions(bool warn) {
1629+
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
1630+
1631+
while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) {
1632+
// Order matters since clearing the stack may already cause
1633+
// the GDScriptFunctionState to be destroyed and thus removed from the list.
1634+
pending_func_states.remove(E);
1635+
GDScriptFunctionState *state = E->self();
1636+
#ifdef DEBUG_ENABLED
1637+
if (warn) {
1638+
WARN_PRINT("Canceling suspended execution of \"" + state->get_readable_function() + "\" due to a script reload.");
1639+
}
1640+
#endif
1641+
ObjectID state_id = state->get_instance_id();
1642+
state->_clear_connections();
1643+
if (ObjectDB::get_instance(state_id)) {
1644+
state->_clear_stack();
1645+
}
1646+
}
1647+
}
1648+
16281649
GDScript::~GDScript() {
16291650
if (destructing) {
16301651
return;
@@ -1640,21 +1661,7 @@ GDScript::~GDScript() {
16401661

16411662
clear();
16421663

1643-
{
1644-
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
1645-
1646-
while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) {
1647-
// Order matters since clearing the stack may already cause
1648-
// the GDScriptFunctionState to be destroyed and thus removed from the list.
1649-
pending_func_states.remove(E);
1650-
GDScriptFunctionState *state = E->self();
1651-
ObjectID state_id = state->get_instance_id();
1652-
state->_clear_connections();
1653-
if (ObjectDB::get_instance(state_id)) {
1654-
state->_clear_stack();
1655-
}
1656-
}
1657-
}
1664+
cancel_pending_functions(false);
16581665

16591666
{
16601667
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);

modules/gdscript/gdscript.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ class GDScript : public Script {
244244

245245
void clear(GDScript::ClearData *p_clear_data = nullptr);
246246

247+
// Cancels all functions of the script that are are waiting to be resumed after using await.
248+
void cancel_pending_functions(bool warn);
249+
247250
virtual bool is_valid() const override { return valid; }
248251
virtual bool is_abstract() const override { return false; } // GDScript does not support abstract classes.
249252

modules/gdscript/gdscript_compiler.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2660,6 +2660,8 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
26602660

26612661
p_script->clearing = true;
26622662

2663+
p_script->cancel_pending_functions(true);
2664+
26632665
p_script->native = Ref<GDScriptNativeClass>();
26642666
p_script->base = Ref<GDScript>();
26652667
p_script->_base = nullptr;

modules/gdscript/gdscript_function.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,13 @@ class GDScriptFunctionState : public RefCounted {
616616
bool is_valid(bool p_extended_check = false) const;
617617
Variant resume(const Variant &p_arg = Variant());
618618

619+
#ifdef DEBUG_ENABLED
620+
// Returns a human-readable representation of the function.
621+
String get_readable_function() {
622+
return state.function_name;
623+
}
624+
#endif
625+
619626
void _clear_stack();
620627
void _clear_connections();
621628

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# TODO: This test is currently disabled since it triggers some complex memory leaks. Try enabling it again once GH-101830 is fixed.
2+
3+
signal finished
4+
5+
const scr: GDScript = preload("reload_suspended_function_helper.notest.gd")
6+
7+
func test():
8+
@warning_ignore("UNSAFE_METHOD_ACCESS")
9+
scr.test(self)
10+
@warning_ignore("RETURN_VALUE_DISCARDED")
11+
scr.reload(true)
12+
finished.emit()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
GDTEST_RUNTIME_ERROR
2+
>> WARNING: Canceling suspended execution of "test" due to a script reload.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
static func test(a):
2+
await a.finished
3+
pass

0 commit comments

Comments
 (0)