godotengine / godotengine/godot
`call_thread_safe()` from main thread to sub thread node runs on the main thread
- Dominant language
- C++
- Stars
- 117k
- Forks
- 26.8k
- PR merge metrics
- PR metrics pending
Description
### Tested versions
- Reproducible in: 4.4.beta3, 4.3.stable, 4.1.stable
### System information
Godot v4.4.beta3 - Linux Mint 22.1 (Xia) on X11 - X11 display driver, Multi-window, 2 monitors - Vulkan (Mobile) - dedicated NVIDIA GeForce GTX 1050 (nvidia; 550.120) - Intel(R) Core(TM) i5-9400F CPU @ 2.90GHz (6 threads)
### Issue description
Calling [`Node.call_thread_safe()`](https://docs.godotengine.org/en/stable/classes/class_node.html#class-node-method-call-thread-safe) *from* the main thread *to* the sub thread node results in the method called from the main thread, instead of the sub thread.
This only specifically happens on `main thread -> sub thread` request.
`sub thread -> main thread` and `sub thread -> other sub thread` requests work as expected (i.e. deferred to receiving node's thread).
### Steps to reproduce
#### Sample Project
1. Open the sample project in v4.4.beta3 .
1. Run the game, and see the output message.
#### Manual setup
1. Create a scene called `TopScene`, and add this script:
```gdscript
extends Node
var running = false
func _process(_delta: float) -> void:
if !running:
print(name, ": tid=", OS.get_thread_caller_id())
$ChildNode.call_thread_safe("print_my_message")
running = true
```
2. Create a child node `ChildNode` in it, and add this script:
```gdscript
extends Node
func _process(_delta: float) -> void:
pass
func print_my_message() -> void:
print(name, ": called from tid=", OS.get_thread_caller_id())
```
3. Set the `ChildNode`'s `Thread Group` to `Sub Thread`.
4. Set the `TopScene`'s `Thread Group` to `Main Thread`.
* Or leave it as `Inherit`, since default thread group should be set to main thread.
5. Run the game, and see the output message.
#### Output
```
TopScene: tid=1
ChildNode: called from tid=1
```
`$ChildNode.print_my_message()` was called from the main thread (`tid=1`), instead of seperate sub thread.
### Minimal reproduction project (MRP)
[call_thread_safe_issue.tar.gz](https://github.com/user-attachments/files/18723480/call_thread_safe_issue.tar.gz)
Contributor guide
Research direction
Start with the Node.call_thread_safe() entry point and run the linked minimal reproduction project using the documented TopScene and ChildNode thread-group setup. Trace why a main-thread call reaches the sub-thread node on tid=1; done means the child method runs on its sub-thread while the other request directions continue to behave as described.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- game-dev, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 42/100