zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit e8a2e6578a3f5e4cd82eb59388e49e152728791e (tree)
parent f2f474fc785a9bc89f16f91067480e8c410dc773
Author: Lukas Lalinsky <lukas@lalinsky.com>
Date:   Sat, 27 Dec 2025 10:06:21 +0100

Add std.Io.net.Stream.shutdown

Diffstat:
Mlib/std/Io.zig | 1+
Mlib/std/Io/Kqueue.zig | 13+++++++++++++
Mlib/std/Io/Threaded.zig | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/std/Io/net.zig | 14++++++++++++++
Mlib/std/Io/net/test.zig | 2++
5 files changed, 118 insertions(+), 0 deletions(-)

diff --git a/lib/std/Io.zig b/lib/std/Io.zig @@ -734,6 +734,7 @@ pub const VTable = struct { netWrite: *const fn (?*anyopaque, dest: net.Socket.Handle, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize, netWriteFile: *const fn (?*anyopaque, net.Socket.Handle, header: []const u8, *Io.File.Reader, Io.Limit) net.Stream.Writer.WriteFileError!usize, netClose: *const fn (?*anyopaque, handle: []const net.Socket.Handle) void, + netShutdown: *const fn (?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void, netInterfaceNameResolve: *const fn (?*anyopaque, *const net.Interface.Name) net.Interface.Name.ResolveError!net.Interface, netInterfaceName: *const fn (?*anyopaque, net.Interface) net.Interface.NameError!net.Interface.Name, netLookup: *const fn (?*anyopaque, net.HostName, *Queue(net.HostName.LookupResult), net.HostName.LookupOptions) net.HostName.LookupError!void, diff --git a/lib/std/Io/Kqueue.zig b/lib/std/Io/Kqueue.zig @@ -900,6 +900,7 @@ pub fn io(k: *Kqueue) Io { .netConnectIp = netConnectIp, .netConnectUnix = netConnectUnix, .netClose = netClose, + .netShutdown = netShutdown, .netRead = netRead, .netWrite = netWrite, .netSend = netSend, @@ -1549,12 +1550,22 @@ fn netWrite(userdata: ?*anyopaque, dest: net.Socket.Handle, header: []const u8, _ = splat; @panic("TODO"); } + fn netClose(userdata: ?*anyopaque, handle: net.Socket.Handle) void { const k: *Kqueue = @ptrCast(@alignCast(userdata)); _ = k; _ = handle; @panic("TODO"); } + +fn netShutdown(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void { + const k: *Kqueue = @ptrCast(@alignCast(userdata)); + _ = k; + _ = handle; + _ = how; + @panic("TODO"); +} + fn netInterfaceNameResolve( userdata: ?*anyopaque, name: *const net.Interface.Name, @@ -1564,12 +1575,14 @@ fn netInterfaceNameResolve( _ = name; @panic("TODO"); } + fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name { const k: *Kqueue = @ptrCast(@alignCast(userdata)); _ = k; _ = interface; @panic("TODO"); } + fn netLookup( userdata: ?*anyopaque, host_name: net.HostName, diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig @@ -891,6 +891,10 @@ pub fn io(t: *Threaded) Io { else => netConnectUnixPosix, }, .netClose = netClose, + .netShutdown = switch (native_os) { + .windows => netShutdownWindows, + else => netShutdownPosix, + }, .netRead = switch (native_os) { .windows => netReadWindows, else => netReadPosix, @@ -1007,6 +1011,7 @@ pub fn ioBasic(t: *Threaded) Io { .netConnectIp = netConnectIpUnavailable, .netConnectUnix = netConnectUnixUnavailable, .netClose = netCloseUnavailable, + .netShutdown = netShutdownUnavailable, .netRead = netReadUnavailable, .netWrite = netWriteUnavailable, .netWriteFile = netWriteFileUnavailable, @@ -10390,6 +10395,89 @@ fn netCloseUnavailable(userdata: ?*anyopaque, handles: []const net.Socket.Handle unreachable; // How you gonna close something that was impossible to open? } +fn netShutdownPosix(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void { + if (!have_networking) return error.NetworkDown; + const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); + + const posix_how: i32 = switch (how) { + .recv => posix.SHUT.RD, + .send => posix.SHUT.WR, + .both => posix.SHUT.RDWR, + }; + + try current_thread.beginSyscall(); + while (true) { + switch (posix.errno(posix.system.shutdown(handle, posix_how))) { + .SUCCESS => { + current_thread.endSyscall(); + return; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .BADF, .NOTSOCK, .INVAL => |err| return errnoBug(err), + .NOTCONN => return error.SocketUnconnected, + .NOBUFS => return error.SystemResources, + else => |err| return posix.unexpectedErrno(err), + } + }, + } + } +} + +fn netShutdownWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void { + if (!have_networking) return error.NetworkDown; + const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); + + const wsa_how: i32 = switch (how) { + .recv => ws2_32.SD_RECEIVE, + .send => ws2_32.SD_SEND, + .both => ws2_32.SD_BOTH, + }; + + try current_thread.beginSyscall(); + while (true) { + const rc = ws2_32.shutdown(handle, wsa_how); + if (rc != ws2_32.SOCKET_ERROR) { + current_thread.endSyscall(); + return; + } + switch (ws2_32.WSAGetLastError()) { + .EINTR => { + try current_thread.checkCancel(); + continue; + }, + .NOTINITIALISED => { + try initializeWsa(t); + try current_thread.checkCancel(); + continue; + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, + .ECONNABORTED => return error.ConnectionAborted, + .ECONNRESET => return error.ConnectionResetByPeer, + .ENETDOWN => return error.NetworkDown, + .ENOTCONN => return error.SocketUnconnected, + .EINVAL, .ENOTSOCK => |err| return wsaErrorBug(err), + else => |err| return windows.unexpectedWSAError(err), + } + }, + } + } +} + +fn netShutdownUnavailable(_: ?*anyopaque, _: net.Socket.Handle, _: net.ShutdownHow) net.ShutdownError!void { + unreachable; // How you gonna shutdown something that was impossible to open? +} + fn netInterfaceNameResolve( userdata: ?*anyopaque, name: *const net.Interface.Name, diff --git a/lib/std/Io/net.zig b/lib/std/Io/net.zig @@ -954,6 +954,16 @@ pub const SendFlags = packed struct(u8) { _: u3 = 0, }; +pub const ShutdownHow = enum { recv, send, both }; + +pub const ShutdownError = error{ + ConnectionAborted, + ConnectionResetByPeer, + NetworkDown, + SocketUnconnected, + SystemResources, +} || Io.UnexpectedError || Io.Cancelable; + pub const Interface = struct { /// Value 0 indicates `none`. index: u32, @@ -1191,6 +1201,10 @@ pub const Stream = struct { io.vtable.netClose(io.userdata, (&s.socket.handle)[0..1]); } + pub fn shutdown(s: *const Stream, io: Io, how: ShutdownHow) ShutdownError!void { + return io.vtable.netShutdown(io.userdata, s.socket.handle, how); + } + pub const Reader = struct { io: Io, interface: Io.Reader, diff --git a/lib/std/Io/net/test.zig b/lib/std/Io/net/test.zig @@ -346,6 +346,8 @@ test "non-blocking tcp server" { const len = try socket_file.read(&buf); const msg = buf[0..len]; try testing.expect(mem.eql(u8, msg, "hello from server\n")); + + try stream.shutdown(io, .both); } test "decompress compressed DNS name" {