mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 13:41:03 +00:00 
			
		
		
		
	GDScript: Cancel suspended functions when reloading a script
This commit is contained in:
		
							parent
							
								
									134da37497
								
							
						
					
					
						commit
						676e4c9013
					
				
					 7 changed files with 51 additions and 15 deletions
				
			
		|  | @ -1625,6 +1625,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; | ||||
|  | @ -1640,21 +1661,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); | ||||
|  |  | |||
|  | @ -244,6 +244,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.
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -2660,6 +2660,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; | ||||
|  |  | |||
|  | @ -616,6 +616,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(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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() | ||||
|  | @ -0,0 +1,2 @@ | |||
| GDTEST_RUNTIME_ERROR | ||||
| >> WARNING: Canceling suspended execution of "test" due to a script reload. | ||||
|  | @ -0,0 +1,3 @@ | |||
| static func test(a): | ||||
| 	await a.finished | ||||
| 	pass | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 HolonProduction
						HolonProduction