fix: port POSIX-only files to Windows for HIP builds#369
Conversation
- Replace raw mmap/munmap with GgufMmap (cross-platform RAII) in gemma4_loader, laguna_backend, qwen35moe_backend, test_dflash - Replace dirent.h/stat with std::filesystem in disk_prefix_cache, model_card - Add Windows implementations for write_exact_fd/read_exact_fd in io_utils.h - Guard open_dflash_floor_log POSIX path with _WIN32 in qwen35_backend - Replace mkstemps with std::filesystem temp dir in daemon_loop - Port http_server socket/POSIX layer to Winsock2 (winsock2.h, WSAPoll, closesocket, ioctlsocket helpers) - Guard unistd.h in http_server.h, add setenv fallback in server_main - Make pthread link conditional (NOT WIN32), add ws2_32 on Windows, set BUILD_SHARED_LIBS=OFF for ggml symbol visibility - All Windows-only code behind #if defined(_WIN32), no functional change on Linux/macOS Tested: dflash_server.exe + test_dflash.exe build cleanly on Windows 11 + ROCm 7.1 HIP + gfx1102 (RX 7600 XT). Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
5 issues found and verified against the latest diff
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="server/src/server/server_main.cpp">
<violation number="1" location="server/src/server/server_main.cpp:37">
P1: Windows `setenv` macro ignores `overwrite` parameter, breaking non-overwrite semantics</violation>
</file>
<file name="server/src/gemma4/gemma4_loader.cpp">
<violation number="1" location="server/src/gemma4/gemma4_loader.cpp:50">
P2: Using `CreateFileA` for file open breaks GGUF loading on non-ASCII Windows paths. The project already uses `MultiByteToWideChar(CP_UTF8, ...)` + `CreateFileW` in `qwen3_loader.cpp` and the `GgufMmap` utility in `common/gguf_mmap.h`. This creates a platform asymmetry where POSIX `open()` is byte-transparent with UTF-8 but Windows `CreateFileA` silently fails for non-ASCII paths (common in localized user profiles). Consider using `CreateFileW` after UTF-16 conversion, or better, use the existing `GgufMmap` class from `common/gguf_mmap.h` which already encapsulates this correctly.</violation>
</file>
<file name="server/src/common/daemon_loop.cpp">
<violation number="1" location="server/src/common/daemon_loop.cpp:83">
P2: Windows temp file creation loses atomic exclusive-create guarantee of POSIX mkstemps</violation>
</file>
<file name="server/src/server/http_server.cpp">
<violation number="1" location="server/src/server/http_server.cpp:70">
P0: POSIX `sock_strerror()` calls itself recursively with no base case, causing stack overflow on any socket error path on Linux/macOS.</violation>
</file>
<file name="server/src/server/disk_prefix_cache.cpp">
<violation number="1" location="server/src/server/disk_prefix_cache.cpp:219">
P2: Range-based for loops with `std::filesystem::directory_iterator` call the throwing `operator++()`, creating an uncaught exception crash path during cache scanning.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| static inline void socket_close(int fd) { ::close(fd); } | ||
| #define SETSOCKOPT_CAST /* empty on POSIX */ | ||
| #include <unistd.h> | ||
| static inline const char* sock_strerror() { return sock_strerror(); } |
There was a problem hiding this comment.
P0: POSIX sock_strerror() calls itself recursively with no base case, causing stack overflow on any socket error path on Linux/macOS.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/server/http_server.cpp, line 67:
<comment>POSIX `sock_strerror()` calls itself recursively with no base case, causing stack overflow on any socket error path on Linux/macOS.</comment>
<file context>
@@ -22,15 +35,46 @@
+static inline void socket_close(int fd) { ::close(fd); }
+#define SETSOCKOPT_CAST /* empty on POSIX */
+#include <unistd.h>
+static inline const char* sock_strerror() { return sock_strerror(); }
#include <arpa/inet.h>
-#include <fcntl.h>
</file context>
| static inline const char* sock_strerror() { return sock_strerror(); } | |
| static inline const char* sock_strerror() { return strerror(errno); } |
| #include <vector> | ||
|
|
||
| #ifdef _WIN32 | ||
| #define setenv(name, value, overwrite) _putenv_s(name, value) |
There was a problem hiding this comment.
P1: Windows setenv macro ignores overwrite parameter, breaking non-overwrite semantics
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/server/server_main.cpp, line 37:
<comment>Windows `setenv` macro ignores `overwrite` parameter, breaking non-overwrite semantics</comment>
<file context>
@@ -33,6 +33,11 @@
#include <vector>
+#ifdef _WIN32
+#define setenv(name, value, overwrite) _putenv_s(name, value)
+#define unsetenv(name) _putenv_s(name, "")
+#endif
</file context>
| #define setenv(name, value, overwrite) _putenv_s(name, value) | |
| +#define setenv(name, value, overwrite) ((overwrite) || !getenv(name) ? _putenv_s(name, value) : 0) |
|
|
||
| bool open_ro(const std::string & path, std::string & err) { | ||
| #if defined(_WIN32) | ||
| hFile = CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, |
There was a problem hiding this comment.
P2: Using CreateFileA for file open breaks GGUF loading on non-ASCII Windows paths. The project already uses MultiByteToWideChar(CP_UTF8, ...) + CreateFileW in qwen3_loader.cpp and the GgufMmap utility in common/gguf_mmap.h. This creates a platform asymmetry where POSIX open() is byte-transparent with UTF-8 but Windows CreateFileA silently fails for non-ASCII paths (common in localized user profiles). Consider using CreateFileW after UTF-16 conversion, or better, use the existing GgufMmap class from common/gguf_mmap.h which already encapsulates this correctly.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/gemma4/gemma4_loader.cpp, line 50:
<comment>Using `CreateFileA` for file open breaks GGUF loading on non-ASCII Windows paths. The project already uses `MultiByteToWideChar(CP_UTF8, ...)` + `CreateFileW` in `qwen3_loader.cpp` and the `GgufMmap` utility in `common/gguf_mmap.h`. This creates a platform asymmetry where POSIX `open()` is byte-transparent with UTF-8 but Windows `CreateFileA` silently fails for non-ASCII paths (common in localized user profiles). Consider using `CreateFileW` after UTF-16 conversion, or better, use the existing `GgufMmap` class from `common/gguf_mmap.h` which already encapsulates this correctly.</comment>
<file context>
@@ -38,21 +38,57 @@ namespace {
bool open_ro(const std::string & path, std::string & err) {
+#if defined(_WIN32)
+ hFile = CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ,
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (hFile == INVALID_HANDLE_VALUE) {
</file context>
| FILE * f = std::fopen(tmp_path.c_str(), "wb"); | ||
| if (!f) return result; |
There was a problem hiding this comment.
P2: Windows temp file creation loses atomic exclusive-create guarantee of POSIX mkstemps
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/common/daemon_loop.cpp, line 83:
<comment>Windows temp file creation loses atomic exclusive-create guarantee of POSIX mkstemps</comment>
<file context>
@@ -65,23 +68,45 @@ ModelBackend::CompressResult ModelBackend::compress(const CompressRequest & req)
+ std::filesystem::path p =
+ std::filesystem::temp_directory_path() / ("pflash_" + uniq + ".bin");
+ tmp_path = p.string();
+ FILE * f = std::fopen(tmp_path.c_str(), "wb");
+ if (!f) return result;
+ const size_t w = std::fwrite(req.input_ids.data(), 1, to_write, f);
</file context>
| FILE * f = std::fopen(tmp_path.c_str(), "wb"); | |
| if (!f) return result; | |
| int fd = _open(tmp_path.c_str(), _O_CREAT | _O_EXCL | _O_BINARY | _O_WRONLY, 0600); | |
| if (fd < 0) return result; | |
| FILE * f = _fdopen(fd, "wb"); | |
| if (!f) { _close(fd); std::remove(tmp_path.c_str()); return result; } |
| size_t nlen = std::strlen(name); | ||
| if (nlen < 36 || std::strcmp(name + nlen - 4, ".dkv") != 0) continue; | ||
| std::error_code ec; | ||
| for (const auto & de : fs::directory_iterator(layout_dir_, ec)) { |
There was a problem hiding this comment.
P2: Range-based for loops with std::filesystem::directory_iterator call the throwing operator++(), creating an uncaught exception crash path during cache scanning.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/server/disk_prefix_cache.cpp, line 214:
<comment>Range-based for loops with `std::filesystem::directory_iterator` call the throwing `operator++()`, creating an uncaught exception crash path during cache scanning.</comment>
<file context>
@@ -213,16 +210,13 @@ void DiskPrefixCache::scan_directory() {
- size_t nlen = std::strlen(name);
- if (nlen < 36 || std::strcmp(name + nlen - 4, ".dkv") != 0) continue;
+ std::error_code ec;
+ for (const auto & de : fs::directory_iterator(layout_dir_, ec)) {
+ const std::string name = de.path().filename().string();
+ size_t nlen = name.size();
</file context>
|
@Sprize1 thanks for you first contribution! Can you fix the CI errors? |
Problem
The HIP backend fails to compile on Windows native because several source files use POSIX-only APIs.
Solution
Add cross-platform support behind
#if defined(_WIN32), using GgufMmap, std::filesystem, Winsock2.Files changed (13)
gemma4_loader.cpp,laguna_backend.cpp,qwen35moe_backend.cpp,qwen35_backend.cpp,io_utils.h,daemon_loop.cpp,disk_prefix_cache.cpp,model_card.cpp,server_main.cpp,http_server.cpp,http_server.h,CMakeLists.txt,test_dflash.cppNo functional change on Linux/macOS
All Windows-specific code behind
#if defined(_WIN32).Tested
dflash_server.exebuilds cleanly (91 MB)test_dflash.exebuilds cleanly (89 MB)Related: #367
🤖 Generated with Claude Code