From e7c8d89aa9e963424a30efade2cab5dd8ada9a89 Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Wed, 24 Jun 2026 00:10:39 +0200 Subject: [PATCH] web: Update to zig 0.16.0 --- packages/web/build.zig.zon | 2 +- packages/web/src/Address.zig | 81 +++++++++++++++++++++++++++++ packages/web/src/Connection.zig | 5 +- packages/web/src/FileDescriptor.zig | 2 +- packages/web/src/Id.zig | 2 +- packages/web/src/Server.zig | 55 +++++++++++--------- packages/web/src/Worker.zig | 12 ++--- packages/web/src/main.zig | 32 ++++-------- 8 files changed, 132 insertions(+), 59 deletions(-) create mode 100644 packages/web/src/Address.zig diff --git a/packages/web/build.zig.zon b/packages/web/build.zig.zon index 6565963..e5b1beb 100644 --- a/packages/web/build.zig.zon +++ b/packages/web/build.zig.zon @@ -1,7 +1,7 @@ .{ .name = .web, .version = "0.0.0", - .minimum_zig_version = "0.15.2", + .minimum_zig_version = "0.16.0", .paths = .{ "src", "build.zig", diff --git a/packages/web/src/Address.zig b/packages/web/src/Address.zig new file mode 100644 index 0000000..972c857 --- /dev/null +++ b/packages/web/src/Address.zig @@ -0,0 +1,81 @@ +const std = @import("std"); + +const linux = std.os.linux; + +pub const Address = extern union { + any: linux.sockaddr, + in: linux.sockaddr.in, + in6: linux.sockaddr.in6, + un: linux.sockaddr.un, + + pub fn initIp4(addr: [4]u8, port: u16) Address { + return .{ + .in = .{ + .addr = @bitCast(addr), + .port = std.mem.nativeToBig(u16, port), + }, + }; + } + + pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address { + return .{ + .in6 = .{ + .addr = addr, + .port = std.mem.nativeToBig(u16, port), + .flowinfo = flowinfo, + .scope_id = scope_id, + }, + }; + } + + pub fn initUnix(path: []const u8) !Address { + var un: linux.sockaddr.un = .{ + .path = @splat(0), + }; + + // Ensure there is space for null terminator; already present by + // initializing the path to zeroes above. + if (path.len + 1 > un.path.len) return error.NameTooLong; + + @memcpy(&un.path[0..path.len], path); + + return .{ .un = un }; + } + + pub fn getOsSockLen(self: Address) linux.socklen_t { + return switch (self.any.family) { + linux.AF.INET => @sizeOf(linux.sockaddr.in), + linux.AF.INET6 => @sizeOf(linux.sockaddr.in6), + // NOTE According to zig's 0.15.2 stdlib, this is technically always + // correct especially since we ensure there is a space for a null + // terminator in init. + linux.AF.UNIX => @sizeOf(linux.sockaddr.un), + else => unreachable, + }; + } + + pub fn format(self: *const Address, writer: *std.Io.Writer) std.Io.Writer.Error!void { + switch (self.any.family) { + linux.AF.INET => { + const addr: std.Io.net.Ip4Address = .{ + .bytes = @bitCast(self.in.addr), + .port = std.mem.bigToNative(u16, self.in.port), + }; + + try addr.format(writer); + }, + linux.AF.INET6 => { + const addr: std.Io.net.Ip6Address.Unresolved = .{ + .bytes = self.in6.addr, + .interface_name = null, + }; + + try addr.format(writer); + }, + linux.AF.UNIX => { + try writer.writeAll(std.mem.sliceTo(&self.un.path, 0)); + }, + else => unreachable, + } + } +}; diff --git a/packages/web/src/Connection.zig b/packages/web/src/Connection.zig index b893aec..67bec1c 100644 --- a/packages/web/src/Connection.zig +++ b/packages/web/src/Connection.zig @@ -1,13 +1,14 @@ const std = @import("std"); const Connection = @This(); +const Address = @import("Address.zig").Address; const FileDescriptor = @import("FileDescriptor.zig").FileDescriptor; const openssl = @import("openssl.zig"); const iovec = std.posix.iovec; const iovec_const = std.posix.iovec_const; -address: std.net.Address, +address: Address, fd: FileDescriptor, ssl: ?*openssl.Ssl, node: std.DoublyLinkedList.Node = .{}, @@ -17,7 +18,7 @@ node: std.DoublyLinkedList.Node = .{}, pub fn reinit( self: *Connection, - address: std.net.Address, + address: Address, fd: FileDescriptor, ssl: ?*openssl.Ssl, ) void { diff --git a/packages/web/src/FileDescriptor.zig b/packages/web/src/FileDescriptor.zig index 22b964c..98027c4 100644 --- a/packages/web/src/FileDescriptor.zig +++ b/packages/web/src/FileDescriptor.zig @@ -1,7 +1,7 @@ const std = @import("std"); const linux = std.os.linux; -const errno = linux.E.init; +const errno = linux.errno; const iovec = std.posix.iovec; const iovec_const = std.posix.iovec_const; diff --git a/packages/web/src/Id.zig b/packages/web/src/Id.zig index a8249be..f2b622a 100644 --- a/packages/web/src/Id.zig +++ b/packages/web/src/Id.zig @@ -5,7 +5,7 @@ const UUID = @import("UUID.zig"); const decoder = &std.base64.url_safe_no_pad.Decoder; const encoder = &std.base64.url_safe_no_pad.Encoder; -pub fn Id(comptime _tag: @Type(.enum_literal)) type { +pub fn Id(comptime _tag: @EnumLiteral()) type { return struct { pub const tag = _tag; diff --git a/packages/web/src/Server.zig b/packages/web/src/Server.zig index a0027d2..8105d80 100644 --- a/packages/web/src/Server.zig +++ b/packages/web/src/Server.zig @@ -1,6 +1,7 @@ const std = @import("std"); const Server = @This(); +const Address = @import("Address.zig").Address; const Connection = @import("Connection.zig"); const FileDescriptor = @import("FileDescriptor.zig").FileDescriptor; const http = @import("http.zig"); @@ -11,10 +12,10 @@ const Worker = @import("Worker.zig"); const log = std.log.scoped(.Server); const linux = std.os.linux; -const errno = linux.E.init; +const errno = linux.errno; fd: FileDescriptor, -address: std.net.Address, +address: Address, ssl_ctx: ?*openssl.SslContext, workers: []Worker, threads: []std.Thread, @@ -29,9 +30,10 @@ connection_queue: std.DoublyLinkedList, connection_pool: std.DoublyLinkedList, connection_buffer: []Connection, -mutex: std.Thread.Mutex, -cond_connection_queued: std.Thread.Condition, -cond_connection_freed: std.Thread.Condition, +io: std.Io, +mutex: std.Io.Mutex, +cond_connection_queued: std.Io.Condition, +cond_connection_freed: std.Io.Condition, /// 4 kiB const page_size = 4 * 1024; @@ -40,7 +42,7 @@ const huge_page_size = 2 * 1024 * 1024; pub const Options = struct { request_handler: RequestHandler, - address: std.net.Address = .initIp4(.{ 127, 0, 0, 1 }, 8000), + address: Address = .initIp4(.{ 127, 0, 0, 1 }, 8000), /// If not `null`, the server will use TLS with the provided OpenSSL /// context. ssl_ctx: ?*openssl.SslContext = null, @@ -73,7 +75,7 @@ pub const Options = struct { read_timeout_us: u64 = 1 * std.time.us_per_s, }; -pub fn init(allocator: std.mem.Allocator, options: Options) !Server { +pub fn init(allocator: std.mem.Allocator, io: std.Io, options: Options) !Server { const worker_count = if (options.worker_count > 0) options.worker_count else try std.Thread.getCpuCount(); // Create socket fd @@ -123,7 +125,7 @@ pub fn init(allocator: std.mem.Allocator, options: Options) !Server { const read_buffer_ptr = try errOrPtr(linux.mmap( null, double_all_read_buffers_size, - linux.PROT.NONE, + .{}, linux.MAP{ .TYPE = .PRIVATE, .ANONYMOUS = true }, -1, 0, @@ -138,8 +140,8 @@ pub fn init(allocator: std.mem.Allocator, options: Options) !Server { try err(linux.mmap( read_buffer_ptr + double_offset, single_read_buffer_size, - linux.PROT.READ | linux.PROT.WRITE, - linux.MAP{ .TYPE = .SHARED, .FIXED = true }, + .{ .READ = true, .WRITE = true }, + .{ .TYPE = .SHARED, .FIXED = true }, @intFromEnum(read_buffer_fd), @intCast(offset), )); @@ -147,8 +149,8 @@ pub fn init(allocator: std.mem.Allocator, options: Options) !Server { try err(linux.mmap( read_buffer_ptr + double_offset + single_read_buffer_size, single_read_buffer_size, - linux.PROT.READ | linux.PROT.WRITE, - linux.MAP{ .TYPE = .SHARED, .FIXED = true }, + .{ .READ = true, .WRITE = true }, + .{ .TYPE = .SHARED, .FIXED = true }, @intFromEnum(read_buffer_fd), @intCast(offset), )); @@ -162,8 +164,8 @@ pub fn init(allocator: std.mem.Allocator, options: Options) !Server { const header_write_buffer_ptr = try errOrPtr(linux.mmap( null, all_header_write_buffers_size, - linux.PROT.READ | linux.PROT.WRITE, - linux.MAP{ .TYPE = .PRIVATE, .ANONYMOUS = true }, + .{ .READ = true, .WRITE = true }, + .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, -1, 0, )); @@ -177,8 +179,8 @@ pub fn init(allocator: std.mem.Allocator, options: Options) !Server { const body_write_buffer_ptr = try errOrPtr(linux.mmap( null, all_body_write_buffers_size, - linux.PROT.READ | linux.PROT.WRITE, - linux.MAP{ .TYPE = .PRIVATE, .ANONYMOUS = true }, + .{ .READ = true, .WRITE = true }, + .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, -1, 0, )); @@ -232,9 +234,10 @@ pub fn init(allocator: std.mem.Allocator, options: Options) !Server { .connection_pool = connection_pool, .connection_buffer = connection_buffer, - .mutex = .{}, - .cond_connection_queued = .{}, - .cond_connection_freed = .{}, + .io = io, + .mutex = .init, + .cond_connection_queued = .init, + .cond_connection_freed = .init, }; } @@ -284,7 +287,7 @@ pub fn listen(self: *Server, running: *const std.atomic.Value(bool)) !void { log.debug("Storing `false` into worker_running.", .{}); worker_running.store(false, .release); log.debug("Broadcasting connection queued condition variable.", .{}); - self.cond_connection_queued.broadcast(); + self.cond_connection_queued.broadcast(self.io); for (self.threads[0..spawned], 0..) |*thread, i| { log.debug("Joining the thread of worker #{d}.", .{i}); thread.join(); @@ -298,8 +301,8 @@ pub fn listen(self: *Server, running: *const std.atomic.Value(bool)) !void { } while (running.load(.acquire)) { - var address: std.net.Address = undefined; - var address_size: u32 = @sizeOf(std.net.Address); + var address: Address = undefined; + var address_size: u32 = @sizeOf(Address); log.debug("Accepting connection.", .{}); const fd = self.fd.accept(&address.any, &address_size) catch |e| { @@ -322,11 +325,11 @@ pub fn listen(self: *Server, running: *const std.atomic.Value(bool)) !void { { log.debug("Acquiring mutex.", .{}); - self.mutex.lock(); + self.mutex.lockUncancelable(self.io); log.debug("Acquired mutex.", .{}); defer { log.debug("Unlocking mutex.", .{}); - self.mutex.unlock(); + self.mutex.unlock(self.io); } while (true) { @@ -339,13 +342,13 @@ pub fn listen(self: *Server, running: *const std.atomic.Value(bool)) !void { } log.debug("Waiting on connection freed condition variable.", .{}); - self.cond_connection_freed.wait(&self.mutex); + self.cond_connection_freed.waitUncancelable(self.io, &self.mutex); log.debug("Woken up on connection freed condition variable.", .{}); } } log.debug("Signaling connection queued condition variable.", .{}); - self.cond_connection_queued.signal(); + self.cond_connection_queued.signal(self.io); } else { log.debug("Loaded `false` from running, the accept loop exited.", .{}); } diff --git a/packages/web/src/Worker.zig b/packages/web/src/Worker.zig index 68ec75c..7915e8b 100644 --- a/packages/web/src/Worker.zig +++ b/packages/web/src/Worker.zig @@ -77,11 +77,11 @@ pub fn worker( running: *const std.atomic.Value(bool), ) void { log.debug("[#{d}] Acquiring mutex.", .{self.worker_id}); - server.mutex.lock(); + server.mutex.lockUncancelable(server.io); log.debug("[#{d}] Acquired mutex.", .{self.worker_id}); defer { log.debug("[#{d}] Unlocking mutex.", .{self.worker_id}); - server.mutex.unlock(); + server.mutex.unlock(server.io); } while (running.load(.acquire)) { @@ -90,16 +90,16 @@ pub fn worker( log.debug("[#{d}] Popped connection to {f} from the connection queue.", .{ self.worker_id, connection.address }); log.debug("[#{d}] Unlocking mutex.", .{self.worker_id}); - server.mutex.unlock(); + server.mutex.unlock(server.io); defer { log.debug("[#{d}] Acquiring mutex.", .{self.worker_id}); - server.mutex.lock(); + server.mutex.lockUncancelable(server.io); log.debug("[#{d}] Acquired mutex.", .{self.worker_id}); log.debug("[#{d}] Returning connection to connection pool.", .{self.worker_id}); server.connection_pool.append(&connection.node); log.debug("[#{d}] Signaling connection freed condition variable.", .{self.worker_id}); - server.cond_connection_freed.signal(); + server.cond_connection_freed.signal(server.io); } log.debug("[#{d}] Handling connection to {f}.", .{ self.worker_id, connection.address }); @@ -108,7 +108,7 @@ pub fn worker( }; } else { log.debug("[#{d}] Waiting on connection queued condition variable.", .{self.worker_id}); - server.cond_connection_queued.wait(&server.mutex); + server.cond_connection_queued.waitUncancelable(server.io, &server.mutex); log.debug("[#{d}] Woken up on connection queued condition variable.", .{self.worker_id}); } } else { diff --git a/packages/web/src/main.zig b/packages/web/src/main.zig index 41f29a0..ae5a46b 100644 --- a/packages/web/src/main.zig +++ b/packages/web/src/main.zig @@ -8,19 +8,12 @@ const UUID = web.UUID; var running: std.atomic.Value(bool) = .init(true); -fn interruptionHandler(sig: i32) callconv(.c) void { +fn interruptionHandler(sig: linux.SIG) callconv(.c) void { var buf: [32]u8 = undefined; - const signal_name = blk: inline for (@typeInfo(linux.SIG).@"struct".decls) |decl| { - if (comptime std.mem.eql(u8, decl.name, "BLOCK") or - std.mem.eql(u8, decl.name, "UNBLOCK") or - std.mem.eql(u8, decl.name, "SETMASK")) continue; - - const decl_value = @field(linux.SIG, decl.name); - if (@TypeOf(decl_value) != comptime_int) continue; - if (decl_value == sig) break :blk "SIG" ++ decl.name; - } else { - break :blk std.fmt.bufPrint(&buf, "#{d}", .{sig}) catch unreachable; + const signal_name = switch (sig) { + _ => std.fmt.bufPrint(&buf, "#{d}", .{sig}) catch unreachable, + inline else => |x| "SIG" ++ @tagName(x), }; std.log.debug("Interrupted with signal {s}.", .{signal_name}); @@ -85,12 +78,8 @@ const Handler = struct { } }; -pub fn main() !void { - var gpa: std.heap.GeneralPurposeAllocator(.{ - .thread_safe = true, - }) = .init; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const allocator = init.gpa; _ = ssl.c_ssl.SSL_library_init(); _ = ssl.c_ssl.OpenSSL_add_all_algorithms(); @@ -106,9 +95,8 @@ pub fn main() !void { try ssl_ctx.usePrivateKeyFile("key.pem", ssl.c_ssl.SSL_FILETYPE_PEM); try ssl_ctx.checkPrivateKey(); - var server = try web.Server.init(allocator, .{ + var server = try web.Server.init(allocator, init.io, .{ .request_handler = Handler.interface(), - .address = .initIp4(.{ 127, 0, 0, 1 }, 8000), .ssl_ctx = ssl_ctx, }); defer server.deinit(allocator); @@ -118,13 +106,13 @@ pub fn main() !void { .mask = linux.sigemptyset(), .flags = linux.SA.RESETHAND, }; - signal(linux.SIG.INT, &sigaction); - signal(linux.SIG.TERM, &sigaction); + signal(.INT, &sigaction); + signal(.TERM, &sigaction); try server.listen(&running); } -fn signal(sig: u8, action: *const linux.Sigaction) void { +fn signal(sig: linux.SIG, action: *const linux.Sigaction) void { var old_action = std.mem.zeroes(linux.Sigaction); _ = linux.sigaction(sig, null, &old_action); if (old_action.handler.handler == linux.SIG.IGN) return;