authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-29 22:59:30-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-29 22:59:30-04:00
logc3d816a98e1126f5de4ec1a45e5f65bb2ff2f43c
tree5930edec7fa411286b2f2a9e36183b01589a6a6a
parent8d3b7689ad9c2dd14d0f5cadf2b711ff1ab70054
signaturelock-open Commit is signed but in an unrecognized format.

std lib networking improvements, especially non-blocking I/O

* delete the std/event/net directory * `std.event.Loop.waitUntilFdReadable` and related functions no longer have possibility of failure. On Linux, they fall back to poll() and then fall back to sleep(). * add some missing `noasync` decorations in `std.event.Loop` * redo the `std.net.Server` API. it's quite nice now, but shutdown does not work cleanly. There is a race condition with close() that I am actively working on. * move `std.io.OutStream` to its own file to match `std.io.InStream`. I started working on making `write` integrated with evented I/O, but it got tricky so I backed off and filed #3557. However I did integrate `std.os.writev` and `std.os.pwritev` with evented I/O. * add `std.Target.stack_align` * move networking tests to `lib/std/net/test.zig` * add `std.net.tcpConnectToHost` and `std.net.tcpConnectToAddress`. * rename `error.UnknownName` to `error.UnknownHostName` within the context of DNS resolution. * add `std.os.readv`, which is integrated with evented I/O. * `std.os.preadv`, is now integrated with evented I/O. * `std.os.accept4` now asserts that ENOTSOCK and EOPNOTSUPP never occur (misuse of API), instead of returning errors. * `std.os.connect` is now integrated with evented I/O. `std.os.connect_async` is gone. Just use `std.os.connect`. * fix false positive dependency loop regarding async function frames * add more compile notes to help when dependency loops occur in determining whether a function is async. * ir: change an assert to ir_assert to make it easier to find workarounds for when such an assert is triggered. In this case it was trying to parse an IPv4 address at comptime.

15 files changed, 512 insertions(+), 556 deletions(-)

lib/std/event.zig-2
......@@ -7,7 +7,6 @@ pub const RwLock = @import("event/rwlock.zig").RwLock;
77pub const RwLocked = @import("event/rwlocked.zig").RwLocked;
88pub const Loop = @import("event/loop.zig").Loop;
99pub const fs = @import("event/fs.zig");
10pub const net = @import("event/net.zig");
1110
1211test "import event tests" {
1312 _ = @import("event/channel.zig");
......@@ -19,5 +18,4 @@ test "import event tests" {
1918 _ = @import("event/rwlock.zig");
2019 _ = @import("event/rwlocked.zig");
2120 _ = @import("event/loop.zig");
22 _ = @import("event/net.zig");
2321}
lib/std/event/channel.zig+6-4
......@@ -4,9 +4,11 @@ const assert = std.debug.assert;
44const testing = std.testing;
55const Loop = std.event.Loop;
66
7/// many producer, many consumer, thread-safe, runtime configurable buffer size
8/// when buffer is empty, consumers suspend and are resumed by producers
9/// when buffer is full, producers suspend and are resumed by consumers
7/// Many producer, many consumer, thread-safe, runtime configurable buffer size.
8/// When buffer is empty, consumers suspend and are resumed by producers.
9/// When buffer is full, producers suspend and are resumed by consumers.
10/// TODO now that async function rewrite has landed, this API should be adjusted
11/// to not use the event loop's allocator, and to not require allocation.
1012pub fn Channel(comptime T: type) type {
1113 return struct {
1214 loop: *Loop,
......@@ -48,7 +50,7 @@ pub fn Channel(comptime T: type) type {
4850 tick_node: *Loop.NextTickNode,
4951 };
5052
51 /// call destroy when done
53 /// Call `destroy` when done.
5254 pub fn create(loop: *Loop, capacity: usize) !*SelfChannel {
5355 const buffer_nodes = try loop.allocator.alloc(T, capacity);
5456 errdefer loop.allocator.free(buffer_nodes);
lib/std/event/loop.zig+65-22
......@@ -448,26 +448,67 @@ pub const Loop = struct {
448448 self.finishOneEvent();
449449 }
450450
451 pub fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
452 defer self.linuxRemoveFd(fd);
451 pub fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) void {
452 assert(flags & os.EPOLLET == os.EPOLLET);
453 assert(flags & os.EPOLLONESHOT == os.EPOLLONESHOT);
454 var resume_node = ResumeNode.Basic{
455 .base = ResumeNode{
456 .id = .Basic,
457 .handle = @frame(),
458 .overlapped = ResumeNode.overlapped_init,
459 },
460 };
461 var need_to_delete = false;
462 defer if (need_to_delete) self.linuxRemoveFd(fd);
463
453464 suspend {
454 var resume_node = ResumeNode.Basic{
455 .base = ResumeNode{
456 .id = .Basic,
457 .handle = @frame(),
458 .overlapped = ResumeNode.overlapped_init,
465 if (self.linuxAddFd(fd, &resume_node.base, flags)) |_| {
466 need_to_delete = true;
467 } else |err| switch (err) {
468 error.FileDescriptorNotRegistered => unreachable,
469 error.OperationCausesCircularLoop => unreachable,
470 error.FileDescriptorIncompatibleWithEpoll => unreachable,
471 error.FileDescriptorAlreadyPresentInSet => unreachable, // evented writes to the same fd is not thread-safe
472
473 error.SystemResources,
474 error.UserResourceLimitReached,
475 error.Unexpected,
476 => {
477 // Fall back to a blocking poll(). Ideally this codepath is never hit, since
478 // epoll should be just fine. But this is better than incorrect behavior.
479 var poll_flags: i16 = 0;
480 if ((flags & os.EPOLLIN) != 0) poll_flags |= os.POLLIN;
481 if ((flags & os.EPOLLOUT) != 0) poll_flags |= os.POLLOUT;
482 var pfd = [1]os.pollfd{os.pollfd{
483 .fd = fd,
484 .events = poll_flags,
485 .revents = undefined,
486 }};
487 _ = os.poll(&pfd, -1) catch |poll_err| switch (poll_err) {
488 error.SystemResources,
489 error.Unexpected,
490 => {
491 // Even poll() didn't work. The best we can do now is sleep for a
492 // small duration and then hope that something changed.
493 std.time.sleep(1 * std.time.millisecond);
494 },
495 };
496 resume @frame();
459497 },
460 };
461 try self.linuxAddFd(fd, &resume_node.base, flags);
498 }
462499 }
463500 }
464501
465 pub fn waitUntilFdReadable(self: *Loop, fd: os.fd_t) !void {
466 return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN);
502 pub fn waitUntilFdReadable(self: *Loop, fd: os.fd_t) void {
503 return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLIN);
467504 }
468505
469 pub fn waitUntilFdWritable(self: *Loop, fd: os.fd_t) !void {
470 return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLOUT);
506 pub fn waitUntilFdWritable(self: *Loop, fd: os.fd_t) void {
507 return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT);
508 }
509
510 pub fn waitUntilFdWritableOrReadable(self: *Loop, fd: os.fd_t) void {
511 return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT | os.EPOLLIN);
471512 }
472513
473514 pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !os.Kevent {
......@@ -645,7 +686,7 @@ pub const Loop = struct {
645686 .linux => {
646687 self.posixFsRequest(&self.os_data.fs_end_request);
647688 // writing 8 bytes to an eventfd cannot fail
648 os.write(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
689 noasync os.write(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
649690 return;
650691 },
651692 .macosx, .freebsd, .netbsd => {
......@@ -793,6 +834,8 @@ pub const Loop = struct {
793834 }
794835 }
795836
837 // TODO make this whole function noasync
838 // https://github.com/ziglang/zig/issues/3157
796839 fn posixFsRun(self: *Loop) void {
797840 while (true) {
798841 if (builtin.os == .linux) {
......@@ -802,27 +845,27 @@ pub const Loop = struct {
802845 switch (node.data.msg) {
803846 .End => return,
804847 .WriteV => |*msg| {
805 msg.result = os.writev(msg.fd, msg.iov);
848 msg.result = noasync os.writev(msg.fd, msg.iov);
806849 },
807850 .PWriteV => |*msg| {
808 msg.result = os.pwritev(msg.fd, msg.iov, msg.offset);
851 msg.result = noasync os.pwritev(msg.fd, msg.iov, msg.offset);
809852 },
810853 .PReadV => |*msg| {
811 msg.result = os.preadv(msg.fd, msg.iov, msg.offset);
854 msg.result = noasync os.preadv(msg.fd, msg.iov, msg.offset);
812855 },
813856 .Open => |*msg| {
814 msg.result = os.openC(msg.path.ptr, msg.flags, msg.mode);
857 msg.result = noasync os.openC(msg.path.ptr, msg.flags, msg.mode);
815858 },
816 .Close => |*msg| os.close(msg.fd),
859 .Close => |*msg| noasync os.close(msg.fd),
817860 .WriteFile => |*msg| blk: {
818861 const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT |
819862 os.O_CLOEXEC | os.O_TRUNC;
820 const fd = os.openC(msg.path.ptr, flags, msg.mode) catch |err| {
863 const fd = noasync os.openC(msg.path.ptr, flags, msg.mode) catch |err| {
821864 msg.result = err;
822865 break :blk;
823866 };
824 defer os.close(fd);
825 msg.result = os.write(fd, msg.contents);
867 defer noasync os.close(fd);
868 msg.result = noasync os.write(fd, msg.contents);
826869 },
827870 }
828871 switch (node.data.finish) {
lib/std/event/net.zig deleted-358
......@@ -1,358 +0,0 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const testing = std.testing;
4const event = std.event;
5const mem = std.mem;
6const os = std.os;
7const Loop = std.event.Loop;
8const File = std.fs.File;
9const fd_t = os.fd_t;
10
11pub const Server = struct {
12 handleRequestFn: async fn (*Server, *const std.net.Address, File) void,
13
14 loop: *Loop,
15 sockfd: ?i32,
16 accept_frame: ?anyframe,
17 listen_address: std.net.Address,
18
19 waiting_for_emfile_node: PromiseNode,
20 listen_resume_node: event.Loop.ResumeNode,
21
22 const PromiseNode = std.TailQueue(anyframe).Node;
23
24 pub fn init(loop: *Loop) Server {
25 // TODO can't initialize handler here because we need well defined copy elision
26 return Server{
27 .loop = loop,
28 .sockfd = null,
29 .accept_frame = null,
30 .handleRequestFn = undefined,
31 .waiting_for_emfile_node = undefined,
32 .listen_address = undefined,
33 .listen_resume_node = event.Loop.ResumeNode{
34 .id = event.Loop.ResumeNode.Id.Basic,
35 .handle = undefined,
36 .overlapped = event.Loop.ResumeNode.overlapped_init,
37 },
38 };
39 }
40
41 pub fn listen(
42 self: *Server,
43 address: *const std.net.Address,
44 handleRequestFn: async fn (*Server, *const std.net.Address, File) void,
45 ) !void {
46 self.handleRequestFn = handleRequestFn;
47
48 const sockfd = try os.socket(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK, os.PROTO_tcp);
49 errdefer os.close(sockfd);
50 self.sockfd = sockfd;
51
52 try os.bind(sockfd, &address.os_addr);
53 try os.listen(sockfd, os.SOMAXCONN);
54 self.listen_address = std.net.Address.initPosix(try os.getsockname(sockfd));
55
56 self.accept_frame = async Server.handler(self);
57 errdefer await self.accept_frame.?;
58
59 self.listen_resume_node.handle = self.accept_frame.?;
60 try self.loop.linuxAddFd(sockfd, &self.listen_resume_node, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
61 errdefer self.loop.removeFd(sockfd);
62 }
63
64 /// Stop listening
65 pub fn close(self: *Server) void {
66 self.loop.linuxRemoveFd(self.sockfd.?);
67 if (self.sockfd) |fd| {
68 os.close(fd);
69 self.sockfd = null;
70 }
71 }
72
73 pub fn deinit(self: *Server) void {
74 if (self.accept_frame) |accept_frame| await accept_frame;
75 if (self.sockfd) |sockfd| os.close(sockfd);
76 }
77
78 pub async fn handler(self: *Server) void {
79 while (true) {
80 var accepted_addr: std.net.Address = undefined;
81 // TODO just inline the following function here and don't expose it as posixAsyncAccept
82 if (os.accept4_async(self.sockfd.?, &accepted_addr.os_addr, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC)) |accepted_fd| {
83 if (accepted_fd == -1) {
84 // would block
85 suspend; // we will get resumed by epoll_wait in the event loop
86 continue;
87 }
88 var socket = File.openHandle(accepted_fd);
89 self.handleRequestFn(self, &accepted_addr, socket);
90 } else |err| switch (err) {
91 error.ProcessFdQuotaExceeded => @panic("TODO handle this error"),
92 error.ConnectionAborted => continue,
93
94 error.FileDescriptorNotASocket => unreachable,
95 error.OperationNotSupported => unreachable,
96
97 error.SystemFdQuotaExceeded, error.SystemResources, error.ProtocolFailure, error.BlockedByFirewall, error.Unexpected => {
98 @panic("TODO handle this error");
99 },
100 }
101 }
102 }
103};
104
105pub async fn connectUnixSocket(loop: *Loop, path: []const u8) !i32 {
106 const sockfd = try os.socket(
107 os.AF_UNIX,
108 os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK,
109 0,
110 );
111 errdefer os.close(sockfd);
112
113 var sock_addr = os.sockaddr_un{
114 .family = os.AF_UNIX,
115 .path = undefined,
116 };
117
118 if (path.len > @typeOf(sock_addr.path).len) return error.NameTooLong;
119 mem.copy(u8, sock_addr.path[0..], path);
120 const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len);
121 try os.connect_async(sockfd, &sock_addr, size);
122 try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
123 try os.getsockoptError(sockfd);
124
125 return sockfd;
126}
127
128pub const ReadError = error{
129 SystemResources,
130 Unexpected,
131 UserResourceLimitReached,
132 InputOutput,
133
134 FileDescriptorNotRegistered, // TODO remove this possibility
135 OperationCausesCircularLoop, // TODO remove this possibility
136 FileDescriptorAlreadyPresentInSet, // TODO remove this possibility
137 FileDescriptorIncompatibleWithEpoll, // TODO remove this possibility
138};
139
140/// returns number of bytes read. 0 means EOF.
141pub async fn read(loop: *std.event.Loop, fd: fd_t, buffer: []u8) ReadError!usize {
142 const iov = os.iovec{
143 .iov_base = buffer.ptr,
144 .iov_len = buffer.len,
145 };
146 const iovs: *const [1]os.iovec = &iov;
147 return readvPosix(loop, fd, iovs, 1);
148}
149
150pub const WriteError = error{};
151
152pub async fn write(loop: *std.event.Loop, fd: fd_t, buffer: []const u8) WriteError!void {
153 const iov = os.iovec_const{
154 .iov_base = buffer.ptr,
155 .iov_len = buffer.len,
156 };
157 const iovs: *const [1]os.iovec_const = &iov;
158 return writevPosix(loop, fd, iovs, 1);
159}
160
161pub async fn writevPosix(loop: *Loop, fd: i32, iov: [*]const os.iovec_const, count: usize) !void {
162 while (true) {
163 switch (builtin.os) {
164 .macosx, .linux => {
165 switch (os.errno(os.system.writev(fd, iov, count))) {
166 0 => return,
167 os.EINTR => continue,
168 os.ESPIPE => unreachable,
169 os.EINVAL => unreachable,
170 os.EFAULT => unreachable,
171 os.EAGAIN => {
172 try loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLOUT);
173 continue;
174 },
175 os.EBADF => unreachable, // always a race condition
176 os.EDESTADDRREQ => unreachable, // connect was never called
177 os.EDQUOT => unreachable,
178 os.EFBIG => unreachable,
179 os.EIO => return error.InputOutput,
180 os.ENOSPC => unreachable,
181 os.EPERM => return error.AccessDenied,
182 os.EPIPE => unreachable,
183 else => |err| return os.unexpectedErrno(err),
184 }
185 },
186 else => @compileError("Unsupported OS"),
187 }
188 }
189}
190
191/// returns number of bytes read. 0 means EOF.
192pub async fn readvPosix(loop: *std.event.Loop, fd: i32, iov: [*]os.iovec, count: usize) !usize {
193 while (true) {
194 switch (builtin.os) {
195 builtin.Os.linux, builtin.Os.freebsd, builtin.Os.macosx => {
196 const rc = os.system.readv(fd, iov, count);
197 switch (os.errno(rc)) {
198 0 => return rc,
199 os.EINTR => continue,
200 os.EINVAL => unreachable,
201 os.EFAULT => unreachable,
202 os.EAGAIN => {
203 try loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN);
204 continue;
205 },
206 os.EBADF => unreachable, // always a race condition
207 os.EIO => return error.InputOutput,
208 os.EISDIR => unreachable,
209 os.ENOBUFS => return error.SystemResources,
210 os.ENOMEM => return error.SystemResources,
211 else => |err| return os.unexpectedErrno(err),
212 }
213 },
214 else => @compileError("Unsupported OS"),
215 }
216 }
217}
218
219pub async fn writev(loop: *Loop, fd: fd_t, data: []const []const u8) !void {
220 const iovecs = try loop.allocator.alloc(os.iovec_const, data.len);
221 defer loop.allocator.free(iovecs);
222
223 for (data) |buf, i| {
224 iovecs[i] = os.iovec_const{
225 .iov_base = buf.ptr,
226 .iov_len = buf.len,
227 };
228 }
229
230 return writevPosix(loop, fd, iovecs.ptr, data.len);
231}
232
233pub async fn readv(loop: *Loop, fd: fd_t, data: []const []u8) !usize {
234 const iovecs = try loop.allocator.alloc(os.iovec, data.len);
235 defer loop.allocator.free(iovecs);
236
237 for (data) |buf, i| {
238 iovecs[i] = os.iovec{
239 .iov_base = buf.ptr,
240 .iov_len = buf.len,
241 };
242 }
243
244 return readvPosix(loop, fd, iovecs.ptr, data.len);
245}
246
247pub async fn connect(loop: *Loop, _address: *const std.net.Address) !File {
248 var address = _address.*; // TODO https://github.com/ziglang/zig/issues/1592
249
250 const sockfd = try os.socket(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK, os.PROTO_tcp);
251 errdefer os.close(sockfd);
252
253 try os.connect_async(sockfd, &address.os_addr, @sizeOf(os.sockaddr_in));
254 try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
255 try os.getsockoptError(sockfd);
256
257 return File.openHandle(sockfd);
258}
259
260test "listen on a port, send bytes, receive bytes" {
261 // https://github.com/ziglang/zig/issues/2377
262 if (true) return error.SkipZigTest;
263
264 if (builtin.os != builtin.Os.linux) {
265 // TODO build abstractions for other operating systems
266 return error.SkipZigTest;
267 }
268
269 const MyServer = struct {
270 tcp_server: Server,
271
272 const Self = @This();
273 async fn handler(tcp_server: *Server, _addr: *const std.net.Address, _socket: File) void {
274 const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
275 var socket = _socket; // TODO https://github.com/ziglang/zig/issues/1592
276 defer socket.close();
277 const next_handler = errorableHandler(self, _addr, socket) catch |err| {
278 std.debug.panic("unable to handle connection: {}\n", err);
279 };
280 }
281 async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: File) !void {
282 const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/1592
283 var socket = _socket; // TODO https://github.com/ziglang/zig/issues/1592
284
285 const stream = &socket.outStream().stream;
286 try stream.print("hello from server\n");
287 }
288 };
289
290 const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable;
291 const addr = std.net.Address.initIp4(ip4addr, 0);
292
293 var loop: Loop = undefined;
294 try loop.initSingleThreaded(std.debug.global_allocator);
295 var server = MyServer{ .tcp_server = Server.init(&loop) };
296 defer server.tcp_server.deinit();
297 try server.tcp_server.listen(&addr, MyServer.handler);
298
299 _ = async doAsyncTest(&loop, &server.tcp_server.listen_address, &server.tcp_server);
300 loop.run();
301}
302
303async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Server) void {
304 errdefer @panic("test failure");
305
306 var socket_file = try connect(loop, address);
307 defer socket_file.close();
308
309 var buf: [512]u8 = undefined;
310 const amt_read = try socket_file.read(buf[0..]);
311 const msg = buf[0..amt_read];
312 testing.expect(mem.eql(u8, msg, "hello from server\n"));
313 server.close();
314}
315
316pub const OutStream = struct {
317 fd: fd_t,
318 stream: Stream,
319 loop: *Loop,
320
321 pub const Error = WriteError;
322 pub const Stream = event.io.OutStream(Error);
323
324 pub fn init(loop: *Loop, fd: fd_t) OutStream {
325 return OutStream{
326 .fd = fd,
327 .loop = loop,
328 .stream = Stream{ .writeFn = writeFn },
329 };
330 }
331
332 async fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {
333 const self = @fieldParentPtr(OutStream, "stream", out_stream);
334 return write(self.loop, self.fd, bytes);
335 }
336};
337
338pub const InStream = struct {
339 fd: fd_t,
340 stream: Stream,
341 loop: *Loop,
342
343 pub const Error = ReadError;
344 pub const Stream = event.io.InStream(Error);
345
346 pub fn init(loop: *Loop, fd: fd_t) InStream {
347 return InStream{
348 .fd = fd,
349 .loop = loop,
350 .stream = Stream{ .readFn = readFn },
351 };
352 }
353
354 async fn readFn(in_stream: *Stream, bytes: []u8) Error!usize {
355 const self = @fieldParentPtr(InStream, "stream", in_stream);
356 return read(self.loop, self.fd, bytes);
357 }
358};
lib/std/fmt.zig+2-2
......@@ -53,7 +53,7 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
5353/// The format string must be comptime known and may contain placeholders following
5454/// this format:
5555/// `{[position][specifier]:[fill][alignment][width].[precision]}`
56///
56///
5757/// Each word between `[` and `]` is a parameter you have to replace with something:
5858///
5959/// - *position* is the index of the argument that should be inserted
......@@ -78,7 +78,7 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
7878/// - `d`: output numeric value in decimal notation
7979/// - `b`: output integer value in binary notation
8080/// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max.
81/// - `*`: output the address of the value instead of the value itself.
81/// - `*`: output the address of the value instead of the value itself.
8282///
8383/// If a formatted user type contains a function of the type
8484/// ```
lib/std/io.zig+1-62
......@@ -64,68 +64,7 @@ pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
6464pub const SliceSeekableInStream = @import("io/seekable_stream.zig").SliceSeekableInStream;
6565pub const COutStream = @import("io/c_out_stream.zig").COutStream;
6666pub const InStream = @import("io/in_stream.zig").InStream;
67
68pub fn OutStream(comptime WriteError: type) type {
69 return struct {
70 const Self = @This();
71 pub const Error = WriteError;
72
73 writeFn: fn (self: *Self, bytes: []const u8) Error!void,
74
75 pub fn print(self: *Self, comptime format: []const u8, args: ...) Error!void {
76 return std.fmt.format(self, Error, self.writeFn, format, args);
77 }
78
79 pub fn write(self: *Self, bytes: []const u8) Error!void {
80 return self.writeFn(self, bytes);
81 }
82
83 pub fn writeByte(self: *Self, byte: u8) Error!void {
84 const slice = (*const [1]u8)(&byte)[0..];
85 return self.writeFn(self, slice);
86 }
87
88 pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) Error!void {
89 const slice = (*const [1]u8)(&byte)[0..];
90 var i: usize = 0;
91 while (i < n) : (i += 1) {
92 try self.writeFn(self, slice);
93 }
94 }
95
96 /// Write a native-endian integer.
97 pub fn writeIntNative(self: *Self, comptime T: type, value: T) Error!void {
98 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
99 mem.writeIntNative(T, &bytes, value);
100 return self.writeFn(self, bytes);
101 }
102
103 /// Write a foreign-endian integer.
104 pub fn writeIntForeign(self: *Self, comptime T: type, value: T) Error!void {
105 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
106 mem.writeIntForeign(T, &bytes, value);
107 return self.writeFn(self, bytes);
108 }
109
110 pub fn writeIntLittle(self: *Self, comptime T: type, value: T) Error!void {
111 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
112 mem.writeIntLittle(T, &bytes, value);
113 return self.writeFn(self, bytes);
114 }
115
116 pub fn writeIntBig(self: *Self, comptime T: type, value: T) Error!void {
117 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
118 mem.writeIntBig(T, &bytes, value);
119 return self.writeFn(self, bytes);
120 }
121
122 pub fn writeInt(self: *Self, comptime T: type, value: T, endian: builtin.Endian) Error!void {
123 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
124 mem.writeInt(T, &bytes, value, endian);
125 return self.writeFn(self, bytes);
126 }
127 };
128}
67pub const OutStream = @import("io/out_stream.zig").OutStream;
12968
13069/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
13170pub fn writeFile(path: []const u8, data: []const u8) !void {
lib/std/io/in_stream.zig+1-2
......@@ -11,7 +11,6 @@ pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_InStream"))
1111 root.stack_size_std_io_InStream
1212else
1313 default_stack_size;
14pub const stack_align = 16;
1514
1615pub fn InStream(comptime ReadError: type) type {
1716 return struct {
......@@ -34,7 +33,7 @@ pub fn InStream(comptime ReadError: type) type {
3433 if (std.io.is_async) {
3534 // Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream read.
3635 @setRuntimeSafety(false);
37 var stack_frame: [stack_size]u8 align(stack_align) = undefined;
36 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
3837 return await @asyncCall(&stack_frame, {}, self.readFn, self, buffer);
3938 } else {
4039 return self.readFn(self, buffer);
lib/std/io/out_stream.zig created+87
......@@ -0,0 +1,87 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const root = @import("root");
4const mem = std.mem;
5
6pub const default_stack_size = 1 * 1024 * 1024;
7pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_OutStream"))
8 root.stack_size_std_io_OutStream
9else
10 default_stack_size;
11
12/// TODO this is not integrated with evented I/O yet.
13/// https://github.com/ziglang/zig/issues/3557
14pub fn OutStream(comptime WriteError: type) type {
15 return struct {
16 const Self = @This();
17 pub const Error = WriteError;
18 // TODO https://github.com/ziglang/zig/issues/3557
19 pub const WriteFn = if (std.io.is_async and false)
20 async fn (self: *Self, bytes: []const u8) Error!void
21 else
22 fn (self: *Self, bytes: []const u8) Error!void;
23
24 writeFn: WriteFn,
25
26 pub fn write(self: *Self, bytes: []const u8) Error!void {
27 // TODO https://github.com/ziglang/zig/issues/3557
28 if (std.io.is_async and false) {
29 // Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream write.
30 @setRuntimeSafety(false);
31 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
32 return await @asyncCall(&stack_frame, {}, self.writeFn, self, bytes);
33 } else {
34 return self.writeFn(self, bytes);
35 }
36 }
37
38 pub fn print(self: *Self, comptime format: []const u8, args: ...) Error!void {
39 return std.fmt.format(self, Error, self.writeFn, format, args);
40 }
41
42 pub fn writeByte(self: *Self, byte: u8) Error!void {
43 const slice = (*const [1]u8)(&byte)[0..];
44 return self.writeFn(self, slice);
45 }
46
47 pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) Error!void {
48 const slice = (*const [1]u8)(&byte)[0..];
49 var i: usize = 0;
50 while (i < n) : (i += 1) {
51 try self.writeFn(self, slice);
52 }
53 }
54
55 /// Write a native-endian integer.
56 pub fn writeIntNative(self: *Self, comptime T: type, value: T) Error!void {
57 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
58 mem.writeIntNative(T, &bytes, value);
59 return self.writeFn(self, bytes);
60 }
61
62 /// Write a foreign-endian integer.
63 pub fn writeIntForeign(self: *Self, comptime T: type, value: T) Error!void {
64 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
65 mem.writeIntForeign(T, &bytes, value);
66 return self.writeFn(self, bytes);
67 }
68
69 pub fn writeIntLittle(self: *Self, comptime T: type, value: T) Error!void {
70 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
71 mem.writeIntLittle(T, &bytes, value);
72 return self.writeFn(self, bytes);
73 }
74
75 pub fn writeIntBig(self: *Self, comptime T: type, value: T) Error!void {
76 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
77 mem.writeIntBig(T, &bytes, value);
78 return self.writeFn(self, bytes);
79 }
80
81 pub fn writeInt(self: *Self, comptime T: type, value: T, endian: builtin.Endian) Error!void {
82 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
83 mem.writeInt(T, &bytes, value, endian);
84 return self.writeFn(self, bytes);
85 }
86 };
87}
lib/std/net.zig+160-38
......@@ -6,6 +6,10 @@ const mem = std.mem;
66const os = std.os;
77const fs = std.fs;
88
9test "" {
10 _ = @import("net/test.zig");
11}
12
913pub const TmpWinAddr = struct {
1014 family: u8,
1115 data: [14]u8,
......@@ -21,6 +25,9 @@ pub const OsAddress = switch (builtin.os) {
2125pub const Address = struct {
2226 os_addr: OsAddress,
2327
28 // TODO this crashed the compiler
29 //pub const localhost = initIp4(parseIp4("127.0.0.1") catch unreachable, 0);
30
2431 pub fn initIp4(ip4: u32, _port: u16) Address {
2532 return Address{
2633 .os_addr = os.sockaddr{
......@@ -141,6 +148,14 @@ pub const Address = struct {
141148 else => return output(context, "(unrecognized address family)"),
142149 }
143150 }
151
152 fn getOsSockLen(self: Address) os.socklen_t {
153 switch (self.os_addr.un.family) {
154 os.AF_INET => return @sizeOf(os.sockaddr_in),
155 os.AF_INET6 => return @sizeOf(os.sockaddr_in6),
156 else => unreachable,
157 }
158 }
144159};
145160
146161pub fn parseIp4(buf: []const u8) !u32 {
......@@ -260,34 +275,8 @@ pub fn parseIp6(buf: []const u8) !Ip6Addr {
260275 return error.Incomplete;
261276}
262277
263test "std.net.parseIp4" {
264 assert((try parseIp4("127.0.0.1")) == mem.bigToNative(u32, 0x7f000001));
265
266 testParseIp4Fail("256.0.0.1", error.Overflow);
267 testParseIp4Fail("x.0.0.1", error.InvalidCharacter);
268 testParseIp4Fail("127.0.0.1.1", error.InvalidEnd);
269 testParseIp4Fail("127.0.0.", error.Incomplete);
270 testParseIp4Fail("100..0.1", error.InvalidCharacter);
271}
272
273fn testParseIp4Fail(buf: []const u8, expected_err: anyerror) void {
274 if (parseIp4(buf)) |_| {
275 @panic("expected error");
276 } else |e| {
277 assert(e == expected_err);
278 }
279}
280
281test "std.net.parseIp6" {
282 const ip6 = try parseIp6("FF01:0:0:0:0:0:0:FB");
283 const addr = Address.initIp6(ip6, 80);
284 var buf: [100]u8 = undefined;
285 const printed = try std.fmt.bufPrint(&buf, "{}", addr);
286 std.testing.expect(mem.eql(u8, "[ff01::fb]:80", printed));
287}
288
289278pub fn connectUnixSocket(path: []const u8) !fs.File {
290 const opt_non_block = if (std.event.Loop.instance != null) os.SOCK_NONBLOCK else 0;
279 const opt_non_block = if (std.io.mode == .evented) os.SOCK_NONBLOCK else 0;
291280 const sockfd = try os.socket(
292281 os.AF_UNIX,
293282 os.SOCK_STREAM | os.SOCK_CLOEXEC | opt_non_block,
......@@ -305,13 +294,7 @@ pub fn connectUnixSocket(path: []const u8) !fs.File {
305294 if (path.len > @typeOf(sock_addr.un.path).len) return error.NameTooLong;
306295 mem.copy(u8, sock_addr.un.path[0..], path);
307296 const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len);
308 if (std.event.Loop.instance) |loop| {
309 try os.connect_async(sockfd, &sock_addr, size);
310 try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
311 try os.getsockoptError(sockfd);
312 } else {
313 try os.connect(sockfd, &sock_addr, size);
314 }
297 try os.connect(sockfd, &sock_addr, size);
315298
316299 return fs.File.openHandle(sockfd);
317300}
......@@ -330,6 +313,27 @@ pub const AddressList = struct {
330313 }
331314};
332315
316/// All memory allocated with `allocator` will be freed before this function returns.
317pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16) !fs.File {
318 const list = getAddressList(allocator, name, port);
319 defer list.deinit();
320
321 const addrs = list.addrs.toSliceConst();
322 if (addrs.len == 0) return error.UnknownHostName;
323
324 return tcpConnectToAddress(addrs[0], port);
325}
326
327pub fn tcpConnectToAddress(address: Address) !fs.File {
328 const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
329 const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock;
330 const sockfd = try os.socket(address.os_addr.un.family, sock_flags, os.IPPROTO_TCP);
331 errdefer os.close(sockfd);
332 try os.connect(sockfd, address.os_addr, address.getOsSockLen());
333
334 return fs.File{ .handle = sockfd };
335}
336
333337/// Call `AddressList.deinit` on the result.
334338pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*AddressList {
335339 const result = blk: {
......@@ -375,7 +379,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
375379 c.EAI_FAMILY => return error.AddressFamilyNotSupported,
376380 c.EAI_MEMORY => return error.OutOfMemory,
377381 c.EAI_NODATA => return error.HostLacksNetworkAddresses,
378 c.EAI_NONAME => return error.UnknownName,
382 c.EAI_NONAME => return error.UnknownHostName,
379383 c.EAI_SERVICE => return error.ServiceUnavailable,
380384 c.EAI_SOCKTYPE => unreachable, // Invalid socket type requested in hints
381385 c.EAI_SYSTEM => switch (os.errno(-1)) {
......@@ -493,7 +497,7 @@ fn linuxLookupName(
493497 try canon.resize(0);
494498 try linuxLookupNameFromNull(addrs, family, flags);
495499 }
496 if (addrs.len == 0) return error.UnknownName;
500 if (addrs.len == 0) return error.UnknownHostName;
497501
498502 // No further processing is needed if there are fewer than 2
499503 // results or if there are only IPv4 results.
......@@ -858,7 +862,7 @@ fn linuxLookupNameFromDnsSearch(
858862
859863 // Strip final dot for canon, fail if multiple trailing dots.
860864 if (mem.endsWith(u8, canon_name, ".")) canon_name.len -= 1;
861 if (mem.endsWith(u8, canon_name, ".")) return error.UnknownName;
865 if (mem.endsWith(u8, canon_name, ".")) return error.UnknownHostName;
862866
863867 // Name with search domain appended is setup in canon[]. This both
864868 // provides the desired default canonical name (if the requested
......@@ -928,7 +932,7 @@ fn linuxLookupNameFromDns(
928932
929933 if (addrs.len != 0) return;
930934 if (ap[0].len < 4 or (ap[0][3] & 15) == 2) return error.TemporaryNameServerFailure;
931 if ((ap[0][3] & 15) == 0) return error.UnknownName;
935 if ((ap[0][3] & 15) == 0) return error.UnknownHostName;
932936 if ((ap[0][3] & 15) == 3) return;
933937 return error.NameServerFailure;
934938}
......@@ -1247,3 +1251,121 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8)
12471251 else => return,
12481252 }
12491253}
1254
1255/// This API only works when `std.io.mode` is `std.io.Mode.evented`.
1256/// This struct is immovable after calling `listen`.
1257pub const Server = struct {
1258 /// This field is meant to be accessed directly.
1259 /// Call `connections.get` to accept a connection.
1260 connections: *ConnectionChannel,
1261
1262 /// Copied from `Options` on `init`.
1263 kernel_backlog: u32,
1264
1265 /// `undefined` until `listen` returns successfully.
1266 listen_address: Address,
1267
1268 sockfd: ?os.fd_t,
1269 accept_frame: @Frame(acceptConnections),
1270
1271 pub const ConnectionChannel = std.event.Channel(AcceptError!fs.File);
1272
1273 pub const AcceptError = error{
1274 ConnectionAborted,
1275
1276 /// The per-process limit on the number of open file descriptors has been reached.
1277 ProcessFdQuotaExceeded,
1278
1279 /// The system-wide limit on the total number of open files has been reached.
1280 SystemFdQuotaExceeded,
1281
1282 /// Not enough free memory. This often means that the memory allocation is limited
1283 /// by the socket buffer limits, not by the system memory.
1284 SystemResources,
1285
1286 ProtocolFailure,
1287
1288 /// Firewall rules forbid connection.
1289 BlockedByFirewall,
1290 } || os.UnexpectedError;
1291
1292 pub const Options = struct {
1293 /// How many connections the kernel will accept on the application's behalf.
1294 /// If more than this many connections pool in the kernel, clients will start
1295 /// seeing "Connection refused".
1296 kernel_backlog: u32 = 128,
1297
1298 /// How many connections this `Server` will accept from the kernel even before
1299 /// they are requested from the `connections` channel.
1300 eager_connections: usize = 16,
1301 };
1302
1303 /// After this call succeeds, resources have been acquired and must
1304 /// be released with `deinit`.
1305 pub fn init(options: Options) !Server {
1306 const loop = std.event.Loop.instance orelse
1307 @compileError("std.net.Server only works in evented I/O mode");
1308 return Server{
1309 .connections = try ConnectionChannel.create(loop, options.eager_connections),
1310 .sockfd = null,
1311 .kernel_backlog = options.kernel_backlog,
1312 .listen_address = undefined,
1313 .accept_frame = undefined,
1314 };
1315 }
1316
1317 /// After calling this function, one must call `init` to do anything else with this `Server`.
1318 pub fn deinit(self: *Server) void {
1319 self.close();
1320 self.connections.destroy();
1321 self.* = undefined;
1322 }
1323
1324 pub fn listen(self: *Server, address: Address) !void {
1325 const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK;
1326 const sockfd = try os.socket(os.AF_INET, sock_flags, os.PROTO_tcp);
1327 self.sockfd = sockfd;
1328 errdefer {
1329 os.close(sockfd);
1330 self.sockfd = null;
1331 }
1332
1333 var socklen = address.getOsSockLen();
1334 try os.bind(sockfd, &address.os_addr, socklen);
1335 try os.listen(sockfd, self.kernel_backlog);
1336 try os.getsockname(sockfd, &self.listen_address.os_addr, &socklen);
1337
1338 // acceptConnections loops, calling os.accept().
1339 self.accept_frame = async self.acceptConnections();
1340 errdefer await self.accept_frame;
1341 }
1342
1343 /// Stop listening. It is still necessary to call `deinit` after stopping listening.
1344 /// Calling `deinit` will automatically call `close`. It is safe to call `close` when
1345 /// not listening.
1346 pub fn close(self: *Server) void {
1347 if (self.sockfd) |fd| {
1348 os.close(fd);
1349 self.sockfd = null;
1350 await self.accept_frame;
1351 self.accept_frame = undefined;
1352 self.listen_address = undefined;
1353 }
1354 }
1355
1356 fn acceptConnections(self: *Server) void {
1357 const sockfd = self.sockfd.?;
1358 const accept_flags = os.SOCK_NONBLOCK | os.SOCK_CLOEXEC;
1359 while (true) {
1360 var accepted_addr: Address = undefined;
1361 var addr_len: os.socklen_t = @sizeOf(os.sockaddr);
1362 const conn = if (os.accept4(sockfd, &accepted_addr.os_addr, &addr_len, accept_flags)) |fd|
1363 fs.File.openHandle(fd)
1364 else |err| switch (err) {
1365 error.WouldBlock => unreachable, // we asserted earlier about non-blocking I/O mode
1366 else => |e| e,
1367 };
1368 self.connections.put(conn);
1369 }
1370 }
1371};
lib/std/net/test.zig created+71
......@@ -0,0 +1,71 @@
1const std = @import("../std.zig");
2const net = std.net;
3const mem = std.mem;
4const testing = std.testing;
5
6test "std.net.parseIp4" {
7 assert((try parseIp4("127.0.0.1")) == mem.bigToNative(u32, 0x7f000001));
8
9 testParseIp4Fail("256.0.0.1", error.Overflow);
10 testParseIp4Fail("x.0.0.1", error.InvalidCharacter);
11 testParseIp4Fail("127.0.0.1.1", error.InvalidEnd);
12 testParseIp4Fail("127.0.0.", error.Incomplete);
13 testParseIp4Fail("100..0.1", error.InvalidCharacter);
14}
15
16fn testParseIp4Fail(buf: []const u8, expected_err: anyerror) void {
17 if (parseIp4(buf)) |_| {
18 @panic("expected error");
19 } else |e| {
20 assert(e == expected_err);
21 }
22}
23
24test "std.net.parseIp6" {
25 const ip6 = try parseIp6("FF01:0:0:0:0:0:0:FB");
26 const addr = Address.initIp6(ip6, 80);
27 var buf: [100]u8 = undefined;
28 const printed = try std.fmt.bufPrint(&buf, "{}", addr);
29 std.testing.expect(mem.eql(u8, "[ff01::fb]:80", printed));
30}
31
32test "listen on a port, send bytes, receive bytes" {
33 if (std.builtin.os != .linux) {
34 // TODO build abstractions for other operating systems
35 return error.SkipZigTest;
36 }
37 if (std.io.mode != .evented) {
38 // TODO add ability to run tests in non-blocking I/O mode
39 return error.SkipZigTest;
40 }
41
42 // TODO doing this at comptime crashed the compiler
43 const localhost = net.Address.initIp4(net.parseIp4("127.0.0.1") catch unreachable, 0);
44
45 var server = try net.Server.init(net.Server.Options{});
46 defer server.deinit();
47 try server.listen(localhost);
48
49 var server_frame = async testServer(&server);
50 var client_frame = async testClient(server.listen_address);
51
52 try await server_frame;
53 try await client_frame;
54}
55
56fn testClient(addr: net.Address) anyerror!void {
57 const socket_file = try net.tcpConnectToAddress(addr);
58 defer socket_file.close();
59
60 var buf: [100]u8 = undefined;
61 const len = try socket_file.read(&buf);
62 const msg = buf[0..len];
63 testing.expect(mem.eql(u8, msg, "hello from server\n"));
64}
65
66fn testServer(server: *net.Server) anyerror!void {
67 var client_file = try server.connections.get();
68
69 const stream = &client_file.outStream().stream;
70 try stream.print("hello from server\n");
71}
lib/std/os.zig+99-62
......@@ -308,7 +308,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
308308 EINVAL => unreachable,
309309 EFAULT => unreachable,
310310 EAGAIN => if (std.event.Loop.instance) |loop| {
311 loop.waitUntilFdReadable(fd) catch return error.WouldBlock;
311 loop.waitUntilFdReadable(fd);
312312 continue;
313313 } else {
314314 return error.WouldBlock;
......@@ -325,7 +325,36 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
325325}
326326
327327/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
328/// This function is for blocking file descriptors only.
328/// If the application has a global event loop enabled, EAGAIN is handled
329/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
330pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
331 while (true) {
332 // TODO handle the case when iov_len is too large and get rid of this @intCast
333 const rc = system.readv(fd, iov.ptr, @intCast(u32, iov.len));
334 switch (errno(rc)) {
335 0 => return @bitCast(usize, rc),
336 EINTR => continue,
337 EINVAL => unreachable,
338 EFAULT => unreachable,
339 EAGAIN => if (std.event.Loop.instance) |loop| {
340 loop.waitUntilFdReadable(fd);
341 continue;
342 } else {
343 return error.WouldBlock;
344 },
345 EBADF => unreachable, // always a race condition
346 EIO => return error.InputOutput,
347 EISDIR => return error.IsDir,
348 ENOBUFS => return error.SystemResources,
349 ENOMEM => return error.SystemResources,
350 else => |err| return unexpectedErrno(err),
351 }
352 }
353}
354
355/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
356/// If the application has a global event loop enabled, EAGAIN is handled
357/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
329358pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize {
330359 if (comptime std.Target.current.isDarwin()) {
331360 // Darwin does not have preadv but it does have pread.
......@@ -355,7 +384,12 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize {
355384 EINVAL => unreachable,
356385 EFAULT => unreachable,
357386 ESPIPE => unreachable, // fd is not seekable
358 EAGAIN => unreachable, // This function is for blocking reads.
387 EAGAIN => if (std.event.Loop.instance) |loop| {
388 loop.waitUntilFdReadable(fd);
389 continue;
390 } else {
391 return error.WouldBlock;
392 },
359393 EBADF => unreachable, // always a race condition
360394 EIO => return error.InputOutput,
361395 EISDIR => return error.IsDir,
......@@ -373,7 +407,12 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize {
373407 EINTR => continue,
374408 EINVAL => unreachable,
375409 EFAULT => unreachable,
376 EAGAIN => unreachable, // This function is for blocking reads.
410 EAGAIN => if (std.event.Loop.instance) |loop| {
411 loop.waitUntilFdReadable(fd);
412 continue;
413 } else {
414 return error.WouldBlock;
415 },
377416 EBADF => unreachable, // always a race condition
378417 EIO => return error.InputOutput,
379418 EISDIR => return error.IsDir,
......@@ -393,10 +432,17 @@ pub const WriteError = error{
393432 BrokenPipe,
394433 SystemResources,
395434 OperationAborted,
435
436 /// This error occurs when no global event loop is configured,
437 /// and reading from the file descriptor would block.
438 WouldBlock,
396439} || UnexpectedError;
397440
398441/// Write to a file descriptor. Keeps trying if it gets interrupted.
399/// This function is for blocking file descriptors only.
442/// If the application has a global event loop enabled, EAGAIN is handled
443/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
444/// TODO evented I/O integration is disabled until
445/// https://github.com/ziglang/zig/issues/3557 is solved.
400446pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
401447 if (builtin.os == .windows) {
402448 return windows.WriteFile(fd, bytes);
......@@ -432,7 +478,14 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
432478 EINTR => continue,
433479 EINVAL => unreachable,
434480 EFAULT => unreachable,
435 EAGAIN => unreachable, // This function is for blocking writes.
481 // TODO https://github.com/ziglang/zig/issues/3557
482 EAGAIN => return error.WouldBlock,
483 //EAGAIN => if (std.event.Loop.instance) |loop| {
484 // loop.waitUntilFdWritable(fd);
485 // continue;
486 //} else {
487 // return error.WouldBlock;
488 //},
436489 EBADF => unreachable, // Always a race condition.
437490 EDESTADDRREQ => unreachable, // `connect` was never called.
438491 EDQUOT => return error.DiskQuota,
......@@ -446,9 +499,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
446499 }
447500}
448501
449/// Write multiple buffers to a file descriptor. Keeps trying if it gets interrupted.
450/// This function is for blocking file descriptors only. For non-blocking, see
451/// `writevAsync`.
502/// Write multiple buffers to a file descriptor.
503/// If the application has a global event loop enabled, EAGAIN is handled
504/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
452505pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void {
453506 while (true) {
454507 // TODO handle the case when iov_len is too large and get rid of this @intCast
......@@ -458,7 +511,12 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void {
458511 EINTR => continue,
459512 EINVAL => unreachable,
460513 EFAULT => unreachable,
461 EAGAIN => unreachable, // This function is for blocking writes.
514 EAGAIN => if (std.event.Loop.instance) |loop| {
515 loop.waitUntilFdWritable(fd);
516 continue;
517 } else {
518 return error.WouldBlock;
519 },
462520 EBADF => unreachable, // Always a race condition.
463521 EDESTADDRREQ => unreachable, // `connect` was never called.
464522 EDQUOT => return error.DiskQuota,
......@@ -474,8 +532,6 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void {
474532
475533/// Write multiple buffers to a file descriptor, with a position offset.
476534/// Keeps trying if it gets interrupted.
477/// This function is for blocking file descriptors only. For non-blocking, see
478/// `pwritevAsync`.
479535pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void {
480536 if (comptime std.Target.current.isDarwin()) {
481537 // Darwin does not have pwritev but it does have pwrite.
......@@ -504,7 +560,12 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void
504560 ESPIPE => unreachable, // `fd` is not seekable.
505561 EINVAL => unreachable,
506562 EFAULT => unreachable,
507 EAGAIN => unreachable, // This function is for blocking writes.
563 EAGAIN => if (std.event.Loop.instance) |loop| {
564 loop.waitUntilFdWritable(fd);
565 continue;
566 } else {
567 return error.WouldBlock;
568 },
508569 EBADF => unreachable, // Always a race condition.
509570 EDESTADDRREQ => unreachable, // `connect` was never called.
510571 EDQUOT => return error.DiskQuota,
......@@ -526,7 +587,12 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void
526587 EINTR => continue,
527588 EINVAL => unreachable,
528589 EFAULT => unreachable,
529 EAGAIN => unreachable, // This function is for blocking writes.
590 EAGAIN => if (std.event.Loop.instance) |loop| {
591 loop.waitUntilFdWritable(fd);
592 continue;
593 } else {
594 return error.WouldBlock;
595 },
530596 EBADF => unreachable, // Always a race condition.
531597 EDESTADDRREQ => unreachable, // `connect` was never called.
532598 EDQUOT => return error.DiskQuota,
......@@ -1621,12 +1687,6 @@ pub const AcceptError = error{
16211687 /// by the socket buffer limits, not by the system memory.
16221688 SystemResources,
16231689
1624 /// The file descriptor sockfd does not refer to a socket.
1625 FileDescriptorNotASocket,
1626
1627 /// The referenced socket is not of type SOCK_STREAM.
1628 OperationNotSupported,
1629
16301690 ProtocolFailure,
16311691
16321692 /// Firewall rules forbid connection.
......@@ -1643,7 +1703,7 @@ pub const AcceptError = error{
16431703pub fn accept4(
16441704 /// This argument is a socket that has been created with `socket`, bound to a local address
16451705 /// with `bind`, and is listening for connections after a `listen`.
1646 sockfd: i32,
1706 sockfd: fd_t,
16471707 /// This argument is a pointer to a sockaddr structure. This structure is filled in with the
16481708 /// address of the peer socket, as known to the communications layer. The exact format of the
16491709 /// address returned addr is determined by the socket's address family (see `socket` and the
......@@ -1664,15 +1724,15 @@ pub fn accept4(
16641724 /// * `SOCK_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the
16651725 /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.
16661726 flags: u32,
1667) AcceptError!i32 {
1727) AcceptError!fd_t {
16681728 while (true) {
16691729 const rc = system.accept4(sockfd, addr, addr_size, flags);
16701730 switch (errno(rc)) {
1671 0 => return @intCast(i32, rc),
1731 0 => return @intCast(fd_t, rc),
16721732 EINTR => continue,
16731733
16741734 EAGAIN => if (std.event.Loop.instance) |loop| {
1675 loop.waitUntilFdReadable(sockfd) catch return error.WouldBlock;
1735 loop.waitUntilFdReadable(sockfd);
16761736 continue;
16771737 } else {
16781738 return error.WouldBlock;
......@@ -1681,12 +1741,12 @@ pub fn accept4(
16811741 ECONNABORTED => return error.ConnectionAborted,
16821742 EFAULT => unreachable,
16831743 EINVAL => unreachable,
1744 ENOTSOCK => unreachable,
16841745 EMFILE => return error.ProcessFdQuotaExceeded,
16851746 ENFILE => return error.SystemFdQuotaExceeded,
16861747 ENOBUFS => return error.SystemResources,
16871748 ENOMEM => return error.SystemResources,
1688 ENOTSOCK => return error.FileDescriptorNotASocket,
1689 EOPNOTSUPP => return error.OperationNotSupported,
1749 EOPNOTSUPP => unreachable,
16901750 EPROTO => return error.ProtocolFailure,
16911751 EPERM => return error.BlockedByFirewall,
16921752
......@@ -1853,26 +1913,31 @@ pub const ConnectError = error{
18531913 /// Timeout while attempting connection. The server may be too busy to accept new connections. Note
18541914 /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
18551915 ConnectionTimedOut,
1916
1917 /// This error occurs when no global event loop is configured,
1918 /// and connecting to the socket would block.
1919 WouldBlock,
18561920} || UnexpectedError;
18571921
18581922/// Initiate a connection on a socket.
1859/// This is for blocking file descriptors only.
1860/// For non-blocking, see `connect_async`.
1861pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void {
1923pub fn connect(sockfd: fd_t, sock_addr: sockaddr, len: socklen_t) ConnectError!void {
18621924 while (true) {
1863 switch (errno(system.connect(sockfd, sock_addr, len))) {
1925 switch (errno(system.connect(sockfd, &sock_addr, len))) {
18641926 0 => return,
18651927 EACCES => return error.PermissionDenied,
18661928 EPERM => return error.PermissionDenied,
18671929 EADDRINUSE => return error.AddressInUse,
18681930 EADDRNOTAVAIL => return error.AddressNotAvailable,
18691931 EAFNOSUPPORT => return error.AddressFamilyNotSupported,
1870 EAGAIN => return error.SystemResources,
1932 EAGAIN, EINPROGRESS => {
1933 const loop = std.event.Loop.instance orelse return error.WouldBlock;
1934 loop.waitUntilFdWritableOrReadable(sockfd);
1935 return getsockoptError(sockfd);
1936 },
18711937 EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
18721938 EBADF => unreachable, // sockfd is not a valid open file descriptor.
18731939 ECONNREFUSED => return error.ConnectionRefused,
18741940 EFAULT => unreachable, // The socket structure address is outside the user's address space.
1875 EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately.
18761941 EINTR => continue,
18771942 EISCONN => unreachable, // The socket is already connected.
18781943 ENETUNREACH => return error.NetworkUnreachable,
......@@ -1884,34 +1949,6 @@ pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!v
18841949 }
18851950}
18861951
1887/// Same as `connect` except it is for non-blocking socket file descriptors.
1888/// It expects to receive EINPROGRESS`.
1889pub fn connect_async(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void {
1890 while (true) {
1891 switch (errno(system.connect(sockfd, sock_addr, len))) {
1892 EINVAL => unreachable,
1893 EINTR => continue,
1894 0, EINPROGRESS => return,
1895 EACCES => return error.PermissionDenied,
1896 EPERM => return error.PermissionDenied,
1897 EADDRINUSE => return error.AddressInUse,
1898 EADDRNOTAVAIL => return error.AddressNotAvailable,
1899 EAFNOSUPPORT => return error.AddressFamilyNotSupported,
1900 EAGAIN => return error.SystemResources,
1901 EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
1902 EBADF => unreachable, // sockfd is not a valid open file descriptor.
1903 ECONNREFUSED => return error.ConnectionRefused,
1904 EFAULT => unreachable, // The socket structure address is outside the user's address space.
1905 EISCONN => unreachable, // The socket is already connected.
1906 ENETUNREACH => return error.NetworkUnreachable,
1907 ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
1908 EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
1909 ETIMEDOUT => return error.ConnectionTimedOut,
1910 else => |err| return unexpectedErrno(err),
1911 }
1912 }
1913}
1914
19151952pub fn getsockoptError(sockfd: i32) ConnectError!void {
19161953 var err_code: u32 = undefined;
19171954 var size: u32 = @sizeOf(u32);
......@@ -2962,7 +2999,7 @@ pub fn sendto(
29622999
29633000 EACCES => return error.AccessDenied,
29643001 EAGAIN => if (std.event.Loop.instance) |loop| {
2965 loop.waitUntilFdWritable(sockfd) catch return error.WouldBlock;
3002 loop.waitUntilFdWritable(sockfd);
29663003 continue;
29673004 } else {
29683005 return error.WouldBlock;
......@@ -3065,7 +3102,7 @@ pub fn recvfrom(
30653102 ENOTSOCK => unreachable,
30663103 EINTR => continue,
30673104 EAGAIN => if (std.event.Loop.instance) |loop| {
3068 loop.waitUntilFdReadable(sockfd) catch return error.WouldBlock;
3105 loop.waitUntilFdReadable(sockfd);
30693106 continue;
30703107 } else {
30713108 return error.WouldBlock;
lib/std/target.zig+2
......@@ -205,6 +205,8 @@ pub const Target = union(enum) {
205205 },
206206 };
207207
208 pub const stack_align = 16;
209
208210 pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 {
209211 return std.fmt.allocPrint(
210212 allocator,
src-self-hosted/stage1.zig+1
......@@ -128,6 +128,7 @@ export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, error
128128export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {
129129 const c_out_stream = &std.io.COutStream.init(output_file).stream;
130130 _ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) {
131 error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
131132 error.SystemResources => return Error.SystemResources,
132133 error.OperationAborted => return Error.OperationAborted,
133134 error.BrokenPipe => return Error.BrokenPipe,
src/analyze.cpp+7-1
......@@ -4381,6 +4381,10 @@ static Error analyze_callee_async(CodeGen *g, ZigFn *fn, ZigFn *callee, AstNode
43814381 if (callee->anal_state == FnAnalStateComplete) {
43824382 analyze_fn_async(g, callee, true);
43834383 if (callee->anal_state == FnAnalStateInvalid) {
4384 if (g->trace_err != nullptr) {
4385 g->trace_err = add_error_note(g, g->trace_err, call_node,
4386 buf_sprintf("while checking if '%s' is async", buf_ptr(&fn->symbol_name)));
4387 }
43844388 return ErrorSemanticAnalyzeFail;
43854389 }
43864390 callee_is_async = fn_is_async(callee);
......@@ -7538,7 +7542,9 @@ bool type_is_c_abi_int(CodeGen *g, ZigType *ty) {
75387542
75397543uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field) {
75407544 assert(struct_type->id == ZigTypeIdStruct);
7541 assert(type_is_resolved(struct_type, ResolveStatusSizeKnown));
7545 if (struct_type->data.structure.layout != ContainerLayoutAuto) {
7546 assert(type_is_resolved(struct_type, ResolveStatusSizeKnown));
7547 }
75427548 if (struct_type->data.structure.host_int_bytes == nullptr)
75437549 return 0;
75447550 return struct_type->data.structure.host_int_bytes[field->gen_index];
src/ir.cpp+10-3
......@@ -17692,7 +17692,8 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
1769217692 {
1769317693 size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index;
1769417694 uint64_t new_index = offset + index;
17695 assert(new_index < ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len);
17695 ir_assert(new_index < ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len,
17696 &elem_ptr_instruction->base);
1769617697 out_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
1769717698 out_val->data.x_ptr.data.base_array.array_val =
1769817699 ptr_field->data.x_ptr.data.base_array.array_val;
......@@ -17854,7 +17855,10 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction
1785417855 case OnePossibleValueNo:
1785517856 break;
1785617857 }
17857 if ((err = type_resolve(ira->codegen, struct_type, ResolveStatusAlignmentKnown)))
17858 ResolveStatus needed_resolve_status =
17859 (struct_type->data.structure.layout == ContainerLayoutAuto) ?
17860 ResolveStatusZeroBitsKnown : ResolveStatusSizeKnown;
17861 if ((err = type_resolve(ira->codegen, struct_type, needed_resolve_status)))
1785817862 return ira->codegen->invalid_instruction;
1785917863 assert(struct_ptr->value.type->id == ZigTypeIdPointer);
1786017864 uint32_t ptr_bit_offset = struct_ptr->value.type->data.pointer.bit_offset_in_host;
......@@ -17873,6 +17877,9 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction
1787317877 return ira->codegen->invalid_instruction;
1787417878
1787517879 if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
17880 if ((err = type_resolve(ira->codegen, struct_type, ResolveStatusSizeKnown)))
17881 return ira->codegen->invalid_instruction;
17882
1787617883 ConstExprValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
1787717884 if (struct_val == nullptr)
1787817885 return ira->codegen->invalid_instruction;
......@@ -17919,7 +17926,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
1791917926 Error err;
1792017927
1792117928 ZigType *bare_type = container_ref_type(container_type);
17922 if ((err = type_resolve(ira->codegen, bare_type, ResolveStatusSizeKnown)))
17929 if ((err = type_resolve(ira->codegen, bare_type, ResolveStatusZeroBitsKnown)))
1792317930 return ira->codegen->invalid_instruction;
1792417931
1792517932 assert(container_ptr->value.type->id == ZigTypeIdPointer);