Skip to content

fix: port POSIX-only files to Windows for HIP builds#369

Open
Sprize1 wants to merge 2 commits into
Luce-Org:mainfrom
Sprize1:fix/windows-hip-build-v2
Open

fix: port POSIX-only files to Windows for HIP builds#369
Sprize1 wants to merge 2 commits into
Luce-Org:mainfrom
Sprize1:fix/windows-hip-build-v2

Conversation

@Sprize1

@Sprize1 Sprize1 commented Jun 10, 2026

Copy link
Copy Markdown

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.cpp

No functional change on Linux/macOS

All Windows-specific code behind #if defined(_WIN32).

Tested

  • dflash_server.exe builds cleanly (91 MB)
  • test_dflash.exe builds cleanly (89 MB)
  • Platform: Windows 11 + ROCm 7.1 HIP + gfx1102 (RX 7600 XT 16GB)

Related: #367

🤖 Generated with Claude Code

Sprize1 and others added 2 commits June 10, 2026 22:45
- 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>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(); }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
#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,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

Comment on lines +83 to +84
FILE * f = std::fopen(tmp_path.c_str(), "wb");
if (!f) return result;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
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)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

easel pushed a commit to easel/lucebox-hub that referenced this pull request Jun 10, 2026
@davide221

Copy link
Copy Markdown
Contributor

@Sprize1 thanks for you first contribution! Can you fix the CI errors?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants