From d064ee899f2460a58d838dad0d190bf0b581a17b Mon Sep 17 00:00:00 2001 From: Jay Leaton <58495321+jayleaton@users.noreply.github.com> Date: Sun, 18 Oct 2026 20:19:52 +0700 Subject: [PATCH 1/5] Windows: main's host tests build again on Windows hosts The zig-windows job fails 15 build steps on main 76aafcf2. Each fix uses the sys layer or an existing Windows gate, and none changes what Linux or macOS run: - std.DynLib, which does not build on Windows, becomes sys.DynLib (which is std.DynLib off Windows): webp, drama3 BLAS, and the copy-custody, native load and H3 runtime test fakes. - std.c.clock_gettime and nanosleep become sys.c's (the same calls on POSIX): Flash Next's rank wire, H3's clock. - Environ.getPosix becomes sys.getenv in the picture tests, as their JPEG case already does. - digest_key's portable fallback reads Windows' signed inode as its unsigned bits. - speech.zig sets TCP_NODELAY and the send stall through sys.posix on Windows; POSIX keeps its std.c call. - The rank wire's send, receive and peer check refuse off Linux as its open already does; Windows has no MSG.NOSIGNAL. - Windows skips POSIX-only tests: benchmark_config_test joins the benchmark tests' existing gate, the speech stream test and Flash Next's rank tests use Unix socket pairs, two death tests fork, the H3 checkpoint test maps with mmap, the listener test reads TCP_NODELAY back with getsockopt, and Flash Next's madvise hint has no Windows call. --- zig/build/cuda.zig | 3 ++- zig/src/core/digest_key.zig | 2 +- zig/src/core/picture/picture_test.zig | 2 +- zig/src/core/picture/webp.zig | 2 +- zig/src/cuda/copy_source_close_test.zig | 2 +- zig/src/cuda/copy_source_race_test.zig | 2 +- zig/src/families/drama3/cuda/blas.zig | 5 +++-- .../families/flashnext/cuda_lanes_restore_test.zig | 1 + zig/src/families/flashnext/cuda_ple.zig | 2 +- zig/src/families/flashnext/cuda_rank_media_test.zig | 1 + zig/src/families/flashnext/cuda_rank_wire.zig | 12 ++++++++---- zig/src/families/h3/cuda.zig | 5 +++-- zig/src/families/h3/cuda_runtime_fake.zig | 4 ++-- zig/src/families/nemotron/cuda_pages_safety_test.zig | 1 + zig/src/native/cuda_load_gpu.zig | 4 ++-- zig/src/server/listener.zig | 10 ++++++---- zig/src/server/root.zig | 7 ++----- zig/src/server/speech.zig | 7 ++++--- zig/src/vision/qwen_cuda_test.zig | 1 + 19 files changed, 42 insertions(+), 31 deletions(-) diff --git a/zig/build/cuda.zig b/zig/build/cuda.zig index 5fadb17f06..e484e60b04 100644 --- a/zig/build/cuda.zig +++ b/zig/build/cuda.zig @@ -358,7 +358,8 @@ pub fn hostTests(b: *std.Build, draft_ids: *std.Build.Module, build_options: *st _ = open_object.getEmittedBin(); b.step("compile-omni-video-host", "Compile actual native CUDA startup and offer for Linux without images or GPU execution").dependOn(&open_object.step); const h3_checkpoint = b.createModule(.{ .root_source_file = b.path("zig/src/families/h3/checkpoint.zig"), .target = host, .optimize = .debug, .link_libc = true }); - step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = h3_checkpoint })).step); + // the checkpoint maps its file with mmap: not Windows + if (host.result.os.tag != .windows) step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = h3_checkpoint })).step); // the HTTP server's unit tests (zig/src/server/root.zig), which the macOS build runs, on Linux hosts too const template = b.createModule(.{ .root_source_file = b.path("zig/src/core/template/template.zig"), .target = host, .optimize = .debug, .link_libc = true }); const server_tests = b.createModule(.{ diff --git a/zig/src/core/digest_key.zig b/zig/src/core/digest_key.zig index 966f1fd223..bf448c6a35 100644 --- a/zig/src/core/digest_key.zig +++ b/zig/src/core/digest_key.zig @@ -31,7 +31,7 @@ pub const Key = struct { } const s = try file.stat(io); if (s.kind != .file) return error.DigestNotRegular; - return .{ .device = 0, .inode = s.inode, .size = s.size, .mtime = s.mtime.toNanoseconds(), .ctime = s.ctime.toNanoseconds() }; + return .{ .device = 0, .inode = @as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(s.inode))), @bitCast(s.inode)), .size = s.size, .mtime = s.mtime.toNanoseconds(), .ctime = s.ctime.toNanoseconds() }; } pub fn equal(a: Key, b: Key) bool { diff --git a/zig/src/core/picture/picture_test.zig b/zig/src/core/picture/picture_test.zig index 287493a71c..80ddb5f4d1 100644 --- a/zig/src/core/picture/picture_test.zig +++ b/zig/src/core/picture/picture_test.zig @@ -119,7 +119,7 @@ test "edge pictures (zig/tests/fixtures/picture/edges.json) get 0.6.6's outcome, /// WebP cases need the pinned libwebp: required when TENSORFOLD_LIBWEBP names one, skipped when none loads. fn webpReady(a: std.mem.Allocator) !bool { if (picture.webp.available(std.testing.io, a)) return true; - if (std.testing.environ.getPosix(picture.webp.env) != null) return error.PinnedLibraryDidNotLoad; + if (@import("sys").getenv(picture.webp.env) != null) return error.PinnedLibraryDidNotLoad; std.debug.print("libwebp {s} not found: WebP pixel cases skipped\n", .{picture.webp.pinned}); return false; } diff --git a/zig/src/core/picture/webp.zig b/zig/src/core/picture/webp.zig index db6d9f7131..b45a2180e1 100644 --- a/zig/src/core/picture/webp.zig +++ b/zig/src/core/picture/webp.zig @@ -66,7 +66,7 @@ fn find(io: std.Io, a: Allocator) Loaded { /// One candidate: null when it does not load or lacks a function; `.wrong` when its version is not the pin. fn open(path: []const u8) ?Loaded { - var dl = std.DynLib.open(path) catch return null; + var dl = @import("sys").DynLib.open(path) catch return null; var f: Fns = undefined; const info = @typeInfo(Fns).@"struct"; inline for (info.field_names, info.field_types) |name, T| { diff --git a/zig/src/cuda/copy_source_close_test.zig b/zig/src/cuda/copy_source_close_test.zig index d1bf218d06..f71959f230 100644 --- a/zig/src/cuda/copy_source_close_test.zig +++ b/zig/src/cuda/copy_source_close_test.zig @@ -84,7 +84,7 @@ pub fn run(reap: *const fn (*const Driver) void) !void { Fake.s = .{}; Fake.reap = reap; var d: Driver = undefined; - d.lib = try std.DynLib.open(if (builtin.os.tag == .windows) "kernel32.dll" else if (builtin.os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); + d.lib = try @import("sys").DynLib.open(if (builtin.os.tag == .windows) "kernel32.dll" else if (builtin.os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); d.api.cuMemHostAlloc = Fake.alloc; d.api.cuMemFreeHost = Fake.free; d.api.cuMemcpyHtoDAsync_v2 = Fake.copy; diff --git a/zig/src/cuda/copy_source_race_test.zig b/zig/src/cuda/copy_source_race_test.zig index 0eed0c9a1b..68583dfb15 100644 --- a/zig/src/cuda/copy_source_race_test.zig +++ b/zig/src/cuda/copy_source_race_test.zig @@ -97,7 +97,7 @@ pub fn run(reaper: *const fn (*const Driver) void, completing: bool) !void { Fake.reaper = reaper; Fake.completing = completing; var d: Driver = undefined; - d.lib = try std.DynLib.open(if (builtin.os.tag == .windows) "kernel32.dll" else if (builtin.os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); + d.lib = try @import("sys").DynLib.open(if (builtin.os.tag == .windows) "kernel32.dll" else if (builtin.os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); d.api.cuMemHostAlloc = Fake.alloc; d.api.cuMemFreeHost = Fake.free; d.api.cuMemcpyHtoDAsync_v2 = Fake.copy; diff --git a/zig/src/families/drama3/cuda/blas.zig b/zig/src/families/drama3/cuda/blas.zig index 60ff29a991..97a78c72c5 100644 --- a/zig/src/families/drama3/cuda/blas.zig +++ b/zig/src/families/drama3/cuda/blas.zig @@ -1,5 +1,6 @@ //! cuBLAS (the classic API) opened at run time: the strided batched GEMM Drama 3's prompt rows and codec take. const std = @import("std"); +const platform = @import("sys"); const cuda = @import("cuda"); pub const Status = c_int; @@ -33,7 +34,7 @@ pub const Api = struct { /// The library and one handle with its workspace, bound to a stream per call. pub const Blas = struct { - lib: std.DynLib, + lib: platform.DynLib, api: Api, h: Handle, @@ -49,7 +50,7 @@ pub const Blas = struct { } fn openPath(path: []const u8, workspace: Ptr, workspace_bytes: usize) Error!Blas { - var lib = std.DynLib.open(path) catch return error.LibraryUnavailable; + var lib = platform.DynLib.open(path) catch return error.LibraryUnavailable; errdefer lib.close(); var b: Blas = .{ .lib = lib, .api = undefined, .h = null }; const info = @typeInfo(Api).@"struct"; diff --git a/zig/src/families/flashnext/cuda_lanes_restore_test.zig b/zig/src/families/flashnext/cuda_lanes_restore_test.zig index e0e5967ecf..aa441f6166 100644 --- a/zig/src/families/flashnext/cuda_lanes_restore_test.zig +++ b/zig/src/families/flashnext/cuda_lanes_restore_test.zig @@ -49,6 +49,7 @@ pub fn fakeDriver() cuda.Driver { /// Exercise the actual lane open and explicit snapshot entry points with a CPU control peer. pub fn run(comptime Cuda: type, comptime open: anytype, comptime restore: anytype, comptime drop: anytype, explicit: bool, drafting: bool) !void { + if (@import("builtin").os.tag == .windows) return error.SkipZigTest; // the peer rank is a Unix socket pair const a = std.testing.allocator; Device.next = 0x100000; Device.copies = 0; diff --git a/zig/src/families/flashnext/cuda_ple.zig b/zig/src/families/flashnext/cuda_ple.zig index 459f56905d..1ea1fe4e7b 100644 --- a/zig/src/families/flashnext/cuda_ple.zig +++ b/zig/src/families/flashnext/cuda_ple.zig @@ -388,7 +388,7 @@ pub const Ngram = struct { const file = findOpen(g.files.items, path) orelse file: { var o = try Open.load(gpa, io, path); errdefer o.close(gpa); - std.posix.madvise(o.file.map.memory.ptr, o.file.map.memory.len, std.posix.MADV.RANDOM) catch {}; + if (@import("builtin").os.tag != .windows) std.posix.madvise(o.file.map.memory.ptr, o.file.map.memory.len, std.posix.MADV.RANDOM) catch {}; try g.files.append(gpa, o); break :file &g.files.items[g.files.items.len - 1]; }; diff --git a/zig/src/families/flashnext/cuda_rank_media_test.zig b/zig/src/families/flashnext/cuda_rank_media_test.zig index ee81179206..45d9a74a6e 100644 --- a/zig/src/families/flashnext/cuda_rank_media_test.zig +++ b/zig/src/families/flashnext/cuda_rank_media_test.zig @@ -82,6 +82,7 @@ fn encoder(d: *const cuda.Driver, s: cuda.Stream) shared.media.Encoder { /// A rank of the pair over one end of a socket pair, its collective faked; the other end is the peer that leaves. fn session(engine: *prompt.Engine, rank: usize, pair: *[2]c_int) !ranks.Session { + if (@import("builtin").os.tag == .windows) return error.SkipZigTest; // the peer rank is a Unix socket pair if (std.c.socketpair(std.posix.AF.UNIX, std.posix.SOCK.STREAM, 0, pair) != 0) return error.SocketPair; const nonblock: c_int = @bitCast(@as(u32, @bitCast(std.c.O{ .NONBLOCK = true }))); for (pair) |fd| { diff --git a/zig/src/families/flashnext/cuda_rank_wire.zig b/zig/src/families/flashnext/cuda_rank_wire.zig index a49429688d..81b9ff26f4 100644 --- a/zig/src/families/flashnext/cuda_rank_wire.zig +++ b/zig/src/families/flashnext/cuda_rank_wire.zig @@ -1,6 +1,7 @@ //! Bounded Linux TCP control channel. Tensor bytes travel over NCCL; this channel owns command order and peer EOF. const std = @import("std"); const builtin = @import("builtin"); +const platform = @import("sys"); const p = std.posix; const net = std.Io.net; @@ -43,8 +44,8 @@ pub const Header = struct { }; pub fn now() u64 { - var ts: std.c.timespec = undefined; - if (std.c.clock_gettime(.MONOTONIC, &ts) != 0 or ts.sec < 0) return 0; + var ts: platform.c.timespec = undefined; + if (platform.c.clock_gettime(platform.c.CLOCK.MONOTONIC, &ts) != 0 or ts.sec < 0) return 0; return @as(u64, @intCast(ts.sec)) * std.time.ns_per_ms * 1000 + @as(u64, @intCast(ts.nsec)); } @@ -53,8 +54,8 @@ pub fn deadline(ms: u64) u64 { } pub fn pause() void { - const ts: std.c.timespec = .{ .sec = 0, .nsec = 50_000 }; - _ = std.c.nanosleep(&ts, null); + const ts: platform.c.timespec = .{ .sec = 0, .nsec = 50_000 }; + _ = platform.c.nanosleep(&ts, null); } fn wait(fd: p.socket_t, events: i16, until: ?u64) !void { @@ -131,6 +132,7 @@ pub const Link = struct { } pub fn send(l: Link, bytes: []const u8, until: u64) !void { + if (builtin.os.tag != .linux) return error.RankPlatform; var at: usize = 0; while (at < bytes.len) { if (now() >= until) return error.RankTimeout; @@ -148,6 +150,7 @@ pub const Link = struct { } pub fn receive(l: Link, bytes: []u8, until: ?u64) !void { + if (builtin.os.tag != .linux) return error.RankPlatform; var at: usize = 0; while (at < bytes.len) { if (until) |end| if (now() >= end) return error.RankTimeout; @@ -179,6 +182,7 @@ pub const Link = struct { /// A non-consuming EOF check while a GPU collective progresses. A future control frame stays queued. pub fn checkPeer(l: Link) !void { + if (builtin.os.tag != .linux) return error.RankPlatform; var byte: u8 = 0; const rc = std.c.recv(l.fd, &byte, 1, p.MSG.PEEK | p.MSG.DONTWAIT); if (rc == 0) return error.RankDisconnected; diff --git a/zig/src/families/h3/cuda.zig b/zig/src/families/h3/cuda.zig index 55c562b5db..1bd9206a03 100644 --- a/zig/src/families/h3/cuda.zig +++ b/zig/src/families/h3/cuda.zig @@ -1,5 +1,6 @@ //! MiniMax H3 / FastH3's transformer blocks over the CUDA runtime in the checkpoint's bf16; the host owns the rest. const std = @import("std"); +const platform = @import("sys"); const cuda = @import("cuda"); const weights = @import("cuda_weights.zig"); pub const geometry = @import("cuda_geometry.zig"); @@ -26,8 +27,8 @@ pub const stages = [_][]const u8{ "norms", "q k v gate", "heads", "routing", "at const attention_shared = 3 * slot_tile * head_dim * 2; pub fn seconds() f64 { - var ts: std.c.timespec = undefined; - if (std.c.clock_gettime(.MONOTONIC, &ts) != 0) return 0; + var ts: platform.c.timespec = undefined; + if (platform.c.clock_gettime(platform.c.CLOCK.MONOTONIC, &ts) != 0) return 0; return @as(f64, @floatFromInt(ts.sec)) + @as(f64, @floatFromInt(ts.nsec)) * 1e-9; } diff --git a/zig/src/families/h3/cuda_runtime_fake.zig b/zig/src/families/h3/cuda_runtime_fake.zig index 24bdfe8a5b..8b4721f9b1 100644 --- a/zig/src/families/h3/cuda_runtime_fake.zig +++ b/zig/src/families/h3/cuda_runtime_fake.zig @@ -101,7 +101,7 @@ pub fn model() !*h3.Model { const m = try std.testing.allocator.create(h3.Model); m.* = undefined; m.gpa = std.testing.allocator; - m.driver.lib = try std.DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); + m.driver.lib = try @import("sys").DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); m.driver.api.cuCtxSetCurrent = current; m.driver.api.cuCtxSynchronize = sync; m.driver.api.cuStreamSynchronize = streamSync; @@ -124,7 +124,7 @@ pub fn model() !*h3.Model { m.blocks = try m.gpa.alloc(h3.Block, 1); m.blocks[0] = .{ .qkvc = empty, .out = empty, .fc1 = empty, .fc2 = empty, .norms = empty }; m.gemms = .{ .gpa = m.gpa, .lt = undefined, .workspace = empty }; - m.gemms.lt.lib = try std.DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); + m.gemms.lt.lib = try @import("sys").DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); m.hidden = 128; m.inner = 128; m.heads = 1; diff --git a/zig/src/families/nemotron/cuda_pages_safety_test.zig b/zig/src/families/nemotron/cuda_pages_safety_test.zig index 057c443672..3a00438422 100644 --- a/zig/src/families/nemotron/cuda_pages_safety_test.zig +++ b/zig/src/families/nemotron/cuda_pages_safety_test.zig @@ -159,6 +159,7 @@ const Child = struct { extern "c" fn fork() c_int; }; test "kv safety: paged constructor fatal completion failure precedes every destination or parent release" { + if (@import("builtin").os.tag == .windows) return error.SkipZigTest; // the child process comes from fork for ([_]Case{ .clear, .slots, .cpu, .wait }) |case| { const pid = Child.fork(); if (pid < 0) return error.ForkFailed; diff --git a/zig/src/native/cuda_load_gpu.zig b/zig/src/native/cuda_load_gpu.zig index 41d8abd90e..bc3d74626c 100644 --- a/zig/src/native/cuda_load_gpu.zig +++ b/zig/src/native/cuda_load_gpu.zig @@ -156,7 +156,7 @@ const Fake = struct { const g = try t.allocator.create(Gpu); g.gpa = t.allocator; g.next = null; - g.driver.lib = try std.DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); + g.driver.lib = try @import("sys").DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); g.driver.api.cuMemAlloc_v2 = alloc; g.driver.api.cuMemFree_v2 = free; g.driver.api.cuMemcpyHtoD_v2 = copy; @@ -278,7 +278,7 @@ const Barrier = @import("load_barrier_test.zig"); fn secondGpu(first: *Gpu) !*Gpu { const g = try t.allocator.create(Gpu); g.* = first.*; - g.driver.lib = try std.DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); + g.driver.lib = try @import("sys").DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); g.ctx = .{ .d = &g.driver, .device = 1, .handle = @ptrFromInt(7) }; g.next = null; return g; diff --git a/zig/src/server/listener.zig b/zig/src/server/listener.zig index 55dee3333c..4095cd8e51 100644 --- a/zig/src/server/listener.zig +++ b/zig/src/server/listener.zig @@ -155,10 +155,12 @@ test "accepted IPv4 and IPv6 TCP streams disable Nagle for incremental responses if (posix.errno(posix.system.connect(client, &storage.any, len)) != .SUCCESS) return error.ConnectFailed; const accepted = acceptWithin(listener, 2000) orelse return error.AcceptFailed; defer _ = posix.system.close(accepted.fd); - var value: c_int = 0; - var size: posix.socklen_t = @sizeOf(c_int); - try std.testing.expect(std.c.getsockopt(accepted.fd, posix.IPPROTO.TCP, posix.TCP.NODELAY, &value, &size) == 0); - try std.testing.expect(value != 0); + if (!platform.windows) { // sys wraps no getsockopt to read the option back on Windows + var value: c_int = 0; + var size: posix.socklen_t = @sizeOf(c_int); + try std.testing.expect(std.c.getsockopt(accepted.fd, posix.IPPROTO.TCP, posix.TCP.NODELAY, &value, &size) == 0); + try std.testing.expect(value != 0); + } } } diff --git a/zig/src/server/root.zig b/zig/src/server/root.zig index c7b31b5004..dcb194bf17 100644 --- a/zig/src/server/root.zig +++ b/zig/src/server/root.zig @@ -39,7 +39,7 @@ test { _ = @import("chunk_plan.zig"); _ = @import("status_routes.zig"); _ = @import("speech.zig"); - _ = @import("speech_stream_test.zig"); + if (comptime @import("builtin").os.tag != .windows) _ = @import("speech_stream_test.zig"); // over a Unix socket pair _ = @import("drama_prompt.zig"); _ = @import("health_test.zig"); _ = @import("memory_routes.zig"); @@ -110,9 +110,6 @@ test { _ = @import("benchmark_cli_test.zig"); _ = @import("benchmark_run_test.zig"); _ = @import("benchmark_early_test.zig"); + _ = @import("benchmark_config_test.zig"); } } - -test { - _ = @import("benchmark_config_test.zig"); -} diff --git a/zig/src/server/speech.zig b/zig/src/server/speech.zig index ec6166bed9..b7f474facd 100644 --- a/zig/src/server/speech.zig +++ b/zig/src/server/speech.zig @@ -1,5 +1,6 @@ //! POST /v1/audio/speech: WAV or raw PCM whole, or raw PCM chunks with stream; only complete synthesis is audio. const std = @import("std"); +const platform = @import("sys"); const api = @import("engine_api"); const json = @import("json"); const errors = @import("errors.zig"); @@ -276,10 +277,10 @@ fn startStream(c: *Conn, sample_rate: u32) error{Closed}!void { if (c.close) c.addHeader("Connection", "close") catch return error.Closed; // each chunk leaves at once rather than waiting on the client's acknowledgement of the last (no effect off TCP) const one: c_int = 1; - _ = std.c.setsockopt(c.fd, std.posix.IPPROTO.TCP, std.posix.TCP.NODELAY, &one, @sizeOf(c_int)); + if (platform.windows) platform.posix.setsockopt(c.fd, platform.posix.IPPROTO.TCP, platform.posix.TCP.NODELAY, std.mem.asBytes(&one)) catch {} else _ = std.c.setsockopt(c.fd, std.posix.IPPROTO.TCP, std.posix.TCP.NODELAY, &one, @sizeOf(c_int)); // a blocking write into a full socket returns after the stall limit, so writeAll's poll can end a stalled stream - const stall: std.posix.timeval = .{ .sec = @divTrunc(c.timeouts.write_ms, 1000), .usec = @rem(c.timeouts.write_ms, 1000) * 1000 }; - std.posix.setsockopt(c.fd, std.posix.SOL.SOCKET, std.posix.SO.SNDTIMEO, std.mem.asBytes(&stall)) catch {}; + const stall: platform.posix.timeval = .{ .sec = @divTrunc(c.timeouts.write_ms, 1000), .usec = @rem(c.timeouts.write_ms, 1000) * 1000 }; + platform.posix.setsockopt(c.fd, platform.posix.SOL.SOCKET, platform.posix.SO.SNDTIMEO, std.mem.asBytes(&stall)) catch {}; return c.finish(""); } diff --git a/zig/src/vision/qwen_cuda_test.zig b/zig/src/vision/qwen_cuda_test.zig index 113cee6323..a088bb4069 100644 --- a/zig/src/vision/qwen_cuda_test.zig +++ b/zig/src/vision/qwen_cuda_test.zig @@ -245,6 +245,7 @@ const Child = struct { extern "c" fn kill(c_int, c_int) c_int; }; test "a stream and context that never complete end the process before an owner is freed" { + if (@import("builtin").os.tag == .windows) return error.SkipZigTest; // the child process comes from fork const pid = Child.fork(); if (pid < 0) return error.ForkFailed; if (pid == 0) { From 9e3282cc6e64ca624372196f4216cbac5dbb67b5 Mon Sep 17 00:00:00 2001 From: Jay Leaton <58495321+jayleaton@users.noreply.github.com> Date: Sun, 18 Oct 2026 20:33:07 +0700 Subject: [PATCH 2/5] Windows: the memory receipt, the RoCE gather and digest keys build too; H3's host tests skip Windows The first Windows run of these fixes left 8 failed steps: - memory_receipt's pid is a HANDLE in std.c on Windows, which JSON cannot carry; sys.c's pid_t, getpid, clock and getenv are the same calls on POSIX. - roce.zig loads libibverbs through sys.DynLib. - digest_key reads Windows' signed 64-bit inode as u64 (std.meta.Int is gone in Zig 0.17). - H3's library opens its checkpoint with mmap too, so its host targets skip Windows like the checkpoint test; the H3 clock and runtime fake edits are no longer needed and are reverted. - Flash Next's rank media test runs over a Unix socket pair: Windows skips its import instead of its session. --- zig/build/cuda.zig | 3 ++- zig/src/core/digest_key.zig | 2 +- zig/src/cuda/memory_receipt.zig | 15 ++++++++------- zig/src/cuda/roce.zig | 5 +++-- zig/src/families/flashnext/cuda_rank_media.zig | 2 +- .../families/flashnext/cuda_rank_media_test.zig | 1 - zig/src/families/h3/cuda.zig | 5 ++--- zig/src/families/h3/cuda_runtime_fake.zig | 4 ++-- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/zig/build/cuda.zig b/zig/build/cuda.zig index e484e60b04..2573889f3a 100644 --- a/zig/build/cuda.zig +++ b/zig/build/cuda.zig @@ -225,7 +225,8 @@ pub fn hostTests(b: *std.Build, draft_ids: *std.Build.Module, build_options: *st const host = b.graph.host; step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = b.createModule(.{ .root_source_file = b.path("zig/build/check_fatbins.zig"), .target = host }) })).step); const cuda = runtime(b, host, .debug, &.{}); - @import("h3_host.zig").targets(b, cuda, step); + // H3's library opens its checkpoint with mmap: not Windows + if (host.result.os.tag != .windows) @import("h3_host.zig").targets(b, cuda, step); const mods = family(b, host, .debug, cuda, draft_ids); @import("qwen_contract.zig").host(b, mods.core, step); const mtp_policy = b.createModule(.{ .root_source_file = b.path("zig/nemotron_mtp_policy_host.zig"), .target = host, .optimize = .debug, .link_libc = true, .imports = &.{ .{ .name = "nemotron", .module = mods.nemotron }, .{ .name = "lanes", .module = mods.lanes } } }); diff --git a/zig/src/core/digest_key.zig b/zig/src/core/digest_key.zig index bf448c6a35..e3682575ee 100644 --- a/zig/src/core/digest_key.zig +++ b/zig/src/core/digest_key.zig @@ -31,7 +31,7 @@ pub const Key = struct { } const s = try file.stat(io); if (s.kind != .file) return error.DigestNotRegular; - return .{ .device = 0, .inode = @as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(s.inode))), @bitCast(s.inode)), .size = s.size, .mtime = s.mtime.toNanoseconds(), .ctime = s.ctime.toNanoseconds() }; + return .{ .device = 0, .inode = @as(u64, @bitCast(s.inode)), .size = s.size, .mtime = s.mtime.toNanoseconds(), .ctime = s.ctime.toNanoseconds() }; } pub fn equal(a: Key, b: Key) bool { diff --git a/zig/src/cuda/memory_receipt.zig b/zig/src/cuda/memory_receipt.zig index a9b6aa7060..6143868f9e 100644 --- a/zig/src/cuda/memory_receipt.zig +++ b/zig/src/cuda/memory_receipt.zig @@ -1,13 +1,14 @@ //! Opt-in process-local allocator boundary receipts; no CUDA calls and no global peak reset unless requested. const std = @import("std"); +const platform = @import("sys"); const memory = @import("memory.zig"); pub const Phase = enum { load_begin, load_end, cleanup_begin, cleanup_end, reset }; pub fn enabled() bool { if (@import("builtin").is_test) return false; - const env = std.c.getenv("TF_CUDA_MEMORY_RECEIPTS") orelse return false; - return std.mem.eql(u8, std.mem.span(env), "1"); + const env = platform.getenv("TF_CUDA_MEMORY_RECEIPTS") orelse return false; + return std.mem.eql(u8, env, "1"); } pub fn emit(rank: u32, phase: Phase, reset: bool) void { @@ -17,22 +18,22 @@ pub fn emit(rank: u32, phase: Phase, reset: bool) void { pub fn emitUsage(rank: u32, phase: Phase, reset: bool, usage: memory.Usage) void { if (!enabled()) return; - var ts: std.c.timespec = undefined; - const ns: ?u64 = if (std.c.clock_gettime(.MONOTONIC, &ts) == 0) + var ts: platform.c.timespec = undefined; + const ns: ?u64 = if (platform.c.clock_gettime(platform.c.CLOCK.MONOTONIC, &ts) == 0) @as(u64, @intCast(ts.sec)) * std.time.ns_per_s + @as(u64, @intCast(ts.nsec)) else null; - const receipt: Receipt = .{ .pid = std.c.getpid(), .rank = rank, .phase = phase, .reset = reset, .monotonic_ns = ns, .usage = usage }; + const receipt: Receipt = .{ .pid = platform.c.getpid(), .rank = rank, .phase = phase, .reset = reset, .monotonic_ns = ns, .usage = usage }; std.debug.print("TF_MEMORY {f}\n", .{std.json.fmt(receipt, .{})}); } pub fn snapshot(rank: u32, phase: Phase, reset: bool, monotonic_ns: ?u64) Receipt { - return .{ .pid = std.c.getpid(), .rank = rank, .phase = phase, .reset = reset, .monotonic_ns = monotonic_ns, .usage = memory.usage(reset) }; + return .{ .pid = platform.c.getpid(), .rank = rank, .phase = phase, .reset = reset, .monotonic_ns = monotonic_ns, .usage = memory.usage(reset) }; } pub const Receipt = struct { schema: u32 = 1, - pid: std.c.pid_t, + pid: platform.c.pid_t, rank: u32, phase: Phase, reset: bool, diff --git a/zig/src/cuda/roce.zig b/zig/src/cuda/roce.zig index 25295d9bcc..9faf1af51a 100644 --- a/zig/src/cuda/roce.zig +++ b/zig/src/cuda/roce.zig @@ -1,6 +1,7 @@ //! Two-rank all-gathers as one-shot RoCE writes after b12x's RoCEnante (local-inference-lab/b12x, Apache-2.0). const std = @import("std"); +const platform = @import("sys"); const abi = @import("abi.zig"); const Driver = @import("driver.zig").Driver; const memory = @import("memory.zig"); @@ -212,7 +213,7 @@ const Hca = struct { pub const Gather = struct { gpa: std.mem.Allocator, d: *const Driver, - lib: std.DynLib, + lib: platform.DynLib, api: Api, rank: u8, hcas: [max_hcas]Hca = @splat(.{}), @@ -284,7 +285,7 @@ pub const Gather = struct { fn setup(g: *Gather, gpa: std.mem.Allocator, io: std.Io, d: *const Driver, rank: u8, max_bytes: usize) !void { g.* = .{ .gpa = gpa, .d = d, .lib = undefined, .api = undefined, .rank = rank, .layout = .of(max_bytes), .region = undefined, .region_dev = 0, .counters = undefined, .module = undefined, .f = undefined, .max_bytes = max_bytes }; - g.lib = std.DynLib.open("libibverbs.so.1") catch return error.VerbsUnavailable; + g.lib = platform.DynLib.open("libibverbs.so.1") catch return error.VerbsUnavailable; errdefer g.lib.close(); const info = @typeInfo(Api).@"struct"; inline for (info.field_names, info.field_types) |name, T| { diff --git a/zig/src/families/flashnext/cuda_rank_media.zig b/zig/src/families/flashnext/cuda_rank_media.zig index 79a783b8b4..4461af16e0 100644 --- a/zig/src/families/flashnext/cuda_rank_media.zig +++ b/zig/src/families/flashnext/cuda_rank_media.zig @@ -58,5 +58,5 @@ test "rank media frames are bounded and rebuild the same image plan" { } test { - _ = @import("cuda_rank_media_test.zig"); + if (@import("builtin").os.tag != .windows) _ = @import("cuda_rank_media_test.zig"); // over a Unix socket pair } diff --git a/zig/src/families/flashnext/cuda_rank_media_test.zig b/zig/src/families/flashnext/cuda_rank_media_test.zig index 45d9a74a6e..ee81179206 100644 --- a/zig/src/families/flashnext/cuda_rank_media_test.zig +++ b/zig/src/families/flashnext/cuda_rank_media_test.zig @@ -82,7 +82,6 @@ fn encoder(d: *const cuda.Driver, s: cuda.Stream) shared.media.Encoder { /// A rank of the pair over one end of a socket pair, its collective faked; the other end is the peer that leaves. fn session(engine: *prompt.Engine, rank: usize, pair: *[2]c_int) !ranks.Session { - if (@import("builtin").os.tag == .windows) return error.SkipZigTest; // the peer rank is a Unix socket pair if (std.c.socketpair(std.posix.AF.UNIX, std.posix.SOCK.STREAM, 0, pair) != 0) return error.SocketPair; const nonblock: c_int = @bitCast(@as(u32, @bitCast(std.c.O{ .NONBLOCK = true }))); for (pair) |fd| { diff --git a/zig/src/families/h3/cuda.zig b/zig/src/families/h3/cuda.zig index 1bd9206a03..55c562b5db 100644 --- a/zig/src/families/h3/cuda.zig +++ b/zig/src/families/h3/cuda.zig @@ -1,6 +1,5 @@ //! MiniMax H3 / FastH3's transformer blocks over the CUDA runtime in the checkpoint's bf16; the host owns the rest. const std = @import("std"); -const platform = @import("sys"); const cuda = @import("cuda"); const weights = @import("cuda_weights.zig"); pub const geometry = @import("cuda_geometry.zig"); @@ -27,8 +26,8 @@ pub const stages = [_][]const u8{ "norms", "q k v gate", "heads", "routing", "at const attention_shared = 3 * slot_tile * head_dim * 2; pub fn seconds() f64 { - var ts: platform.c.timespec = undefined; - if (platform.c.clock_gettime(platform.c.CLOCK.MONOTONIC, &ts) != 0) return 0; + var ts: std.c.timespec = undefined; + if (std.c.clock_gettime(.MONOTONIC, &ts) != 0) return 0; return @as(f64, @floatFromInt(ts.sec)) + @as(f64, @floatFromInt(ts.nsec)) * 1e-9; } diff --git a/zig/src/families/h3/cuda_runtime_fake.zig b/zig/src/families/h3/cuda_runtime_fake.zig index 8b4721f9b1..24bdfe8a5b 100644 --- a/zig/src/families/h3/cuda_runtime_fake.zig +++ b/zig/src/families/h3/cuda_runtime_fake.zig @@ -101,7 +101,7 @@ pub fn model() !*h3.Model { const m = try std.testing.allocator.create(h3.Model); m.* = undefined; m.gpa = std.testing.allocator; - m.driver.lib = try @import("sys").DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); + m.driver.lib = try std.DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); m.driver.api.cuCtxSetCurrent = current; m.driver.api.cuCtxSynchronize = sync; m.driver.api.cuStreamSynchronize = streamSync; @@ -124,7 +124,7 @@ pub fn model() !*h3.Model { m.blocks = try m.gpa.alloc(h3.Block, 1); m.blocks[0] = .{ .qkvc = empty, .out = empty, .fc1 = empty, .fc2 = empty, .norms = empty }; m.gemms = .{ .gpa = m.gpa, .lt = undefined, .workspace = empty }; - m.gemms.lt.lib = try @import("sys").DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); + m.gemms.lt.lib = try std.DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); m.hidden = 128; m.inner = 128; m.heads = 1; From 176666adcc537884181428cd0ad035979354d222 Mon Sep 17 00:00:00 2001 From: Jay Leaton <58495321+jayleaton@users.noreply.github.com> Date: Sun, 18 Oct 2026 20:41:33 +0700 Subject: [PATCH 3/5] Windows: the native load fakes open kernel32.dll, and digest-cache identity tests skip without persistent keys The second Windows run built everything but the digest probe and failed 13 tests: - cuda_load_gpu's fake driver opened libc.so.6; on Windows it opens kernel32.dll, as the copy-custody tests do. - Two checkpoint identity tests need digests reused across runs, which digest_key keeps only on Linux and macOS; they skip on !persistent like every digest_cache test. - digest_cache_probe writes its count through sys.c.write (std.c.write takes a HANDLE on Windows). --- zig/src/core/checkpoint_identity_test.zig | 2 ++ zig/src/core/digest_cache_probe.zig | 2 +- zig/src/native/cuda_load_gpu.zig | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/zig/src/core/checkpoint_identity_test.zig b/zig/src/core/checkpoint_identity_test.zig index a058bca520..c2e3857c8a 100644 --- a/zig/src/core/checkpoint_identity_test.zig +++ b/zig/src/core/checkpoint_identity_test.zig @@ -124,6 +124,7 @@ fn fixturePaths(a: std.mem.Allocator, tmp: *const std.testing.TmpDir, sub: []con } test "whole and split consumers share cached file digests and still check manifests" { + if (!@import("digest_key.zig").persistent) return error.SkipZigTest; const a = std.testing.allocator; const io = std.testing.io; var tmp = std.testing.tmpDir(.{}); @@ -168,6 +169,7 @@ test "full checkpoint identity includes payloads and detects changed files at re } test "a corrupt shared digest cache refuses the real content consumer" { + if (!@import("digest_key.zig").persistent) return error.SkipZigTest; const a = std.testing.allocator; const io = std.testing.io; var tmp = std.testing.tmpDir(.{}); diff --git a/zig/src/core/digest_cache_probe.zig b/zig/src/core/digest_cache_probe.zig index ad75b8c90e..6c92669d14 100644 --- a/zig/src/core/digest_cache_probe.zig +++ b/zig/src/core/digest_cache_probe.zig @@ -30,5 +30,5 @@ pub fn main(init: std.process.Init) !void { } var output: [32]u8 = undefined; const text = try std.fmt.bufPrint(&output, "{d}\n", .{calls}); - if (std.c.write(1, text.ptr, text.len) != text.len) return error.Output; + if (@import("sys").c.write(1, text.ptr, text.len) != text.len) return error.Output; } diff --git a/zig/src/native/cuda_load_gpu.zig b/zig/src/native/cuda_load_gpu.zig index bc3d74626c..1774c6eebb 100644 --- a/zig/src/native/cuda_load_gpu.zig +++ b/zig/src/native/cuda_load_gpu.zig @@ -156,7 +156,7 @@ const Fake = struct { const g = try t.allocator.create(Gpu); g.gpa = t.allocator; g.next = null; - g.driver.lib = try @import("sys").DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); + g.driver.lib = try @import("sys").DynLib.open(if (@import("builtin").os.tag == .windows) "kernel32.dll" else if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); g.driver.api.cuMemAlloc_v2 = alloc; g.driver.api.cuMemFree_v2 = free; g.driver.api.cuMemcpyHtoD_v2 = copy; @@ -278,7 +278,7 @@ const Barrier = @import("load_barrier_test.zig"); fn secondGpu(first: *Gpu) !*Gpu { const g = try t.allocator.create(Gpu); g.* = first.*; - g.driver.lib = try @import("sys").DynLib.open(if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); + g.driver.lib = try @import("sys").DynLib.open(if (@import("builtin").os.tag == .windows) "kernel32.dll" else if (@import("builtin").os.tag == .macos) "/usr/lib/libSystem.B.dylib" else "libc.so.6"); g.ctx = .{ .d = &g.driver, .device = 1, .handle = @ptrFromInt(7) }; g.next = null; return g; From 023975f1e501c1ec1c3cab71a1d239705f5964b1 Mon Sep 17 00:00:00 2001 From: Jay Leaton <58495321+jayleaton@users.noreply.github.com> Date: Sun, 18 Oct 2026 20:47:42 +0700 Subject: [PATCH 4/5] Windows: digest_cache_probe writes through std.Io on Windows; the executable has no sys module The third run's only failure: digest-writer is built outside the sys walk, so it cannot import sys. POSIX keeps its std.c.write. --- zig/src/core/digest_cache_probe.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zig/src/core/digest_cache_probe.zig b/zig/src/core/digest_cache_probe.zig index 6c92669d14..87ddf5e253 100644 --- a/zig/src/core/digest_cache_probe.zig +++ b/zig/src/core/digest_cache_probe.zig @@ -30,5 +30,7 @@ pub fn main(init: std.process.Init) !void { } var output: [32]u8 = undefined; const text = try std.fmt.bufPrint(&output, "{d}\n", .{calls}); - if (@import("sys").c.write(1, text.ptr, text.len) != text.len) return error.Output; + // std.c.write takes a HANDLE on Windows; this executable is built without the sys module + if (@import("builtin").os.tag == .windows) return std.Io.File.stdout().writeStreamingAll(init.io, text); + if (std.c.write(1, text.ptr, text.len) != text.len) return error.Output; } From 729e0d7ab20bf66558514b71b09eab564f4602a9 Mon Sep 17 00:00:00 2001 From: Jay Leaton <58495321+jayleaton@users.noreply.github.com> Date: Mon, 19 Oct 2026 02:53:19 +0700 Subject: [PATCH 5/5] Flash Next rank wire: the send, receive and peer-check refusal is Windows only macOS's restore and media tests build links straight from socket pairs, bypassing Link.open, so the earlier off-Linux refusal stopped their controls. Every POSIX OS keeps its transport; Winsock has no MSG.NOSIGNAL or DONTWAIT. --- zig/src/families/flashnext/cuda_rank_wire.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zig/src/families/flashnext/cuda_rank_wire.zig b/zig/src/families/flashnext/cuda_rank_wire.zig index 81b9ff26f4..98606b2994 100644 --- a/zig/src/families/flashnext/cuda_rank_wire.zig +++ b/zig/src/families/flashnext/cuda_rank_wire.zig @@ -132,7 +132,7 @@ pub const Link = struct { } pub fn send(l: Link, bytes: []const u8, until: u64) !void { - if (builtin.os.tag != .linux) return error.RankPlatform; + if (builtin.os.tag == .windows) return error.RankPlatform; // no MSG.NOSIGNAL or DONTWAIT in Winsock var at: usize = 0; while (at < bytes.len) { if (now() >= until) return error.RankTimeout; @@ -150,7 +150,7 @@ pub const Link = struct { } pub fn receive(l: Link, bytes: []u8, until: ?u64) !void { - if (builtin.os.tag != .linux) return error.RankPlatform; + if (builtin.os.tag == .windows) return error.RankPlatform; var at: usize = 0; while (at < bytes.len) { if (until) |end| if (now() >= end) return error.RankTimeout; @@ -182,7 +182,7 @@ pub const Link = struct { /// A non-consuming EOF check while a GPU collective progresses. A future control frame stays queued. pub fn checkPeer(l: Link) !void { - if (builtin.os.tag != .linux) return error.RankPlatform; + if (builtin.os.tag == .windows) return error.RankPlatform; var byte: u8 = 0; const rc = std.c.recv(l.fd, &byte, 1, p.MSG.PEEK | p.MSG.DONTWAIT); if (rc == 0) return error.RankDisconnected;