fix(platform): implement atomic hot-reload synchronization

* Resolve STATUS_INVALID_IMAGE_FORMAT on Windows by preventing race conditions during file I/O.
* is_file_ready: Uses GENERIC_WRITE to block if any process (compiler, copy, etc.) is writing to the source. Added GetFileSize check to ensure headers are flushed.
* platform_copy_file: Copies to .tmp and uses MoveFileExA for an atomic swap, hiding the file from the loader until completion.
* Refactor: Unified Win32 and POSIX logic with a single exit point.
This commit is contained in:
2026-01-29 08:00:15 +01:00
parent fb80c3a6c5
commit 64435fd8cd
+18 -6
View File
@@ -32,14 +32,21 @@ void extract_module_id(const char *path, char *out_id);
static uint8_t is_file_ready(const char *dir_path, const char *filename)
{
char full_path[STK_PATH_MAX_OS];
DWORD size;
HANDLE h;
sprintf(full_path, "%s\\%s", dir_path, filename);
h = CreateFileA(full_path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
h = CreateFileA(full_path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
return 0;
size = GetFileSize(h, NULL);
CloseHandle(h);
if (size == INVALID_FILE_SIZE || size < 1024)
return 0;
return 1;
}
#endif
@@ -93,13 +100,19 @@ int platform_remove_file(const char *path)
int platform_copy_file(const char *from, const char *to)
{
char buf[STK_PATH_MAX_OS];
int ret = -1;
#ifdef _WIN32
return CopyFileA(from, to, FALSE) ? 0 : -1;
sprintf(buf, "%s.tmp", to);
if (CopyFileA(from, buf, FALSE)) {
if (MoveFileExA(buf, to, MOVEFILE_REPLACE_EXISTING))
ret = 0;
else
DeleteFileA(buf);
}
#else
FILE *src = NULL, *dst = NULL;
char buf[STK_PATH_MAX_OS];
size_t n;
int ret = -1;
src = fopen(from, "rb");
if (!src)
@@ -117,12 +130,11 @@ int platform_copy_file(const char *from, const char *to)
cleanup:
if (src)
fclose(src);
if (dst)
fclose(dst);
#endif
return ret;
#endif
}
int platform_remove_dir(const char *path)