### Godot version

v4.1.1.stable.mono.official [bd6af8e0e]

### System information

Godot v4.1.1.stable.mono - macOS 13.4.1 - Vulkan (Forward+) - integrated Apple M1 - Apple M1 (8 Threads)

### Issue description

After creating a `@tool` script that is autoloaded and uses Semaphores and a Thread, when exiting the editor it freezes and stops responding on MacOS.

Editor not responding after closing the editor:
![image](https://github.com/godotengine/godot/assets/29845000/1a08e748-5ca9-4ed1-9bf0-a1399aab7629)
![image](https://github.com/godotengine/godot/assets/29845000/fa60171c-3123-4e85-b3ca-ab806d4f7bde)

The editor must then be force quit and produces the following error information:
https://gist.github.com/Trikzon/dc3e1ed7a258fa9cc2c363b18b43c353

A full minimal reproducible product is provided, but the important snippets are:
```
@tool
extends Node

var mutex: Mutex
var semaphore: Semaphore
var thread: Thread
var exit_thread := false


func _ready():
    mutex = Mutex.new()
    semaphore = Semaphore.new()
    exit_thread = false
    
    thread = Thread.new()
    thread.start(_thread_function)

func _thread_function():
    while true:
        semaphore.wait()
        
        mutex.lock()
        var should_exit = exit_thread
        mutex.unlock()
        
        if should_exit:
            break
        
       # Actual thread work goes here

func _exit_tree():
    mutex.lock()
    exit_thread = true
    mutex.unlock()
    
    semaphore.post()
    
    thread.wait_to_finish()
```

At first I thought that `_exit_tree()` was not being called when the editor closed, but I found that it is because placing a `ResourceSaver.save()` in `_exit_tree()` creates a file when the editor closes.

### Steps to reproduce

Create a `@tool` script using Semaphores and a Thread and set it as an autoloading script in-editor.

The [Using Multiple Threads#Semaphores](https://docs.godotengine.org/en/stable/tutorials/performance/using_multiple_threads.html#semaphores) guide can be used as a base.

### Minimal reproduction project

[Thread Tools Test.zip](https://github.com/godotengine/godot/files/12237193/Thread.Tools.Test.zip)

Note: I use a mono build of Godot, but this does not contain any C#.

Contains an autoloaded tool script that is a slightly modified version of the [Using Multiple Threads#Semaphores](https://docs.godotengine.org/en/stable/tutorials/performance/using_multiple_threads.html#semaphores) guide.

Also contains a test scene with a tool script where changing a number in-editor will post the semaphore to see that the thread is running correctly in the background.
String source_scene = get_edited_scene()->get_scene_file_path();
			if (FileAccess::exists(source_scene + ".import")) {
				editable_info = TTR("This scene was imported, so changes to it won't be kept.\nInstantiating or inheriting it will allow you to make changes to it.\nPlease read the documentation relevant to importing scenes to better understand this workflow.");
				info_is_warning = true;
			}
		}
	} else {
		Node *selected_node = nullptr;
		if (current_obj->is_class("EditorDebuggerRemoteObject")) {
			disable_folding = true;
		} else if (current_obj->is_class("MultiNodeEdit")) {
			Node *scene = get_edited_scene();
			if (scene) {
				MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(current_obj);
				int node_count = multi_node_edit->get_node_count();
				if (node_count > 0) {
					List<Node *> multi_nodes;
					for (int node_index = 0; node_index < node_count; ++node_index) {
						Node *node = scene->get_node(multi_node_edit->get_node(node_index));
						if (node) {
							multi_nodes.push_back(node);
						}
					}
					if (!multi_nodes.is_empty()) {
						
						multi_nodes.sort_custom<Node::Comparator>();
						selected_node = multi_nodes.front()->get();
					}
				}
			}