Repository: godot
Issue: Function calls made after awaiting on signal emitted from within a thread has a chance of failing abruptly without error.
Labels: topic:gdscript,needs testing

### Tested versions

v4.2.rc.custom_build [d34f1ec4b], v4.3.beta.custom_build [44ac302e3]

### System information

Windows 10

### Issue description

I built a "thread hooking" system (which essentially allows me to jump in and out of a thread at any point in my code) to avoid creating multiple threads because [Godot 3.X would stutter every time a thread was started](https://github.com/godotengine/godot/issues/67174). My code revolves around usage of this system which only now I've found to just stop working at random without any error. (And re-working all my thread reliant code to call functions directly with threads will give me Big Sad™ which is why I want some sorta fix.)
It took me so long to find this issue because this code can run completely fine a few thousand times... until it doesn't.

When the issue occurs the function called within the thread just does not execute past a certain point as if it were awaiting on a signal that never comes. 

### Steps to reproduce

Here is the "thread hooking" code and the "stability/stress test" which crashes it:

```gdscript
extends Node2D
#optional label to show thread progress without std_out
#@onready var label = get_node("RichTextLabel")

var thread = Thread.new()
var semaphore = Semaphore.new()
var thread_active = true
signal thread_frame()
signal thread_finished()
var yielding_on_thread_process = false




func thread_process():
	Thread.set_thread_safety_checks_enabled(false)
	while thread_active:
		
		
		if !yielding_on_thread_process:
			print("held")
			semaphore.wait()
			print("released")
		#this fixes the deadlocking and error issues (because i think the thread will call emit_signal() at the exact same time as `await self.thread_frame` is called in reattach_to_thread(), which seems to break godot ) (maybe make the delay a bit larger than 1 usec just in case):
		OS.delay_usec(100)
		emit_signal("thread_frame")
		
	print("finished")



var ri = 0
func reattach_to_thread():
	var id = thread.get_id()
	#if not already in thread
	if OS.get_thread_caller_id() != int(id):
		#on semaphore.post(), the thread is "released" to emit a signal
		yielding_on_thread_process = true
		print("posted" + str(ri))
		semaphore.post()
		#this merges/hooks the function into the thread (awaiting on anything adds the code being run to whatever thread is being awaited on)
		await self.thread_frame
		print("hooked"+ str(ri))
		yielding_on_thread_process = false
		ri+=1
		if ri > 999:
			ri = 0
	else:
		print("reattach not needed")







#stability test functions==============================


func stability_test():
	label.text = ""
	#stability test:
	for i in range(1000):
		await reattach_to_thread()
		await get_tree().process_frame
		label.text += ", "+str(i)
		print(i)


#READY=============================================




# Called when the node enters the scene tree for the first time.
func _ready():
	#HOW TO USE THREAD HOOKING:
	#start the thread process
	var c = Callable(self,"thread_process")
	thread.start(c)
	
	print("you are currently in the main thread:"+str(OS.get_thread_caller_id()))
	
	
	await reattach_to_thread()
	
	
	print("you are in the thread now:"+str(OS.get_thread_caller_id()))
	
	
	#return to main thread by awaiting on process_frame
	await get_tree().process_frame
	print("you are in the main thread again:"+str(OS.get_thread_caller_id()))
	
	
	
	#TESTING STABILITY:
	while true:
		await stability_test()
	

	
```

Read comments under `func awaitable_threader(i)` for more info about the issue.

### Minimal reproduction project (MRP)

N/A