Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# MSVC: treat source files as UTF-8 (the codebase contains UTF-8 string
# literals such as tags). Without this, MSVC defaults to the system
# code page and emits C4819 warnings or garbles multi-byte literals.
# Use a generator expression so /utf-8 is NOT passed to nvcc (which would
# misinterpret the leading slash as a file path).
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/utf-8>)
endif()

# ── nlohmann/json (header-only, prefer system install, fallback to download) ──
find_package(nlohmann_json CONFIG QUIET)
if(NOT nlohmann_json_FOUND)
Expand Down
80 changes: 74 additions & 6 deletions server/src/deepseek4/deepseek4_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
#include <vector>
#include <thread>
#include <atomic>
#include <unistd.h>
#include <fcntl.h>

extern "C" bool ggml_backend_cuda_buffer_is_managed(ggml_backend_buffer_t buffer);
Expand All @@ -51,21 +50,82 @@ namespace {
struct DS4Mmap {
void * addr = nullptr;
size_t len = 0;
#if defined(_WIN32)
HANDLE hFile = INVALID_HANDLE_VALUE;
HANDLE hMap = nullptr;
#else
int fd = -1;
#endif

bool is_fd_open() const {
#if defined(_WIN32)
return hFile != INVALID_HANDLE_VALUE;
#else
return fd >= 0;
#endif
}
void close_fd() {
#if defined(_WIN32)
if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; }
#else
if (fd >= 0) { ::close(fd); fd = -1; }
#endif
}

bool open_ro(const std::string & path, std::string & err) {
#if defined(_WIN32)
// Convert UTF-8 path to UTF-16 for CreateFileW — CreateFileA uses the
// active ANSI code page and fails on non-ASCII paths (e.g. CJK chars).
int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), -1, nullptr, 0);
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
std::wstring wpath(wlen, L'\0');
MultiByteToWideChar(CP_UTF8, 0, path.c_str(), -1, wpath.data(), wlen);

hFile = CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE) {
err = "CreateFileW: " + path + ": error " + std::to_string(GetLastError());
return false;
}
LARGE_INTEGER sz;
if (!GetFileSizeEx(hFile, &sz)) {
err = "GetFileSizeEx: error " + std::to_string(GetLastError());
CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE;
return false;
}
len = (size_t)sz.QuadPart;
hMap = CreateFileMappingW(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (!hMap) {
err = "CreateFileMappingW: error " + std::to_string(GetLastError());
CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE;
return false;
}
addr = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
if (!addr) {
err = "MapViewOfFile: error " + std::to_string(GetLastError());
CloseHandle(hMap); hMap = nullptr;
CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE;
return false;
}
#else
fd = ::open(path.c_str(), O_RDONLY);
if (fd < 0) { err = "open: " + path + " " + strerror(errno); return false; }
struct stat st;
if (fstat(fd, &st) < 0) { err = "fstat"; ::close(fd); fd = -1; return false; }
len = (size_t)st.st_size;
addr = ::mmap(nullptr, len, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) { err = "mmap"; addr = nullptr; ::close(fd); fd = -1; return false; }
#endif
return true;
}
void close_map() {
#if defined(_WIN32)
if (addr) { UnmapViewOfFile(addr); addr = nullptr; }
if (hMap) { CloseHandle(hMap); hMap = nullptr; }
if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; }
#else
if (addr) { ::munmap(addr, len); addr = nullptr; }
if (fd >= 0) { ::close(fd); fd = -1; }
#endif
}
};

Expand Down Expand Up @@ -504,7 +564,12 @@ bool load_deepseek4_gguf_partial(const std::string & path,
a.file_offset = data_offset + a.tensor_offset;
}

#if !defined(_WIN32)
bool fast_managed = (buf != nullptr) && ggml_backend_cuda_buffer_is_managed(buf) && (getenv("DFLASH_NO_PREAD") == nullptr);
#else
// pread/posix_fadvise not available on Windows; fall back to mmap path.
bool fast_managed = false;
#endif
if (fast_managed) {
// Unified/managed buffer: read weights straight off disk into it in parallel at
// disk bandwidth, instead of mmap page-faults (~5x slower). Drop the cached file
Expand All @@ -522,8 +587,12 @@ bool load_deepseek4_gguf_partial(const std::string & path,
char * dst = (char *) a.tensor->data;
size_t done = 0;
while (done < a.file_size) {
#if !defined(_WIN32)
ssize_t r = pread(mmap.fd, dst + done, a.file_size - done,
(off_t) (a.file_offset + done));
(off_t) (a.file_offset + done));
#else
int r = -1; // not reached: fast_managed is false on Windows
#endif
if (r <= 0) { read_ok = false; return; }
done += (size_t) r;
}
Expand All @@ -532,7 +601,9 @@ bool load_deepseek4_gguf_partial(const std::string & path,
std::vector<std::thread> pool;
for (unsigned t = 0; t < nth; t++) pool.emplace_back(worker);
for (auto & th : pool) th.join();
#if !defined(_WIN32)
posix_fadvise(mmap.fd, 0, (off_t) mmap.len, POSIX_FADV_DONTNEED);
#endif
ggml_backend_synchronize(backend); // make CPU-written managed pages visible to GPU
if (!read_ok) {
set_last_error("parallel weight read failed");
Expand Down Expand Up @@ -809,10 +880,7 @@ bool build_deepseek4_moe_hybrid_storage_from_file_with_mmap(
if (err) *err = mmap_err;
return false;
}
if (mmap.fd >= 0) {
::close(mmap.fd);
mmap.fd = -1;
}
mmap.close_fd();

const size_t data_start = gguf_get_data_offset(gctx);
const auto * file_bytes = static_cast<const uint8_t *>(mmap.addr);
Expand Down
5 changes: 5 additions & 0 deletions server/src/ipc/backend_ipc_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
#include <string>
#include <vector>

#if defined(_WIN32)
#define setenv(name, value, overwrite) _putenv_s(name, value)
#define unsetenv(name) _putenv_s(name, "")
#endif

using namespace dflash::common;

namespace {
Expand Down
5 changes: 5 additions & 0 deletions server/src/laguna/laguna_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
#include <sstream>
#include "common/gguf_mmap.h"

#if defined(_WIN32)
#define setenv(name, value, overwrite) _putenv_s(name, value)
#define unsetenv(name) _putenv_s(name, "")
#endif

namespace dflash::common {

namespace {
Expand Down
59 changes: 50 additions & 9 deletions server/src/server/http_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ static inline const char* sock_strerror() {
snprintf(buf, sizeof(buf), "WSA error %d", WSAGetLastError());
return buf;
}
static inline int sock_errno() { return WSAGetLastError(); }
static inline bool sock_is_eintr (int e) { return e == WSAEINTR; }
static inline bool sock_is_eagain(int e) { return e == WSAEWOULDBLOCK; }
#else
#include <fcntl.h>
#include <sys/stat.h>
Expand All @@ -73,6 +76,9 @@ 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 strerror(errno); }
static inline int sock_errno() { return errno; }
static inline bool sock_is_eintr (int e) { return e == EINTR; }
static inline bool sock_is_eagain(int e) { return e == EAGAIN || e == EWOULDBLOCK; }
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
Expand Down Expand Up @@ -957,13 +963,13 @@ static bool sse_try_send(int fd, const void * data, size_t len) {
int ret;
do {
ret = poll(&pfd, 1, (int)(remaining < 50 ? remaining : 50));
} while (ret < 0 && errno == EINTR);
} while (ret < 0 && sock_is_eintr(sock_errno()));
if (ret < 0 || (pfd.revents & (POLLERR | POLLHUP | POLLNVAL))) return false;
if (ret == 0) continue;

ssize_t n = ::send(fd, p + sent, len - sent, MSG_NOSIGNAL);
if (n < 0) {
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) continue;
if (sock_is_eintr(sock_errno()) || sock_is_eagain(sock_errno())) continue;
return false;
}
sent += n;
Expand Down Expand Up @@ -1091,6 +1097,13 @@ int HttpServer::run() {
#if !defined(_WIN32)
// Ignore SIGPIPE so send() returns EPIPE instead of killing the process.
signal(SIGPIPE, SIG_IGN);
#else
// Initialise Winsock — required before any socket call on Windows.
WSADATA wsa_data;
if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) {
std::fprintf(stderr, "[server] WSAStartup() failed: %d\n", WSAGetLastError());
return 1;
}
#endif

// Create listen socket.
Expand Down Expand Up @@ -1155,7 +1168,7 @@ int HttpServer::run() {
int pr = poll(&pfd, 1, 200 /* ms */);
if (pr <= 0) {
// 0 = timeout (re-check stopping_); <0 with EINTR = signal. Both loop.
if (pr < 0 && errno != EINTR) {
if (pr < 0 && !sock_is_eintr(sock_errno())) {
std::fprintf(stderr, "[server] poll() error: %s\n", sock_strerror());
}
continue;
Expand All @@ -1166,7 +1179,7 @@ int HttpServer::run() {
int client_fd = accept(listen_fd_, (struct sockaddr *)&client_sa, &client_len);
if (client_fd < 0) {
if (stopping_.load()) break;
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) continue;
if (sock_is_eintr(sock_errno()) || sock_is_eagain(sock_errno())) continue;
std::fprintf(stderr, "[server] accept() error: %s\n", sock_strerror());
continue;
}
Expand Down Expand Up @@ -1223,6 +1236,15 @@ int HttpServer::run() {
slot_tokens_.clear();
}

#if defined(_WIN32)
// Intentionally NOT calling WSACleanup() here. Detached client threads
// may still be running after the 5-second shutdown grace period (e.g. a
// client mid-stream on a long SSE generation). Tearing down Winsock
// underneath them causes spurious socket errors. The OS reclaims all
// Winsock resources on process exit, so retaining it for the full process
// lifetime is safe and avoids the race.
#endif

return 0;
}

Expand Down Expand Up @@ -3366,6 +3388,11 @@ ServerJob * HttpServer::dequeue() {
// ─── HTTP I/O ───────────────────────────────────────────────────────────

bool HttpServer::read_http_request(int fd, HttpRequest & out) {
#if defined(_WIN32)
// On Windows, accept() may return a socket that inherits the non-blocking
// mode of the listen socket. Force blocking mode for reliable recv().
sock_set_block(fd);
#endif
std::string buf;
buf.reserve(8192);
char tmp[4096];
Expand All @@ -3374,7 +3401,14 @@ bool HttpServer::read_http_request(int fd, HttpRequest & out) {
ssize_t hend = -1;
while (hend < 0 && buf.size() < 65536) {
ssize_t n = recv(fd, tmp, sizeof(tmp), 0);
if (n < 0 && errno == EINTR) continue;
if (n < 0 && sock_is_eintr(sock_errno())) continue;
#if defined(_WIN32)
if (n < 0 && sock_is_eagain(sock_errno())) {
struct pollfd pfd{SOCK_FD(fd), POLLIN, 0};
poll(&pfd, 1, 1000);
continue;
}
#endif
if (n <= 0) return false;
buf.append(tmp, n);

Expand Down Expand Up @@ -3440,7 +3474,14 @@ bool HttpServer::read_http_request(int fd, HttpRequest & out) {
// Read body.
while ((ssize_t)buf.size() < hend + content_length) {
ssize_t n = recv(fd, tmp, sizeof(tmp), 0);
if (n < 0 && errno == EINTR) continue;
if (n < 0 && sock_is_eintr(sock_errno())) continue;
#if defined(_WIN32)
if (n < 0 && sock_is_eagain(sock_errno())) {
struct pollfd pfd{SOCK_FD(fd), POLLIN, 0};
poll(&pfd, 1, 1000);
continue;
}
#endif
if (n <= 0) return false;
buf.append(tmp, n);
}
Expand All @@ -3464,14 +3505,14 @@ bool HttpServer::send_all(int fd, const void * data, size_t len) {
int ret;
do {
ret = poll(&pfd, 1, timeout);
} while (ret < 0 && errno == EINTR);
} while (ret < 0 && sock_is_eintr(sock_errno()));
if (ret < 0 || (pfd.revents & (POLLERR | POLLHUP | POLLNVAL))) return false;
if (ret == 0) continue; // poll timeout, retry until deadline

ssize_t n = send(fd, p + sent, len - sent, MSG_NOSIGNAL);
if (n < 0) {
if (errno == EINTR) continue;
if (errno == EAGAIN || errno == EWOULDBLOCK) continue;
if (sock_is_eintr(sock_errno())) continue;
if (sock_is_eagain(sock_errno())) continue;
return false; // EPIPE, ECONNRESET, etc.
}
sent += n;
Expand Down