refactor: optimize file watching and fix cross-platform reliability issues

Refactor memory allocation patterns:
- Replace realloc-in-loop with count-then-allocate pattern across all platforms
- Eliminate arbitrary buffer sizes (e.g., malloc(8 * ...)) in favor of exact counts
- Reduce allocation overhead by pre-counting items before malloc

Fix Windows file watching:
- Replace unreliable FindFirstChangeNotification with directory handle approach
- Add is_file_ready() to prevent events while compiler is still writing files
- Preserve timestamps when file is locked to retry on next poll
- Fix do-while loop in platform_directory_init_scan (was skipping first file)

Fix Linux inotify event handling:
- Consolidate DELETE+CREATE pairs into single RELOAD event
- Prevents duplicate events when compiler uses temp-file-and-rename pattern

Fix BSD/macOS kqueue implementation:
- Remove realloc loops from update_watches() and watch initialization
- Pre-count files before allocating file descriptor arrays

All platforms now correctly handle:
- Compiler overwrites (temp file operations)
- Manual copy/move operations
- Explicit file deletions

Tested on Linux, Windows 10, and FreeBSD.
This commit is contained in:
2026-01-23 23:39:24 +01:00
parent 93657d71e3
commit f331970ae2
2 changed files with 294 additions and 145 deletions
+1 -4
View File
@@ -1,13 +1,10 @@
include config.mk include config.mk
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
# Force the shell to cmd.exe to avoid bash/sh interference
SHELL := cmd.exe SHELL := cmd.exe
FULL_LIB := $(LIB_NAME).dll FULL_LIB := $(LIB_NAME).dll
LDFLAGS_PLAT := LDFLAGS_PLAT :=
CFLAGS_PLAT := CFLAGS_PLAT :=
# Windows-safe directory creation: check existence, then create
# Use 2>NUL to silence "directory already exists" warnings if any
MKDIR = if not exist $(subst /,\,$(1)) mkdir $(subst /,\,$(1)) MKDIR = if not exist $(subst /,\,$(1)) mkdir $(subst /,\,$(1))
RMDIR = if exist $(subst /,\,$(1)) rd /s /q $(subst /,\,$(1)) RMDIR = if exist $(subst /,\,$(1)) rd /s /q $(subst /,\,$(1))
else else
@@ -50,4 +47,4 @@ obj/release/%.o: src/%.c
clean: clean:
@$(call RMDIR,$(OBJ_DIR)) @$(call RMDIR,$(OBJ_DIR))
@$(call RMDIR,$(BIN_DIR)) @$(call RMDIR,$(BIN_DIR))
+293 -141
View File
@@ -28,6 +28,22 @@ int is_module_loaded(const char *filename,
char (*loaded_ids)[STK_MOD_ID_BUFFER], size_t count); char (*loaded_ids)[STK_MOD_ID_BUFFER], size_t count);
uint8_t is_valid_module_file(const char *filename); uint8_t is_valid_module_file(const char *filename);
#ifdef _WIN32
static uint8_t is_file_ready(const char *dir_path, const char *filename)
{
char full_path[STK_PATH_MAX_OS];
HANDLE h;
sprintf(full_path, "%s\\%s", dir_path, filename);
h = CreateFileA(full_path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
return 0;
CloseHandle(h);
return 1;
}
#endif
#ifndef __linux__ #ifndef __linux__
typedef struct { typedef struct {
char filename[STK_PATH_MAX]; char filename[STK_PATH_MAX];
@@ -220,12 +236,13 @@ char (*platform_directory_init_scan(const char *dir_path, size_t *out_count))
if (h == INVALID_HANDLE_VALUE) if (h == INVALID_HANDLE_VALUE)
goto exit; goto exit;
while (FindNextFileA(h, &fd) && i < count) { do {
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
continue; continue;
if (is_valid_module_file(fd.cFileName)) if (is_valid_module_file(fd.cFileName) && i < count)
strncpy(list[i++], fd.cFileName, STK_PATH_MAX - 1); strncpy(list[i++], fd.cFileName, STK_PATH_MAX - 1);
} } while (FindNextFileA(h, &fd));
FindClose(h); FindClose(h);
goto exit; goto exit;
@@ -300,13 +317,12 @@ static void update_watches(platform_watch_context_t *ctx)
DIR *d; DIR *d;
struct dirent *e; struct dirent *e;
char f[STK_PATH_MAX_OS]; char f[STK_PATH_MAX_OS];
size_t i; size_t i, count = 0;
int fd; int fd;
int *tmp_fds; int *new_fds = NULL;
for (i = 0; i < ctx->watch.k.file_fd_count; i++) { for (i = 0; i < ctx->watch.k.file_fd_count; i++)
close(ctx->watch.k.file_fds[i]); close(ctx->watch.k.file_fds[i]);
}
free(ctx->watch.k.file_fds); free(ctx->watch.k.file_fds);
ctx->watch.k.file_fds = NULL; ctx->watch.k.file_fds = NULL;
ctx->watch.k.file_fd_count = 0; ctx->watch.k.file_fd_count = 0;
@@ -320,9 +336,28 @@ static void update_watches(platform_watch_context_t *ctx)
if (!d) if (!d)
return; return;
scan_loop: count_loop:
e = readdir(d); e = readdir(d);
if (!e) if (!e)
goto count_done;
if (is_valid_module_file(e->d_name))
count++;
goto count_loop;
count_done:
if (count == 0)
goto cleanup;
new_fds = malloc(count * sizeof(int));
if (!new_fds)
goto cleanup;
rewinddir(d);
i = 0;
scan_loop:
e = readdir(d);
if (!e || i >= count)
goto scan_done; goto scan_done;
if (!is_valid_module_file(e->d_name)) if (!is_valid_module_file(e->d_name))
@@ -333,15 +368,7 @@ scan_loop:
if (fd < 0) if (fd < 0)
goto scan_loop; goto scan_loop;
tmp_fds = realloc(ctx->watch.k.file_fds, new_fds[i++] = fd;
sizeof(int) * (ctx->watch.k.file_fd_count + 1));
if (!tmp_fds) {
close(fd);
goto scan_loop;
}
ctx->watch.k.file_fds = tmp_fds;
ctx->watch.k.file_fds[ctx->watch.k.file_fd_count++] = fd;
EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR,
NOTE_WRITE | NOTE_ATTRIB, 0, NULL); NOTE_WRITE | NOTE_ATTRIB, 0, NULL);
@@ -350,13 +377,17 @@ scan_loop:
goto scan_loop; goto scan_loop;
scan_done: scan_done:
ctx->watch.k.file_fds = new_fds;
ctx->watch.k.file_fd_count = i;
cleanup:
closedir(d); closedir(d);
} }
#endif #endif
void *platform_directory_watch_start(const char *path) void *platform_directory_watch_start(const char *path)
{ {
#if defined(__linux__) #ifdef __linux__
int fd = inotify_init1(IN_NONBLOCK); int fd = inotify_init1(IN_NONBLOCK);
if (fd < 0) if (fd < 0)
return NULL; return NULL;
@@ -364,6 +395,18 @@ void *platform_directory_watch_start(const char *path)
fd, path, IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM); fd, path, IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM);
return (void *)(long)fd; return (void *)(long)fd;
#else #else
#ifdef _WIN32
WIN32_FIND_DATAA fd;
HANDLE h;
char s[STK_PATH_MAX_OS];
size_t count = 0, i = 0;
#else
DIR *d;
struct dirent *e;
struct stat st;
char f[STK_PATH_MAX_OS];
size_t count = 0, i = 0;
#endif
platform_watch_context_t *ctx = platform_watch_context_t *ctx =
calloc(1, sizeof(platform_watch_context_t)); calloc(1, sizeof(platform_watch_context_t));
if (!ctx) if (!ctx)
@@ -371,85 +414,106 @@ void *platform_directory_watch_start(const char *path)
strncpy(ctx->path, path, STK_PATH_MAX - 1); strncpy(ctx->path, path, STK_PATH_MAX - 1);
#ifdef _WIN32 #ifdef _WIN32
{ ctx->watch.change_handle =
WIN32_FIND_DATAA fd; CreateFileA(path, FILE_LIST_DIRECTORY,
HANDLE h; FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
char s[STK_PATH_MAX_OS]; NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
void *tmp_snaps;
ctx->watch.change_handle = FindFirstChangeNotificationA( if (ctx->watch.change_handle == INVALID_HANDLE_VALUE)
path, FALSE, goto done;
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_LAST_WRITE);
sprintf(s, "%s\\*", path); sprintf(s, "%s\\*", path);
h = FindFirstFileA(s, &fd); h = FindFirstFileA(s, &fd);
if (h == INVALID_HANDLE_VALUE) if (h == INVALID_HANDLE_VALUE)
goto done; goto done;
win_scan_loop: do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
is_valid_module_file(fd.cFileName)) { is_valid_module_file(fd.cFileName))
tmp_snaps = realloc(ctx->snaps, count++;
(ctx->count + 1) * } while (FindNextFileA(h, &fd));
sizeof(platform_snapshot_t));
if (tmp_snaps) { FindClose(h);
ctx->snaps = tmp_snaps;
strncpy(ctx->snaps[ctx->count].filename, if (count == 0)
fd.cFileName, STK_PATH_MAX - 1); goto done;
ctx->snaps[ctx->count++].mtime =
fd.ftLastWriteTime; ctx->snaps = malloc(count * sizeof(platform_snapshot_t));
} if (!ctx->snaps)
goto done;
h = FindFirstFileA(s, &fd);
if (h == INVALID_HANDLE_VALUE)
goto done;
do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
is_valid_module_file(fd.cFileName) && i < count) {
strncpy(ctx->snaps[i].filename, fd.cFileName,
STK_PATH_MAX - 1);
ctx->snaps[i].mtime = fd.ftLastWriteTime;
i++;
} }
if (FindNextFileA(h, &fd)) } while (FindNextFileA(h, &fd));
goto win_scan_loop;
FindClose(h);
ctx->count = i;
FindClose(h);
}
#else #else
{
DIR *d;
struct dirent *e;
struct stat st;
char f[STK_PATH_MAX_OS];
void *tmp_snaps;
ctx->watch.k.kq = kqueue(); ctx->watch.k.kq = kqueue();
ctx->watch.k.dir_fd = open(path, O_RDONLY); ctx->watch.k.dir_fd = open(path, O_RDONLY);
d = opendir(path); d = opendir(path);
if (!d) if (!d)
goto bsd_setup; goto bsd_setup;
bsd_scan_loop: bsd_count_loop:
e = readdir(d); e = readdir(d);
if (!e) if (!e)
goto bsd_scan_done; goto bsd_count_done;
sprintf(f, "%s/%s", path, e->d_name); sprintf(f, "%s/%s", path, e->d_name);
if (!is_valid_module_file(e->d_name)) if (!is_valid_module_file(e->d_name))
goto bsd_scan_loop; goto bsd_count_loop;
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode)) if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
goto bsd_scan_loop; goto bsd_count_loop;
tmp_snaps = realloc( count++;
ctx->snaps, (ctx->count + 1) * sizeof(platform_snapshot_t)); goto bsd_count_loop;
if (!tmp_snaps)
goto bsd_scan_loop;
ctx->snaps = tmp_snaps; bsd_count_done:
strncpy(ctx->snaps[ctx->count].filename, e->d_name, if (count == 0)
STK_PATH_MAX - 1); goto bsd_setup;
ctx->snaps[ctx->count++].mtime = st.st_mtime;
ctx->snaps = malloc(count * sizeof(platform_snapshot_t));
if (!ctx->snaps)
goto bsd_setup;
rewinddir(d);
bsd_scan_loop:
e = readdir(d);
if (!e || i >= count)
goto bsd_scan_done;
sprintf(f, "%s/%s", path, e->d_name);
if (!is_valid_module_file(e->d_name))
goto bsd_scan_loop;
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
goto bsd_scan_loop; goto bsd_scan_loop;
bsd_scan_done: strncpy(ctx->snaps[i].filename, e->d_name, STK_PATH_MAX - 1);
closedir(d); ctx->snaps[i].mtime = st.st_mtime;
i++;
goto bsd_scan_loop;
bsd_setup: bsd_scan_done:
update_watches(ctx); closedir(d);
} ctx->count = i;
bsd_setup:
update_watches(ctx);
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
@@ -473,11 +537,10 @@ void platform_directory_watch_stop(void *handle)
return; return;
#ifdef _WIN32 #ifdef _WIN32
FindCloseChangeNotification(ctx->watch.change_handle); CloseHandle(ctx->watch.change_handle);
#else #else
for (i = 0; i < ctx->watch.k.file_fd_count; i++) { for (i = 0; i < ctx->watch.k.file_fd_count; i++)
close(ctx->watch.k.file_fds[i]); close(ctx->watch.k.file_fds[i]);
}
free(ctx->watch.k.file_fds); free(ctx->watch.k.file_fds);
close(ctx->watch.k.kq); close(ctx->watch.k.kq);
close(ctx->watch.k.dir_fd); close(ctx->watch.k.dir_fd);
@@ -495,9 +558,9 @@ stk_module_event_t *platform_directory_watch_check(
int fd = (int)(long)handle; int fd = (int)(long)handle;
char buf[STK_EVENT_BUFFER]; char buf[STK_EVENT_BUFFER];
ssize_t len; ssize_t len;
size_t idx = 0; size_t idx = 0, count = 0;
stk_module_event_t *evs; stk_module_event_t *evs;
char *ptr; char *ptr, *end;
struct inotify_event *e; struct inotify_event *e;
int event_type; int event_type;
@@ -507,34 +570,74 @@ stk_module_event_t *platform_directory_watch_check(
return NULL; return NULL;
} }
evs = malloc(8 * sizeof(stk_module_event_t));
*file_list = malloc(8 * sizeof(**file_list));
ptr = buf; ptr = buf;
end = buf + len;
process_inotify_loop: while (ptr < end) {
if (ptr >= buf + len) e = (struct inotify_event *)ptr;
goto linux_done; if (e->len && is_valid_module_file(e->name))
count++;
e = (struct inotify_event *)ptr; ptr += sizeof(struct inotify_event) + e->len;
if (!e->len || !is_valid_module_file(e->name))
goto next_event;
strncpy((*file_list)[idx], e->name, STK_PATH_MAX - 1);
event_type = STK_MOD_UNLOAD;
if (e->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)) {
if (is_module_loaded(e->name, loaded_ids, loaded_count) >= 0)
event_type = STK_MOD_RELOAD;
else
event_type = STK_MOD_LOAD;
} }
evs[idx++] = event_type;
next_event: if (count == 0) {
ptr += sizeof(struct inotify_event) + e->len; *out_count = 0;
goto process_inotify_loop; return NULL;
}
evs = malloc(count * sizeof(stk_module_event_t));
*file_list = malloc(count * sizeof(**file_list));
if (!evs || !*file_list) {
free(evs);
free(*file_list);
*out_count = 0;
return NULL;
}
ptr = buf;
while (ptr < end && idx < count) {
e = (struct inotify_event *)ptr;
if (e->len && is_valid_module_file(e->name)) {
if (e->mask & (IN_DELETE | IN_MOVED_FROM)) {
char *ptr2 =
ptr + sizeof(struct inotify_event) + e->len;
int has_create = 0;
while (ptr2 < end) {
struct inotify_event *e2 =
(struct inotify_event *)ptr2;
if (e2->len &&
strcmp(e->name, e2->name) == 0 &&
(e2->mask &
(IN_CLOSE_WRITE | IN_MOVED_TO))) {
has_create = 1;
break;
}
ptr2 += sizeof(struct inotify_event) +
e2->len;
}
if (has_create) {
ptr += sizeof(struct inotify_event) +
e->len;
continue;
}
}
strncpy((*file_list)[idx], e->name, STK_PATH_MAX - 1);
event_type = STK_MOD_UNLOAD;
if (e->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)) {
if (is_module_loaded(e->name, loaded_ids,
loaded_count) >= 0)
event_type = STK_MOD_RELOAD;
else
event_type = STK_MOD_LOAD;
}
evs[idx++] = event_type;
}
ptr += sizeof(struct inotify_event) + e->len;
}
linux_done:
*out_count = idx; *out_count = idx;
return evs; return evs;
@@ -543,41 +646,52 @@ linux_done:
platform_snapshot_t *new_snaps = NULL; platform_snapshot_t *new_snaps = NULL;
size_t new_count = 0, i, j, ev_idx = 0; size_t new_count = 0, i, j, ev_idx = 0;
stk_module_event_t *evs = NULL; stk_module_event_t *evs = NULL;
void *tmp_ptr;
int found; int found;
#ifdef _WIN32 #ifdef _WIN32
WIN32_FIND_DATAA fd; WIN32_FIND_DATAA fd;
HANDLE h; HANDLE h;
char s[STK_PATH_MAX_OS]; char s[STK_PATH_MAX_OS];
size_t count = 0;
if (WaitForSingleObject(ctx->watch.change_handle, 0) != WAIT_OBJECT_0)
goto no_change;
FindNextChangeNotification(ctx->watch.change_handle);
sprintf(s, "%s\\*", ctx->path); sprintf(s, "%s\\*", ctx->path);
h = FindFirstFileA(s, &fd); h = FindFirstFileA(s, &fd);
if (h == INVALID_HANDLE_VALUE) if (h == INVALID_HANDLE_VALUE)
goto build_diff; goto build_diff;
win_snap_loop: do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
is_valid_module_file(fd.cFileName)) { is_valid_module_file(fd.cFileName))
tmp_ptr = realloc(new_snaps, (new_count + 1) * count++;
sizeof(platform_snapshot_t)); } while (FindNextFileA(h, &fd));
if (!tmp_ptr)
goto win_next; FindClose(h);
new_snaps = tmp_ptr;
strncpy(new_snaps[new_count].filename, fd.cFileName, if (count == 0)
STK_PATH_MAX - 1); goto build_diff;
new_snaps[new_count++].mtime = fd.ftLastWriteTime;
new_snaps = malloc(count * sizeof(platform_snapshot_t));
if (!new_snaps)
goto build_diff;
h = FindFirstFileA(s, &fd);
if (h == INVALID_HANDLE_VALUE) {
free(new_snaps);
new_snaps = NULL;
goto build_diff;
} }
win_next:
if (FindNextFileA(h, &fd)) do {
goto win_snap_loop; if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
is_valid_module_file(fd.cFileName) && new_count < count) {
strncpy(new_snaps[new_count].filename, fd.cFileName,
STK_PATH_MAX - 1);
new_snaps[new_count].mtime = fd.ftLastWriteTime;
new_count++;
}
} while (FindNextFileA(h, &fd));
FindClose(h); FindClose(h);
goto build_diff;
#else #else
struct kevent kev; struct kevent kev;
@@ -586,6 +700,7 @@ win_next:
struct dirent *e; struct dirent *e;
struct stat st; struct stat st;
char f[STK_PATH_MAX_OS]; char f[STK_PATH_MAX_OS];
size_t count = 0;
if (kevent(ctx->watch.k.kq, NULL, 0, &kev, 1, &ts) <= 0) if (kevent(ctx->watch.k.kq, NULL, 0, &kev, 1, &ts) <= 0)
goto no_change; goto no_change;
@@ -594,9 +709,38 @@ win_next:
if (!d) if (!d)
goto bsd_update; goto bsd_update;
bsd_snap_loop: bsd_count_loop:
e = readdir(d); e = readdir(d);
if (!e) if (!e)
goto bsd_count_done;
if (!is_valid_module_file(e->d_name))
goto bsd_count_loop;
sprintf(f, "%s/%s", ctx->path, e->d_name);
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
goto bsd_count_loop;
count++;
goto bsd_count_loop;
bsd_count_done:
if (count == 0) {
closedir(d);
goto bsd_update;
}
new_snaps = malloc(count * sizeof(platform_snapshot_t));
if (!new_snaps) {
closedir(d);
goto bsd_update;
}
rewinddir(d);
bsd_snap_loop:
e = readdir(d);
if (!e || new_count >= count)
goto bsd_snap_done; goto bsd_snap_done;
if (!is_valid_module_file(e->d_name)) if (!is_valid_module_file(e->d_name))
@@ -606,15 +750,9 @@ bsd_snap_loop:
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode)) if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
goto bsd_snap_loop; goto bsd_snap_loop;
tmp_ptr =
realloc(new_snaps, (new_count + 1) * sizeof(platform_snapshot_t));
if (!tmp_ptr)
goto bsd_snap_loop;
new_snaps = tmp_ptr;
strncpy(new_snaps[new_count].filename, e->d_name, STK_PATH_MAX - 1); strncpy(new_snaps[new_count].filename, e->d_name, STK_PATH_MAX - 1);
new_snaps[new_count++].mtime = st.st_mtime; new_snaps[new_count].mtime = st.st_mtime;
new_count++;
goto bsd_snap_loop; goto bsd_snap_loop;
bsd_snap_done: bsd_snap_done:
@@ -622,10 +760,11 @@ bsd_snap_done:
bsd_update: bsd_update:
update_watches(ctx); update_watches(ctx);
goto build_diff;
#endif #endif
#ifdef _WIN32
build_diff: build_diff:
#endif
evs = malloc((ctx->count + new_count + 1) * sizeof(stk_module_event_t)); evs = malloc((ctx->count + new_count + 1) * sizeof(stk_module_event_t));
*file_list = malloc((ctx->count + new_count + 1) * sizeof(**file_list)); *file_list = malloc((ctx->count + new_count + 1) * sizeof(**file_list));
if (!evs || !*file_list) if (!evs || !*file_list)
@@ -642,14 +781,25 @@ build_diff:
#ifdef _WIN32 #ifdef _WIN32
if (CompareFileTime(&ctx->snaps[i].mtime, if (CompareFileTime(&ctx->snaps[i].mtime,
&new_snaps[j].mtime) != 0) { &new_snaps[j].mtime) != 0) {
if (is_file_ready(ctx->path,
new_snaps[j].filename)) {
strncpy((*file_list)[ev_idx],
new_snaps[j].filename,
STK_PATH_MAX - 1);
evs[ev_idx++] = STK_MOD_RELOAD;
} else {
new_snaps[j].mtime =
ctx->snaps[i].mtime;
}
}
#else #else
if (ctx->snaps[i].mtime != new_snaps[j].mtime) { if (ctx->snaps[i].mtime != new_snaps[j].mtime) {
#endif
strncpy((*file_list)[ev_idx], strncpy((*file_list)[ev_idx],
new_snaps[j].filename, new_snaps[j].filename,
STK_PATH_MAX - 1); STK_PATH_MAX - 1);
evs[ev_idx++] = STK_MOD_RELOAD; evs[ev_idx++] = STK_MOD_RELOAD;
} }
#endif
break; break;
} }
if (!found) { if (!found) {
@@ -693,7 +843,9 @@ cleanup_error:
cleanup_empty: cleanup_empty:
free(new_snaps); free(new_snaps);
#ifndef _WIN32
no_change: no_change:
#endif
*out_count = 0; *out_count = 0;
return NULL; return NULL;
#endif #endif