Skip to content

Commit 01d3346

Browse files
authored
Merge pull request #1367 from JoeyEamigh/fix_macos_hot_reload
Fix hot-reload crashes on macOS when the `.gdextension` file changes
2 parents 41e5f47 + 10eac7e commit 01d3346

File tree

3 files changed

+71
-15
lines changed

3 files changed

+71
-15
lines changed

godot-core/src/meta/class_id.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,20 @@ impl ClassIdCache {
309309
}
310310

311311
fn clear(&mut self) {
312-
self.entries.clear();
312+
// MACOS-PARTIAL-RELOAD: Previous implementation for when upstream fixes `.gdextension` reload.
313+
// self.entries.clear();
314+
// self.type_to_index.clear();
315+
// self.string_to_index.clear();
316+
317+
// MACOS-PARTIAL-RELOAD: Preserve existing `ClassId` entries when only the `.gdextension` reloads so indices stay valid.
318+
// There are two types of hot reload: `dylib` reload (`dylib` `mtime` newer) unloads and reloads the library, whereas
319+
// `.gdextension` reload (`.gdextension` `mtime` newer) re-initializes the existing `dylib` without unloading it. To handle
320+
// `.gdextension` reload, keep the backing entries (and thus the `string_to_index` map) but drop cached Godot `StringNames`
321+
// and the `TypeId` lookup so they can be rebuilt.
322+
for entry in &mut self.entries {
323+
entry.godot_str = OnceCell::new();
324+
}
325+
313326
self.type_to_index.clear();
314-
self.string_to_index.clear();
315327
}
316328
}

godot-ffi/src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,15 @@ pub unsafe fn initialize(
283283
/// # Safety
284284
/// See [`initialize`].
285285
pub unsafe fn deinitialize() {
286-
deinitialize_binding()
286+
deinitialize_binding();
287+
288+
// MACOS-PARTIAL-RELOAD: Clear the main thread ID to allow re-initialization during hot reload.
289+
#[cfg(not(wasm_nothreads))]
290+
{
291+
if MAIN_THREAD_ID.is_initialized() {
292+
MAIN_THREAD_ID.clear();
293+
}
294+
}
287295
}
288296

289297
fn print_preamble(version: GDExtensionGodotVersion) {

itest/hot-reload/godot/run-test.sh

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,39 @@ cleanup() {
3737
set -euo pipefail
3838
trap cleanup EXIT
3939

40+
godotAwait() {
41+
if [[ $godotPid -ne 0 ]]; then
42+
echo "[Bash] Error: godotAwait called while Godot (PID $godotPid) is still running."
43+
exit 1
44+
fi
45+
46+
$GODOT4_BIN -e --headless --path $rel &
47+
godotPid=$!
48+
echo "[Bash] Wait for Godot ready (PID $godotPid)..."
49+
50+
$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- await
51+
}
52+
53+
godotNotify() {
54+
if [[ $godotPid -eq 0 ]]; then
55+
echo "[Bash] Error: godotNotify called but Godot is not running."
56+
exit 1
57+
fi
58+
59+
$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- notify
60+
61+
echo "[Bash] Wait for Godot exit..."
62+
local status=0
63+
wait $godotPid
64+
status=$?
65+
echo "[Bash] Godot (PID $godotPid) has completed with status $status."
66+
godotPid=0
67+
68+
if [[ $status -ne 0 ]]; then
69+
exit $status
70+
fi
71+
}
72+
4073
echo "[Bash] Start hot-reload integration test..."
4174

4275
# Restore un-reloaded file (for local testing).
@@ -54,22 +87,25 @@ cargo build -p hot-reload $cargoArgs
5487
# Wait briefly so artifacts are present on file system.
5588
sleep 0.5
5689

57-
$GODOT4_BIN -e --headless --path $rel &
58-
godotPid=$!
59-
echo "[Bash] Wait for Godot ready (PID $godotPid)..."
90+
# ----------------------------------------------------------------
91+
# Test Case 1: Update Rust source and compile to trigger reload.
92+
# ----------------------------------------------------------------
6093

61-
$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- await
62-
$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- replace
94+
echo "[Bash] Scenario 1: Reload after updating Rust source..."
6395

96+
godotAwait
97+
$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- replace
6498
# Compile updated Rust source.
6599
cargo build -p hot-reload $cargoArgs
100+
godotNotify
66101

67-
$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- notify
68-
69-
echo "[Bash] Wait for Godot exit..."
70-
wait $godotPid
71-
status=$?
72-
echo "[Bash] Godot (PID $godotPid) has completed with status $status."
73-
102+
# ----------------------------------------------------------------
103+
# Test Case 2: Touch the .gdextension file to trigger reload.
104+
# ----------------------------------------------------------------
74105

106+
echo "[Bash] Scenario 2: Reload after touching rust.gdextension..."
75107

108+
godotAwait
109+
# Update timestamp to trigger reload.
110+
touch "$rel/rust.gdextension"
111+
godotNotify

0 commit comments

Comments
 (0)