fix(linux): prevent segfaults during rapid module reloads

Fixes multiple issues causing segfaults when hot-reloading modules
on Linux, particularly when file changes are detected rapidly:

- Enable is_file_ready() check on Linux to prevent loading partially-written
  shared libraries (previously only used on Windows/BSD)
- Fix event deduplication on Linux to actually remove duplicate inotify events
  instead of just marking them, preventing double-free on same module
- Reorder reload operations to unload old module only after successfully
  copying new version, avoiding invalid state when copy fails

Changes:
- platform.c: Remove __linux__ guards around is_file_ready() function
- platform.c: Add compaction step after deduplication to remove -1 entries
- stk.c: Move module unload to after platform_copy_file() in reload loop

These changes make Linux hot-reload as robust as Windows/BSD implementations.
This commit is contained in:
2026-02-09 22:25:02 +01:00
parent 043fea6092
commit f83f2051ef
2 changed files with 114 additions and 103 deletions
+19 -7
View File
@@ -32,7 +32,6 @@ int is_mod_loaded(const char *module_name);
uint8_t is_valid_module_file(const char *filename);
void extract_module_id(const char *path, char *out_id);
#ifndef __linux__
static uint8_t is_file_ready(const char *dir_path, const char *filename)
{
char full_path[STK_PATH_MAX_OS];
@@ -80,6 +79,7 @@ static uint8_t is_file_ready(const char *dir_path, const char *filename)
#endif
}
#ifndef __linux__
typedef struct {
char filename[STK_PATH_MAX];
#ifdef _WIN32
@@ -146,7 +146,6 @@ uint8_t platform_copy_file(const char *from, const char *to)
FILE *src = NULL, *dst = NULL;
char tmp_path[STK_PATH_MAX_OS];
size_t n;
#ifndef __linux__
char dir_path[STK_PATH_MAX_OS];
const char *filename;
@@ -163,7 +162,6 @@ uint8_t platform_copy_file(const char *from, const char *to)
if (!is_file_ready(dir_path, filename))
goto done;
#endif
sprintf(tmp_path, "%s.tmp", to);
@@ -286,9 +284,8 @@ void *platform_get_symbol(void *h, const char *s)
#endif
}
char (*platform_directory_init_scan(const char *dir_path,
size_t *out_count))[STK_PATH_MAX]
{
char (*platform_directory_init_scan(const char *dir_path, size_t *out_count))
[STK_PATH_MAX] {
size_t count = 0, i = 0, name_len;
char (*list)[STK_PATH_MAX] = NULL;
#ifdef _WIN32
@@ -665,7 +662,7 @@ stk_module_event_t *platform_directory_watch_check(
int fd = (int)(long)handle;
char buf[STK_EVENT_BUFFER];
ssize_t len;
size_t index = 0, count = 0, i;
size_t index = 0, count = 0, i, write_idx;
stk_module_event_t *evs;
char *ptr, *end;
struct inotify_event *e;
@@ -763,6 +760,21 @@ stk_module_event_t *platform_directory_watch_check(
}
}
write_idx = 0;
for (i = 0; i < index; ++i) {
if (evs[i] != -1) {
if (write_idx != i) {
evs[write_idx] = evs[i];
strncpy((*file_list)[write_idx],
(*file_list)[i], STK_PATH_MAX - 1);
(*file_list)[write_idx][STK_PATH_MAX - 1] =
'\0';
}
write_idx++;
}
}
index = write_idx;
*out_count = index;
return evs;
+2 -3
View File
@@ -242,9 +242,6 @@ begin_operations:
for (i = 0; i < unload_count; ++i)
stk_module_unload(unloaded_mod_indices[i]);
for (i = 0; i < reload_count; ++i)
stk_module_unload(reloaded_mod_indices[i]);
for (i = 0; i < reload_count; ++i) {
int file_index = reloaded_mod_file_indices[i];
int mod_index = reloaded_mod_indices[i];
@@ -261,6 +258,8 @@ begin_operations:
continue;
}
stk_module_unload(mod_index);
load_result = stk_module_load(tmp_path, mod_index);
if (load_result != STK_MOD_INIT_SUCCESS) {
stk_log(stderr, "Failed to reload module %s: %s",