authorgravatar for alex_naskos@hotmail.comAlexandros Naskos <alex_naskos@hotmail.com> 2020-09-29 16:19:44+03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-09-29 16:19:44+03:00
logd27a34f05c8c78aa9a9a6f2a30d4db1468b50999
tree4aea123e0325d8ce3569305d886f2a3fb1355b08
parent55dfe729b480c2ba121c6f650d160921560d9535
parent3342e28784b9ef7bf8356004a7b2698edcb70b40
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge branch 'master' into args-tuple


18 files changed, 1240 insertions(+), 521 deletions(-)

lib/std/event/future.zig+1-1
...@@ -95,7 +95,7 @@ test "std.event.Future" {...@@ -95,7 +95,7 @@ test "std.event.Future" {
95 // TODO provide a way to run tests in evented I/O mode95 // TODO provide a way to run tests in evented I/O mode
96 if (!std.io.is_async) return error.SkipZigTest;96 if (!std.io.is_async) return error.SkipZigTest;
9797
98 const handle = async testFuture();98 testFuture();
99}99}
100100
101fn testFuture() void {101fn testFuture() void {
lib/std/event/lock.zig+92-113
...@@ -16,107 +16,111 @@ const Loop = std.event.Loop;...@@ -16,107 +16,111 @@ const Loop = std.event.Loop;
16/// Allows only one actor to hold the lock.16/// Allows only one actor to hold the lock.
17/// TODO: make this API also work in blocking I/O mode.17/// TODO: make this API also work in blocking I/O mode.
18pub const Lock = struct {18pub const Lock = struct {
19 shared: bool,19 mutex: std.Mutex = std.Mutex{},
20 queue: Queue,20 head: usize = UNLOCKED,
21 queue_empty: bool,
2221
23 const Queue = std.atomic.Queue(anyframe);22 const UNLOCKED = 0;
23 const LOCKED = 1;
2424
25 const global_event_loop = Loop.instance orelse25 const global_event_loop = Loop.instance orelse
26 @compileError("std.event.Lock currently only works with event-based I/O");26 @compileError("std.event.Lock currently only works with event-based I/O");
2727
28 pub const Held = struct {28 const Waiter = struct {
29 lock: *Lock,29 // forced Waiter alignment to ensure it doesn't clash with LOCKED
3030 next: ?*Waiter align(2),
31 pub fn release(self: Held) void {31 tail: *Waiter,
32 // Resume the next item from the queue.32 node: Loop.NextTickNode,
33 if (self.lock.queue.get()) |node| {33 };
34 global_event_loop.onNextTick(node);
35 return;
36 }
37
38 // We need to release the lock.
39 @atomicStore(bool, &self.lock.queue_empty, true, .SeqCst);
40 @atomicStore(bool, &self.lock.shared, false, .SeqCst);
41
42 // There might be a queue item. If we know the queue is empty, we can be done,
43 // because the other actor will try to obtain the lock.
44 // But if there's a queue item, we are the actor which must loop and attempt
45 // to grab the lock again.
46 if (@atomicLoad(bool, &self.lock.queue_empty, .SeqCst)) {
47 return;
48 }
49
50 while (true) {
51 if (@atomicRmw(bool, &self.lock.shared, .Xchg, true, .SeqCst)) {
52 // We did not obtain the lock. Great, the queue is someone else's problem.
53 return;
54 }
55
56 // Resume the next item from the queue.
57 if (self.lock.queue.get()) |node| {
58 global_event_loop.onNextTick(node);
59 return;
60 }
6134
62 // Release the lock again.35 pub fn initLocked() Lock {
63 @atomicStore(bool, &self.lock.queue_empty, true, .SeqCst);36 return Lock{ .head = LOCKED };
64 @atomicStore(bool, &self.lock.shared, false, .SeqCst);37 }
6538
66 // Find out if we can be done.39 pub fn acquire(self: *Lock) Held {
67 if (@atomicLoad(bool, &self.lock.queue_empty, .SeqCst)) {40 const held = self.mutex.acquire();
68 return;41
69 }42 // self.head transitions from multiple stages depending on the value:
70 }43 // UNLOCKED -> LOCKED:
44 // acquire Lock ownership when theres no waiters
45 // LOCKED -> <Waiter head ptr>:
46 // Lock is already owned, enqueue first Waiter
47 // <head ptr> -> <head ptr>:
48 // Lock is owned with pending waiters. Push our waiter to the queue.
49
50 if (self.head == UNLOCKED) {
51 self.head = LOCKED;
52 held.release();
53 return Held{ .lock = self };
71 }54 }
72 };
7355
74 pub fn init() Lock {56 var waiter: Waiter = undefined;
75 return Lock{57 waiter.next = null;
76 .shared = false,58 waiter.tail = &waiter;
77 .queue = Queue.init(),
78 .queue_empty = true,
79 };
80 }
8159
82 pub fn initLocked() Lock {60 const head = switch (self.head) {
83 return Lock{61 UNLOCKED => unreachable,
84 .shared = true,62 LOCKED => null,
85 .queue = Queue.init(),63 else => @intToPtr(*Waiter, self.head),
86 .queue_empty = true,
87 };64 };
88 }
8965
90 /// Must be called when not locked. Not thread safe.66 if (head) |h| {
91 /// All calls to acquire() and release() must complete before calling deinit().67 h.tail.next = &waiter;
92 pub fn deinit(self: *Lock) void {68 h.tail = &waiter;
93 assert(!self.shared);69 } else {
94 while (self.queue.get()) |node| resume node.data;70 self.head = @ptrToInt(&waiter);
95 }71 }
96
97 pub fn acquire(self: *Lock) callconv(.Async) Held {
98 var my_tick_node = Loop.NextTickNode.init(@frame());
9972
100 errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire
101 suspend {73 suspend {
102 self.queue.put(&my_tick_node);74 waiter.node = Loop.NextTickNode{
75 .prev = undefined,
76 .next = undefined,
77 .data = @frame(),
78 };
79 held.release();
80 }
10381
104 // At this point, we are in the queue, so we might have already been resumed.82 return Held{ .lock = self };
83 }
10584
106 // We set this bit so that later we can rely on the fact, that if queue_empty == true, some actor85 pub const Held = struct {
107 // will attempt to grab the lock.86 lock: *Lock,
108 @atomicStore(bool, &self.queue_empty, false, .SeqCst);
10987
110 if (!@atomicRmw(bool, &self.shared, .Xchg, true, .SeqCst)) {88 pub fn release(self: Held) void {
111 if (self.queue.get()) |node| {89 const waiter = blk: {
112 // Whether this node is us or someone else, we tail resume it.90 const held = self.lock.mutex.acquire();
113 resume node.data;91 defer held.release();
92
93 // self.head goes through the reverse transition from acquire():
94 // <head ptr> -> <new head ptr>:
95 // pop a waiter from the queue to give Lock ownership when theres still others pending
96 // <head ptr> -> LOCKED:
97 // pop the laster waiter from the queue, while also giving it lock ownership when awaken
98 // LOCKED -> UNLOCKED:
99 // last lock owner releases lock while no one else is waiting for it
100
101 switch (self.lock.head) {
102 UNLOCKED => {
103 unreachable; // Lock unlocked while unlocking
104 },
105 LOCKED => {
106 self.lock.head = UNLOCKED;
107 break :blk null;
108 },
109 else => {
110 const waiter = @intToPtr(*Waiter, self.lock.head);
111 self.lock.head = if (waiter.next == null) LOCKED else @ptrToInt(waiter.next);
112 if (waiter.next) |next|
113 next.tail = waiter.tail;
114 break :blk waiter;
115 },
114 }116 }
117 };
118
119 if (waiter) |w| {
120 global_event_loop.onNextTick(&w.node);
115 }121 }
116 }122 }
117123 };
118 return Held{ .lock = self };
119 }
120};124};
121125
122test "std.event.Lock" {126test "std.event.Lock" {
...@@ -128,41 +132,16 @@ test "std.event.Lock" {...@@ -128,41 +132,16 @@ test "std.event.Lock" {
128 // TODO https://github.com/ziglang/zig/issues/3251132 // TODO https://github.com/ziglang/zig/issues/3251
129 if (builtin.os.tag == .freebsd) return error.SkipZigTest;133 if (builtin.os.tag == .freebsd) return error.SkipZigTest;
130134
131 // TODO this file has bit-rotted. repair it135 var lock = Lock{};
132 if (true) return error.SkipZigTest;136 testLock(&lock);
133
134 var lock = Lock.init();
135 defer lock.deinit();
136
137 _ = async testLock(&lock);
138137
139 const expected_result = [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len;138 const expected_result = [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len;
140 testing.expectEqualSlices(i32, &expected_result, &shared_test_data);139 testing.expectEqualSlices(i32, &expected_result, &shared_test_data);
141}140}
142fn testLock(lock: *Lock) callconv(.Async) void {141fn testLock(lock: *Lock) void {
143 var handle1 = async lockRunner(lock);142 var handle1 = async lockRunner(lock);
144 var tick_node1 = Loop.NextTickNode{
145 .prev = undefined,
146 .next = undefined,
147 .data = &handle1,
148 };
149 Loop.instance.?.onNextTick(&tick_node1);
150
151 var handle2 = async lockRunner(lock);143 var handle2 = async lockRunner(lock);
152 var tick_node2 = Loop.NextTickNode{
153 .prev = undefined,
154 .next = undefined,
155 .data = &handle2,
156 };
157 Loop.instance.?.onNextTick(&tick_node2);
158
159 var handle3 = async lockRunner(lock);144 var handle3 = async lockRunner(lock);
160 var tick_node3 = Loop.NextTickNode{
161 .prev = undefined,
162 .next = undefined,
163 .data = &handle3,
164 };
165 Loop.instance.?.onNextTick(&tick_node3);
166145
167 await handle1;146 await handle1;
168 await handle2;147 await handle2;
...@@ -171,13 +150,13 @@ fn testLock(lock: *Lock) callconv(.Async) void {...@@ -171,13 +150,13 @@ fn testLock(lock: *Lock) callconv(.Async) void {
171150
172var shared_test_data = [1]i32{0} ** 10;151var shared_test_data = [1]i32{0} ** 10;
173var shared_test_index: usize = 0;152var shared_test_index: usize = 0;
174fn lockRunner(lock: *Lock) callconv(.Async) void {153
175 suspend; // resumed by onNextTick154fn lockRunner(lock: *Lock) void {
155 Lock.global_event_loop.yield();
176156
177 var i: usize = 0;157 var i: usize = 0;
178 while (i < shared_test_data.len) : (i += 1) {158 while (i < shared_test_data.len) : (i += 1) {
179 var lock_frame = async lock.acquire();159 const handle = lock.acquire();
180 const handle = await lock_frame;
181 defer handle.release();160 defer handle.release();
182161
183 shared_test_index = 0;162 shared_test_index = 0;
lib/std/event/loop.zig+315-101
...@@ -721,6 +721,50 @@ pub const Loop = struct {...@@ -721,6 +721,50 @@ pub const Loop = struct {
721 }721 }
722 }722 }
723723
724 /// ------- I/0 APIs -------
725 pub fn accept(
726 self: *Loop,
727 /// This argument is a socket that has been created with `socket`, bound to a local address
728 /// with `bind`, and is listening for connections after a `listen`.
729 sockfd: os.fd_t,
730 /// This argument is a pointer to a sockaddr structure. This structure is filled in with the
731 /// address of the peer socket, as known to the communications layer. The exact format of the
732 /// address returned addr is determined by the socket's address family (see `socket` and the
733 /// respective protocol man pages).
734 addr: *os.sockaddr,
735 /// This argument is a value-result argument: the caller must initialize it to contain the
736 /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size
737 /// of the peer address.
738 ///
739 /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size`
740 /// will return a value greater than was supplied to the call.
741 addr_size: *os.socklen_t,
742 /// The following values can be bitwise ORed in flags to obtain different behavior:
743 /// * `SOCK_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the
744 /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.
745 flags: u32,
746 ) os.AcceptError!os.fd_t {
747 while (true) {
748 return os.accept(sockfd, addr, addr_size, flags | os.SOCK_NONBLOCK) catch |err| switch (err) {
749 error.WouldBlock => {
750 self.waitUntilFdReadable(sockfd);
751 continue;
752 },
753 else => return err,
754 };
755 }
756 }
757
758 pub fn connect(self: *Loop, sockfd: os.socket_t, sock_addr: *const os.sockaddr, len: os.socklen_t) os.ConnectError!void {
759 os.connect(sockfd, sock_addr, len) catch |err| switch (err) {
760 error.WouldBlock => {
761 self.waitUntilFdWritable(sockfd);
762 return os.getsockoptError(sockfd);
763 },
764 else => return err,
765 };
766 }
767
724 /// Performs an async `os.open` using a separate thread.768 /// Performs an async `os.open` using a separate thread.
725 pub fn openZ(self: *Loop, file_path: [*:0]const u8, flags: u32, mode: os.mode_t) os.OpenError!os.fd_t {769 pub fn openZ(self: *Loop, file_path: [*:0]const u8, flags: u32, mode: os.mode_t) os.OpenError!os.fd_t {
726 var req_node = Request.Node{770 var req_node = Request.Node{
...@@ -779,152 +823,309 @@ pub const Loop = struct {...@@ -779,152 +823,309 @@ pub const Loop = struct {
779823
780 /// Performs an async `os.read` using a separate thread.824 /// Performs an async `os.read` using a separate thread.
781 /// `fd` must block and not return EAGAIN.825 /// `fd` must block and not return EAGAIN.
782 pub fn read(self: *Loop, fd: os.fd_t, buf: []u8) os.ReadError!usize {826 pub fn read(self: *Loop, fd: os.fd_t, buf: []u8, simulate_evented: bool) os.ReadError!usize {
783 var req_node = Request.Node{827 if (simulate_evented) {
784 .data = .{828 var req_node = Request.Node{
785 .msg = .{829 .data = .{
786 .read = .{830 .msg = .{
787 .fd = fd,831 .read = .{
788 .buf = buf,832 .fd = fd,
789 .result = undefined,833 .buf = buf,
834 .result = undefined,
835 },
790 },836 },
837 .finish = .{ .TickNode = .{ .data = @frame() } },
791 },838 },
792 .finish = .{ .TickNode = .{ .data = @frame() } },839 };
793 },840 suspend {
794 };841 self.posixFsRequest(&req_node);
795 suspend {842 }
796 self.posixFsRequest(&req_node);843 return req_node.data.msg.read.result;
844 } else {
845 while (true) {
846 return os.read(fd, buf) catch |err| switch (err) {
847 error.WouldBlock => {
848 self.waitUntilFdReadable(fd);
849 continue;
850 },
851 else => return err,
852 };
853 }
797 }854 }
798 return req_node.data.msg.read.result;
799 }855 }
800856
801 /// Performs an async `os.readv` using a separate thread.857 /// Performs an async `os.readv` using a separate thread.
802 /// `fd` must block and not return EAGAIN.858 /// `fd` must block and not return EAGAIN.
803 pub fn readv(self: *Loop, fd: os.fd_t, iov: []const os.iovec) os.ReadError!usize {859 pub fn readv(self: *Loop, fd: os.fd_t, iov: []const os.iovec, simulate_evented: bool) os.ReadError!usize {
804 var req_node = Request.Node{860 if (simulate_evented) {
805 .data = .{861 var req_node = Request.Node{
806 .msg = .{862 .data = .{
807 .readv = .{863 .msg = .{
808 .fd = fd,864 .readv = .{
809 .iov = iov,865 .fd = fd,
810 .result = undefined,866 .iov = iov,
867 .result = undefined,
868 },
811 },869 },
870 .finish = .{ .TickNode = .{ .data = @frame() } },
812 },871 },
813 .finish = .{ .TickNode = .{ .data = @frame() } },872 };
814 },873 suspend {
815 };874 self.posixFsRequest(&req_node);
816 suspend {875 }
817 self.posixFsRequest(&req_node);876 return req_node.data.msg.readv.result;
877 } else {
878 while (true) {
879 return os.readv(fd, iov) catch |err| switch (err) {
880 error.WouldBlock => {
881 self.waitUntilFdReadable(fd);
882 continue;
883 },
884 else => return err,
885 };
886 }
818 }887 }
819 return req_node.data.msg.readv.result;
820 }888 }
821889
822 /// Performs an async `os.pread` using a separate thread.890 /// Performs an async `os.pread` using a separate thread.
823 /// `fd` must block and not return EAGAIN.891 /// `fd` must block and not return EAGAIN.
824 pub fn pread(self: *Loop, fd: os.fd_t, buf: []u8, offset: u64) os.PReadError!usize {892 pub fn pread(self: *Loop, fd: os.fd_t, buf: []u8, offset: u64, simulate_evented: bool) os.PReadError!usize {
825 var req_node = Request.Node{893 if (simulate_evented) {
826 .data = .{894 var req_node = Request.Node{
827 .msg = .{895 .data = .{
828 .pread = .{896 .msg = .{
829 .fd = fd,897 .pread = .{
830 .buf = buf,898 .fd = fd,
831 .offset = offset,899 .buf = buf,
832 .result = undefined,900 .offset = offset,
901 .result = undefined,
902 },
833 },903 },
904 .finish = .{ .TickNode = .{ .data = @frame() } },
834 },905 },
835 .finish = .{ .TickNode = .{ .data = @frame() } },906 };
836 },907 suspend {
837 };908 self.posixFsRequest(&req_node);
838 suspend {909 }
839 self.posixFsRequest(&req_node);910 return req_node.data.msg.pread.result;
911 } else {
912 while (true) {
913 return os.pread(fd, buf, offset) catch |err| switch (err) {
914 error.WouldBlock => {
915 self.waitUntilFdReadable(fd);
916 continue;
917 },
918 else => return err,
919 };
920 }
840 }921 }
841 return req_node.data.msg.pread.result;
842 }922 }
843923
844 /// Performs an async `os.preadv` using a separate thread.924 /// Performs an async `os.preadv` using a separate thread.
845 /// `fd` must block and not return EAGAIN.925 /// `fd` must block and not return EAGAIN.
846 pub fn preadv(self: *Loop, fd: os.fd_t, iov: []const os.iovec, offset: u64) os.ReadError!usize {926 pub fn preadv(self: *Loop, fd: os.fd_t, iov: []const os.iovec, offset: u64, simulate_evented: bool) os.ReadError!usize {
847 var req_node = Request.Node{927 if (simulate_evented) {
848 .data = .{928 var req_node = Request.Node{
849 .msg = .{929 .data = .{
850 .preadv = .{930 .msg = .{
851 .fd = fd,931 .preadv = .{
852 .iov = iov,932 .fd = fd,
853 .offset = offset,933 .iov = iov,
854 .result = undefined,934 .offset = offset,
935 .result = undefined,
936 },
855 },937 },
938 .finish = .{ .TickNode = .{ .data = @frame() } },
856 },939 },
857 .finish = .{ .TickNode = .{ .data = @frame() } },940 };
858 },941 suspend {
859 };942 self.posixFsRequest(&req_node);
860 suspend {943 }
861 self.posixFsRequest(&req_node);944 return req_node.data.msg.preadv.result;
945 } else {
946 while (true) {
947 return os.preadv(fd, iov, offset) catch |err| switch (err) {
948 error.WouldBlock => {
949 self.waitUntilFdReadable(fd);
950 continue;
951 },
952 else => return err,
953 };
954 }
862 }955 }
863 return req_node.data.msg.preadv.result;
864 }956 }
865957
866 /// Performs an async `os.write` using a separate thread.958 /// Performs an async `os.write` using a separate thread.
867 /// `fd` must block and not return EAGAIN.959 /// `fd` must block and not return EAGAIN.
868 pub fn write(self: *Loop, fd: os.fd_t, bytes: []const u8) os.WriteError!usize {960 pub fn write(self: *Loop, fd: os.fd_t, bytes: []const u8, simulate_evented: bool) os.WriteError!usize {
869 var req_node = Request.Node{961 if (simulate_evented) {
870 .data = .{962 var req_node = Request.Node{
871 .msg = .{963 .data = .{
872 .write = .{964 .msg = .{
873 .fd = fd,965 .write = .{
874 .bytes = bytes,966 .fd = fd,
875 .result = undefined,967 .bytes = bytes,
968 .result = undefined,
969 },
876 },970 },
971 .finish = .{ .TickNode = .{ .data = @frame() } },
877 },972 },
878 .finish = .{ .TickNode = .{ .data = @frame() } },973 };
879 },974 suspend {
880 };975 self.posixFsRequest(&req_node);
881 suspend {976 }
882 self.posixFsRequest(&req_node);977 return req_node.data.msg.write.result;
978 } else {
979 while (true) {
980 return os.write(fd, bytes) catch |err| switch (err) {
981 error.WouldBlock => {
982 self.waitUntilFdWritable(fd);
983 continue;
984 },
985 else => return err,
986 };
987 }
883 }988 }
884 return req_node.data.msg.write.result;
885 }989 }
886990
887 /// Performs an async `os.writev` using a separate thread.991 /// Performs an async `os.writev` using a separate thread.
888 /// `fd` must block and not return EAGAIN.992 /// `fd` must block and not return EAGAIN.
889 pub fn writev(self: *Loop, fd: os.fd_t, iov: []const os.iovec_const) os.WriteError!usize {993 pub fn writev(self: *Loop, fd: os.fd_t, iov: []const os.iovec_const, simulate_evented: bool) os.WriteError!usize {
890 var req_node = Request.Node{994 if (simulate_evented) {
891 .data = .{995 var req_node = Request.Node{
892 .msg = .{996 .data = .{
893 .writev = .{997 .msg = .{
894 .fd = fd,998 .writev = .{
895 .iov = iov,999 .fd = fd,
896 .result = undefined,1000 .iov = iov,
1001 .result = undefined,
1002 },
897 },1003 },
1004 .finish = .{ .TickNode = .{ .data = @frame() } },
898 },1005 },
899 .finish = .{ .TickNode = .{ .data = @frame() } },1006 };
900 },1007 suspend {
901 };1008 self.posixFsRequest(&req_node);
902 suspend {1009 }
903 self.posixFsRequest(&req_node);1010 return req_node.data.msg.writev.result;
1011 } else {
1012 while (true) {
1013 return os.writev(fd, iov) catch |err| switch (err) {
1014 error.WouldBlock => {
1015 self.waitUntilFdWritable(fd);
1016 continue;
1017 },
1018 else => return err,
1019 };
1020 }
1021 }
1022 }
1023
1024 /// Performs an async `os.pwrite` using a separate thread.
1025 /// `fd` must block and not return EAGAIN.
1026 pub fn pwrite(self: *Loop, fd: os.fd_t, bytes: []const u8, offset: u64, simulate_evented: bool) os.PerformsWriteError!usize {
1027 if (simulate_evented) {
1028 var req_node = Request.Node{
1029 .data = .{
1030 .msg = .{
1031 .pwrite = .{
1032 .fd = fd,
1033 .bytes = bytes,
1034 .offset = offset,
1035 .result = undefined,
1036 },
1037 },
1038 .finish = .{ .TickNode = .{ .data = @frame() } },
1039 },
1040 };
1041 suspend {
1042 self.posixFsRequest(&req_node);
1043 }
1044 return req_node.data.msg.pwrite.result;
1045 } else {
1046 while (true) {
1047 return os.pwrite(fd, bytes, offset) catch |err| switch (err) {
1048 error.WouldBlock => {
1049 self.waitUntilFdWritable(fd);
1050 continue;
1051 },
1052 else => return err,
1053 };
1054 }
904 }1055 }
905 return req_node.data.msg.writev.result;
906 }1056 }
9071057
908 /// Performs an async `os.pwritev` using a separate thread.1058 /// Performs an async `os.pwritev` using a separate thread.
909 /// `fd` must block and not return EAGAIN.1059 /// `fd` must block and not return EAGAIN.
910 pub fn pwritev(self: *Loop, fd: os.fd_t, iov: []const os.iovec_const, offset: u64) os.WriteError!usize {1060 pub fn pwritev(self: *Loop, fd: os.fd_t, iov: []const os.iovec_const, offset: u64, simulate_evented: bool) os.PWriteError!usize {
911 var req_node = Request.Node{1061 if (simulate_evented) {
912 .data = .{1062 var req_node = Request.Node{
913 .msg = .{1063 .data = .{
914 .pwritev = .{1064 .msg = .{
915 .fd = fd,1065 .pwritev = .{
916 .iov = iov,1066 .fd = fd,
917 .offset = offset,1067 .iov = iov,
918 .result = undefined,1068 .offset = offset,
1069 .result = undefined,
1070 },
919 },1071 },
1072 .finish = .{ .TickNode = .{ .data = @frame() } },
920 },1073 },
921 .finish = .{ .TickNode = .{ .data = @frame() } },1074 };
922 },1075 suspend {
923 };1076 self.posixFsRequest(&req_node);
924 suspend {1077 }
925 self.posixFsRequest(&req_node);1078 return req_node.data.msg.pwritev.result;
1079 } else {
1080 while (true) {
1081 return os.pwritev(fd, iov, offset) catch |err| switch (err) {
1082 error.WouldBlock => {
1083 self.waitUntilFdWritable(fd);
1084 continue;
1085 },
1086 else => return err,
1087 };
1088 }
1089 }
1090 }
1091
1092 pub fn sendto(
1093 self: *Loop,
1094 /// The file descriptor of the sending socket.
1095 sockfd: os.fd_t,
1096 /// Message to send.
1097 buf: []const u8,
1098 flags: u32,
1099 dest_addr: ?*const os.sockaddr,
1100 addrlen: os.socklen_t,
1101 ) os.SendError!usize {
1102 while (true) {
1103 return os.sendto(sockfd, buf, flags, dest_addr, addrlen) catch |err| switch (err) {
1104 error.WouldBlock => {
1105 self.waitUntilFdWritable(sockfd);
1106 continue;
1107 },
1108 else => return err,
1109 };
1110 }
1111 }
1112
1113 pub fn recvfrom(
1114 sockfd: os.fd_t,
1115 buf: []u8,
1116 flags: u32,
1117 src_addr: ?*os.sockaddr,
1118 addrlen: ?*os.socklen_t,
1119 ) os.RecvFromError!usize {
1120 while (true) {
1121 return os.recvfrom(sockfd, buf, flags, src_addr, addrlen) catch |err| switch (err) {
1122 error.WouldBlock => {
1123 self.waitUntilFdReadable(sockfd);
1124 continue;
1125 },
1126 else => return err,
1127 };
926 }1128 }
927 return req_node.data.msg.pwritev.result;
928 }1129 }
9291130
930 /// Performs an async `os.faccessatZ` using a separate thread.1131 /// Performs an async `os.faccessatZ` using a separate thread.
...@@ -1079,6 +1280,9 @@ pub const Loop = struct {...@@ -1079,6 +1280,9 @@ pub const Loop = struct {
1079 .writev => |*msg| {1280 .writev => |*msg| {
1080 msg.result = os.writev(msg.fd, msg.iov);1281 msg.result = os.writev(msg.fd, msg.iov);
1081 },1282 },
1283 .pwrite => |*msg| {
1284 msg.result = os.pwrite(msg.fd, msg.bytes, msg.offset);
1285 },
1082 .pwritev => |*msg| {1286 .pwritev => |*msg| {
1083 msg.result = os.pwritev(msg.fd, msg.iov, msg.offset);1287 msg.result = os.pwritev(msg.fd, msg.iov, msg.offset);
1084 },1288 },
...@@ -1148,6 +1352,7 @@ pub const Loop = struct {...@@ -1148,6 +1352,7 @@ pub const Loop = struct {
1148 readv: ReadV,1352 readv: ReadV,
1149 write: Write,1353 write: Write,
1150 writev: WriteV,1354 writev: WriteV,
1355 pwrite: PWrite,
1151 pwritev: PWriteV,1356 pwritev: PWriteV,
1152 pread: PRead,1357 pread: PRead,
1153 preadv: PReadV,1358 preadv: PReadV,
...@@ -1191,6 +1396,15 @@ pub const Loop = struct {...@@ -1191,6 +1396,15 @@ pub const Loop = struct {
1191 pub const Error = os.WriteError;1396 pub const Error = os.WriteError;
1192 };1397 };
11931398
1399 pub const PWrite = struct {
1400 fd: os.fd_t,
1401 bytes: []const u8,
1402 offset: usize,
1403 result: Error!usize,
1404
1405 pub const Error = os.PWriteError;
1406 };
1407
1194 pub const PWriteV = struct {1408 pub const PWriteV = struct {
1195 fd: os.fd_t,1409 fd: os.fd_t,
1196 iov: []const os.iovec_const,1410 iov: []const os.iovec_const,
lib/std/fifo.zig+11-1
...@@ -186,7 +186,9 @@ pub fn LinearFifo(...@@ -186,7 +186,9 @@ pub fn LinearFifo(
186 } else {186 } else {
187 var head = self.head + count;187 var head = self.head + count;
188 if (powers_of_two) {188 if (powers_of_two) {
189 head &= self.buf.len - 1;189 // Note it is safe to do a wrapping subtract as
190 // bitwise & with all 1s is a noop
191 head &= self.buf.len -% 1;
190 } else {192 } else {
191 head %= self.buf.len;193 head %= self.buf.len;
192 }194 }
...@@ -376,6 +378,14 @@ pub fn LinearFifo(...@@ -376,6 +378,14 @@ pub fn LinearFifo(
376 };378 };
377}379}
378380
381test "LinearFifo(u8, .Dynamic) discard(0) from empty buffer should not error on overflow" {
382 var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator);
383 defer fifo.deinit();
384
385 // If overflow is not explicitly allowed this will crash in debug / safe mode
386 fifo.discard(0);
387}
388
379test "LinearFifo(u8, .Dynamic)" {389test "LinearFifo(u8, .Dynamic)" {
380 var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator);390 var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator);
381 defer fifo.deinit();391 defer fifo.deinit();
lib/std/fs/file.zig+40-24
...@@ -414,10 +414,12 @@ pub const File = struct {...@@ -414,10 +414,12 @@ pub const File = struct {
414 pub fn read(self: File, buffer: []u8) ReadError!usize {414 pub fn read(self: File, buffer: []u8) ReadError!usize {
415 if (is_windows) {415 if (is_windows) {
416 return windows.ReadFile(self.handle, buffer, null, self.intended_io_mode);416 return windows.ReadFile(self.handle, buffer, null, self.intended_io_mode);
417 } else if (self.capable_io_mode != self.intended_io_mode) {417 }
418 return std.event.Loop.instance.?.read(self.handle, buffer);418
419 } else {419 if (self.intended_io_mode == .blocking) {
420 return os.read(self.handle, buffer);420 return os.read(self.handle, buffer);
421 } else {
422 return std.event.Loop.instance.?.read(self.handle, buffer, self.capable_io_mode != self.intended_io_mode);
421 }423 }
422 }424 }
423425
...@@ -436,10 +438,12 @@ pub const File = struct {...@@ -436,10 +438,12 @@ pub const File = struct {
436 pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {438 pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
437 if (is_windows) {439 if (is_windows) {
438 return windows.ReadFile(self.handle, buffer, offset, self.intended_io_mode);440 return windows.ReadFile(self.handle, buffer, offset, self.intended_io_mode);
439 } else if (self.capable_io_mode != self.intended_io_mode) {441 }
440 return std.event.Loop.instance.?.pread(self.handle, buffer, offset);442
441 } else {443 if (self.intended_io_mode == .blocking) {
442 return os.pread(self.handle, buffer, offset);444 return os.pread(self.handle, buffer, offset);
445 } else {
446 return std.event.Loop.instance.?.pread(self.handle, buffer, offset, self.capable_io_mode != self.intended_io_mode);
443 }447 }
444 }448 }
445449
...@@ -461,10 +465,12 @@ pub const File = struct {...@@ -461,10 +465,12 @@ pub const File = struct {
461 if (iovecs.len == 0) return @as(usize, 0);465 if (iovecs.len == 0) return @as(usize, 0);
462 const first = iovecs[0];466 const first = iovecs[0];
463 return windows.ReadFile(self.handle, first.iov_base[0..first.iov_len], null, self.intended_io_mode);467 return windows.ReadFile(self.handle, first.iov_base[0..first.iov_len], null, self.intended_io_mode);
464 } else if (self.capable_io_mode != self.intended_io_mode) {468 }
465 return std.event.Loop.instance.?.readv(self.handle, iovecs);469
466 } else {470 if (self.intended_io_mode == .blocking) {
467 return os.readv(self.handle, iovecs);471 return os.readv(self.handle, iovecs);
472 } else {
473 return std.event.Loop.instance.?.readv(self.handle, iovecs, self.capable_io_mode != self.intended_io_mode);
468 }474 }
469 }475 }
470476
...@@ -500,10 +506,12 @@ pub const File = struct {...@@ -500,10 +506,12 @@ pub const File = struct {
500 if (iovecs.len == 0) return @as(usize, 0);506 if (iovecs.len == 0) return @as(usize, 0);
501 const first = iovecs[0];507 const first = iovecs[0];
502 return windows.ReadFile(self.handle, first.iov_base[0..first.iov_len], offset, self.intended_io_mode);508 return windows.ReadFile(self.handle, first.iov_base[0..first.iov_len], offset, self.intended_io_mode);
503 } else if (self.capable_io_mode != self.intended_io_mode) {509 }
504 return std.event.Loop.instance.?.preadv(self.handle, iovecs, offset);510
505 } else {511 if (self.intended_io_mode == .blocking) {
506 return os.preadv(self.handle, iovecs, offset);512 return os.preadv(self.handle, iovecs, offset);
513 } else {
514 return std.event.Loop.instance.?.preadv(self.handle, iovecs, offset, self.capable_io_mode != self.intended_io_mode);
507 }515 }
508 }516 }
509517
...@@ -539,10 +547,12 @@ pub const File = struct {...@@ -539,10 +547,12 @@ pub const File = struct {
539 pub fn write(self: File, bytes: []const u8) WriteError!usize {547 pub fn write(self: File, bytes: []const u8) WriteError!usize {
540 if (is_windows) {548 if (is_windows) {
541 return windows.WriteFile(self.handle, bytes, null, self.intended_io_mode);549 return windows.WriteFile(self.handle, bytes, null, self.intended_io_mode);
542 } else if (self.capable_io_mode != self.intended_io_mode) {550 }
543 return std.event.Loop.instance.?.write(self.handle, bytes);551
544 } else {552 if (self.intended_io_mode == .blocking) {
545 return os.write(self.handle, bytes);553 return os.write(self.handle, bytes);
554 } else {
555 return std.event.Loop.instance.?.write(self.handle, bytes, self.capable_io_mode != self.intended_io_mode);
546 }556 }
547 }557 }
548558
...@@ -556,10 +566,12 @@ pub const File = struct {...@@ -556,10 +566,12 @@ pub const File = struct {
556 pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize {566 pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize {
557 if (is_windows) {567 if (is_windows) {
558 return windows.WriteFile(self.handle, bytes, offset, self.intended_io_mode);568 return windows.WriteFile(self.handle, bytes, offset, self.intended_io_mode);
559 } else if (self.capable_io_mode != self.intended_io_mode) {569 }
560 return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset);570
561 } else {571 if (self.intended_io_mode == .blocking) {
562 return os.pwrite(self.handle, bytes, offset);572 return os.pwrite(self.handle, bytes, offset);
573 } else {
574 return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset, self.capable_io_mode != self.intended_io_mode);
563 }575 }
564 }576 }
565577
...@@ -576,10 +588,12 @@ pub const File = struct {...@@ -576,10 +588,12 @@ pub const File = struct {
576 if (iovecs.len == 0) return @as(usize, 0);588 if (iovecs.len == 0) return @as(usize, 0);
577 const first = iovecs[0];589 const first = iovecs[0];
578 return windows.WriteFile(self.handle, first.iov_base[0..first.iov_len], null, self.intended_io_mode);590 return windows.WriteFile(self.handle, first.iov_base[0..first.iov_len], null, self.intended_io_mode);
579 } else if (self.capable_io_mode != self.intended_io_mode) {591 }
580 return std.event.Loop.instance.?.writev(self.handle, iovecs);592
581 } else {593 if (self.intended_io_mode == .blocking) {
582 return os.writev(self.handle, iovecs);594 return os.writev(self.handle, iovecs);
595 } else {
596 return std.event.Loop.instance.?.writev(self.handle, iovecs, self.capable_io_mode != self.intended_io_mode);
583 }597 }
584 }598 }
585599
...@@ -607,10 +621,12 @@ pub const File = struct {...@@ -607,10 +621,12 @@ pub const File = struct {
607 if (iovecs.len == 0) return @as(usize, 0);621 if (iovecs.len == 0) return @as(usize, 0);
608 const first = iovecs[0];622 const first = iovecs[0];
609 return windows.WriteFile(self.handle, first.iov_base[0..first.iov_len], offset, self.intended_io_mode);623 return windows.WriteFile(self.handle, first.iov_base[0..first.iov_len], offset, self.intended_io_mode);
610 } else if (self.capable_io_mode != self.intended_io_mode) {624 }
611 return std.event.Loop.instance.?.pwritev(self.handle, iovecs, offset);625
612 } else {626 if (self.intended_io_mode == .blocking) {
613 return os.pwritev(self.handle, iovecs, offset);627 return os.pwritev(self.handle, iovecs, offset);
628 } else {
629 return std.event.Loop.instance.?.pwritev(self.handle, iovecs, offset, self.capable_io_mode != self.intended_io_mode);
614 }630 }
615 }631 }
616632
lib/std/fs/test.zig+23
...@@ -813,3 +813,26 @@ fn run_lock_file_test(contexts: []FileLockTestContext) !void {...@@ -813,3 +813,26 @@ fn run_lock_file_test(contexts: []FileLockTestContext) !void {
813 try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run));813 try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run));
814 }814 }
815}815}
816
817test "deleteDir" {
818 var tmp_dir = tmpDir(.{});
819 defer tmp_dir.cleanup();
820
821 // deleting a non-existent directory
822 testing.expectError(error.FileNotFound, tmp_dir.dir.deleteDir("test_dir"));
823
824 var dir = try tmp_dir.dir.makeOpenPath("test_dir", .{});
825 var file = try dir.createFile("test_file", .{});
826 file.close();
827 dir.close();
828
829 // deleting a non-empty directory
830 testing.expectError(error.DirNotEmpty, tmp_dir.dir.deleteDir("test_dir"));
831
832 dir = try tmp_dir.dir.openDir("test_dir", .{});
833 try dir.deleteFile("test_file");
834 dir.close();
835
836 // deleting an empty directory
837 try tmp_dir.dir.deleteDir("test_dir");
838}
lib/std/heap.zig+7
...@@ -919,6 +919,13 @@ pub fn testAllocator(base_allocator: *mem.Allocator) !void {...@@ -919,6 +919,13 @@ pub fn testAllocator(base_allocator: *mem.Allocator) !void {
919 const zero_bit_ptr = try allocator.create(u0);919 const zero_bit_ptr = try allocator.create(u0);
920 zero_bit_ptr.* = 0;920 zero_bit_ptr.* = 0;
921 allocator.destroy(zero_bit_ptr);921 allocator.destroy(zero_bit_ptr);
922
923 const oversize = try allocator.allocAdvanced(u32, null, 5, .at_least);
924 testing.expect(oversize.len >= 5);
925 for (oversize) |*item| {
926 item.* = 0xDEADBEEF;
927 }
928 allocator.free(oversize);
922}929}
923930
924pub fn testAllocatorAligned(base_allocator: *mem.Allocator, comptime alignment: u29) !void {931pub fn testAllocatorAligned(base_allocator: *mem.Allocator, comptime alignment: u29) !void {
lib/std/heap/arena_allocator.zig+15-6
...@@ -75,13 +75,22 @@ pub const ArenaAllocator = struct {...@@ -75,13 +75,22 @@ pub const ArenaAllocator = struct {
75 const adjusted_addr = mem.alignForward(addr, ptr_align);75 const adjusted_addr = mem.alignForward(addr, ptr_align);
76 const adjusted_index = self.state.end_index + (adjusted_addr - addr);76 const adjusted_index = self.state.end_index + (adjusted_addr - addr);
77 const new_end_index = adjusted_index + n;77 const new_end_index = adjusted_index + n;
78 if (new_end_index > cur_buf.len) {78
79 cur_node = try self.createNode(cur_buf.len, n + ptr_align);79 if (new_end_index <= cur_buf.len) {
80 continue;80 const result = cur_buf[adjusted_index..new_end_index];
81 self.state.end_index = new_end_index;
82 return result;
81 }83 }
82 const result = cur_buf[adjusted_index..new_end_index];84
83 self.state.end_index = new_end_index;85 const bigger_buf_size = @sizeOf(BufNode) + new_end_index;
84 return result;86 // Try to grow the buffer in-place
87 cur_node.data = self.child_allocator.resize(cur_node.data, bigger_buf_size) catch |err| switch (err) {
88 error.OutOfMemory => {
89 // Allocate a new node if that's not possible
90 cur_node = try self.createNode(cur_buf.len, n + ptr_align);
91 continue;
92 },
93 };
85 }94 }
86 }95 }
8796
lib/std/meta.zig+61-24
...@@ -867,34 +867,71 @@ pub fn ArgsTuple(comptime Function: type) type {...@@ -867,34 +867,71 @@ pub fn ArgsTuple(comptime Function: type) type {
867 });867 });
868}868}
869869
870test "ArgsTuple" {870/// For a given anonymous list of types, returns a new tuple type
871 const T = struct {871/// with those types as fields.
872 fn assertTypeEqual(comptime Expected: type, comptime Actual: type) void {872///
873 if (Expected != Actual)873/// Examples:
874 @compileError("Expected type " ++ @typeName(Expected) ++ ", but got type " ++ @typeName(Actual));874/// - `Tuple(&[_]type {})` ⇒ `tuple { }`
875 }875/// - `Tuple(&[_]type {f32})` ⇒ `tuple { f32 }`
876/// - `Tuple(&[_]type {f32,u32})` ⇒ `tuple { f32, u32 }`
877pub fn Tuple(comptime types: []const type) type {
878 var tuple_fields: [types.len]std.builtin.TypeInfo.StructField = undefined;
879 inline for (types) |T, i| {
880 @setEvalBranchQuota(10_000);
881 var num_buf: [128]u8 = undefined;
882 tuple_fields[i] = std.builtin.TypeInfo.StructField{
883 .name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable,
884 .field_type = T,
885 .default_value = @as(?T, null),
886 .is_comptime = false,
887 };
888 }
876889
877 fn assertTuple(comptime expected: anytype, comptime Actual: type) void {890 return @Type(std.builtin.TypeInfo{
878 const info = @typeInfo(Actual);891 .Struct = std.builtin.TypeInfo.Struct{
879 if (info != .Struct)892 .is_tuple = true,
880 @compileError("Expected struct type");893 .layout = .Auto,
881 if (!info.Struct.is_tuple)894 .decls = &[_]std.builtin.TypeInfo.Declaration{},
882 @compileError("Struct type must be a tuple type");895 .fields = &tuple_fields,
896 },
897 });
898}
883899
884 const fields_list = std.meta.fields(Actual);900const TupleTester = struct {
885 if (expected.len != fields_list.len)901 fn assertTypeEqual(comptime Expected: type, comptime Actual: type) void {
886 @compileError("Argument count mismatch");902 if (Expected != Actual)
903 @compileError("Expected type " ++ @typeName(Expected) ++ ", but got type " ++ @typeName(Actual));
904 }
887905
888 inline for (fields_list) |fld, i| {906 fn assertTuple(comptime expected: anytype, comptime Actual: type) void {
889 if (expected[i] != fld.field_type) {907 const info = @typeInfo(Actual);
890 @compileError("Field " ++ fld.name ++ " expected to be type " ++ @typeName(expected[i]) ++ ", but was type " ++ @typeName(fld.field_type));908 if (info != .Struct)
891 }909 @compileError("Expected struct type");
910 if (!info.Struct.is_tuple)
911 @compileError("Struct type must be a tuple type");
912
913 const fields_list = std.meta.fields(Actual);
914 if (expected.len != fields_list.len)
915 @compileError("Argument count mismatch");
916
917 inline for (fields_list) |fld, i| {
918 if (expected[i] != fld.field_type) {
919 @compileError("Field " ++ fld.name ++ " expected to be type " ++ @typeName(expected[i]) ++ ", but was type " ++ @typeName(fld.field_type));
892 }920 }
893 }921 }
894 };922 }
923};
924
925test "ArgsTuple" {
926 TupleTester.assertTuple(.{}, ArgsTuple(fn () void));
927 TupleTester.assertTuple(.{u32}, ArgsTuple(fn (a: u32) []const u8));
928 TupleTester.assertTuple(.{ u32, f16 }, ArgsTuple(fn (a: u32, b: f16) noreturn));
929 TupleTester.assertTuple(.{ u32, f16, []const u8 }, ArgsTuple(fn (a: u32, b: f16, c: []const u8) noreturn));
930}
895931
896 T.assertTuple(.{}, ArgsTuple(fn () void));932test "Tuple" {
897 T.assertTuple(.{u32}, ArgsTuple(fn (a: u32) []const u8));933 TupleTester.assertTuple(.{}, Tuple(&[_]type{}));
898 T.assertTuple(.{ u32, f16 }, ArgsTuple(fn (a: u32, b: f16) noreturn));934 TupleTester.assertTuple(.{u32}, Tuple(&[_]type{u32}));
899 T.assertTuple(.{ u32, f16, []const u8 }, ArgsTuple(fn (a: u32, b: f16, c: []const u8) noreturn));935 TupleTester.assertTuple(.{ u32, f16 }, Tuple(&[_]type{ u32, f16 }));
936 TupleTester.assertTuple(.{ u32, f16, []const u8 }, Tuple(&[_]type{ u32, f16, []const u8 }));
900}937}
lib/std/net.zig+36-14
...@@ -614,11 +614,11 @@ pub fn connectUnixSocket(path: []const u8) !fs.File {...@@ -614,11 +614,11 @@ pub fn connectUnixSocket(path: []const u8) !fs.File {
614614
615 var addr = try std.net.Address.initUnix(path);615 var addr = try std.net.Address.initUnix(path);
616616
617 try os.connect(617 if (std.io.is_async) {
618 sockfd,618 try loop.connect(sockfd, &addr.any, addr.getOsSockLen());
619 &addr.any,619 } else {
620 addr.getOsSockLen(),620 try os.connect(sockfd, &addr.any, addr.getOsSockLen());
621 );621 }
622622
623 return fs.File{623 return fs.File{
624 .handle = sockfd,624 .handle = sockfd,
...@@ -677,7 +677,13 @@ pub fn tcpConnectToAddress(address: Address) !fs.File {...@@ -677,7 +677,13 @@ pub fn tcpConnectToAddress(address: Address) !fs.File {
677 (if (builtin.os.tag == .windows) 0 else os.SOCK_CLOEXEC);677 (if (builtin.os.tag == .windows) 0 else os.SOCK_CLOEXEC);
678 const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP);678 const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP);
679 errdefer os.close(sockfd);679 errdefer os.close(sockfd);
680 try os.connect(sockfd, &address.any, address.getOsSockLen());680
681 if (std.io.is_async) {
682 const loop = std.event.Loop.instance orelse return error.WouldBlock;
683 try loop.connect(sockfd, &address.any, address.getOsSockLen());
684 } else {
685 try os.connect(sockfd, &address.any, address.getOsSockLen());
686 }
681687
682 return fs.File{ .handle = sockfd };688 return fs.File{ .handle = sockfd };
683}689}
...@@ -1429,7 +1435,11 @@ fn resMSendRc(...@@ -1429,7 +1435,11 @@ fn resMSendRc(
1429 if (answers[i].len == 0) {1435 if (answers[i].len == 0) {
1430 var j: usize = 0;1436 var j: usize = 0;
1431 while (j < ns.len) : (j += 1) {1437 while (j < ns.len) : (j += 1) {
1432 _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined;1438 if (std.io.is_async) {
1439 _ = std.event.Loop.instance.?.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined;
1440 } else {
1441 _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined;
1442 }
1433 }1443 }
1434 }1444 }
1435 }1445 }
...@@ -1444,7 +1454,10 @@ fn resMSendRc(...@@ -1444,7 +1454,10 @@ fn resMSendRc(
14441454
1445 while (true) {1455 while (true) {
1446 var sl_copy = sl;1456 var sl_copy = sl;
1447 const rlen = os.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break;1457 const rlen = if (std.io.is_async)
1458 std.event.Loop.instance.?.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break
1459 else
1460 os.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break;
14481461
1449 // Ignore non-identifiable packets1462 // Ignore non-identifiable packets
1450 if (rlen < 4) continue;1463 if (rlen < 4) continue;
...@@ -1470,7 +1483,11 @@ fn resMSendRc(...@@ -1470,7 +1483,11 @@ fn resMSendRc(
1470 0, 3 => {},1483 0, 3 => {},
1471 2 => if (servfail_retry != 0) {1484 2 => if (servfail_retry != 0) {
1472 servfail_retry -= 1;1485 servfail_retry -= 1;
1473 _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined;1486 if (std.io.is_async) {
1487 _ = std.event.Loop.instance.?.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined;
1488 } else {
1489 _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined;
1490 }
1474 },1491 },
1475 else => continue,1492 else => continue,
1476 }1493 }
...@@ -1661,18 +1678,23 @@ pub const StreamServer = struct {...@@ -1661,18 +1678,23 @@ pub const StreamServer = struct {
16611678
1662 /// If this function succeeds, the returned `Connection` is a caller-managed resource.1679 /// If this function succeeds, the returned `Connection` is a caller-managed resource.
1663 pub fn accept(self: *StreamServer) AcceptError!Connection {1680 pub fn accept(self: *StreamServer) AcceptError!Connection {
1664 const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
1665 const accept_flags = nonblock | os.SOCK_CLOEXEC;
1666 var accepted_addr: Address = undefined;1681 var accepted_addr: Address = undefined;
1667 var adr_len: os.socklen_t = @sizeOf(Address);1682 var adr_len: os.socklen_t = @sizeOf(Address);
1668 if (os.accept(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| {1683 const accept_result = blk: {
1684 if (std.io.is_async) {
1685 const loop = std.event.Loop.instance orelse return error.UnexpectedError;
1686 break :blk loop.accept(self.sockfd.?, &accepted_addr.any, &adr_len, os.SOCK_CLOEXEC);
1687 } else {
1688 break :blk os.accept(self.sockfd.?, &accepted_addr.any, &adr_len, os.SOCK_CLOEXEC);
1689 }
1690 };
1691
1692 if (accept_result) |fd| {
1669 return Connection{1693 return Connection{
1670 .file = fs.File{ .handle = fd },1694 .file = fs.File{ .handle = fd },
1671 .address = accepted_addr,1695 .address = accepted_addr,
1672 };1696 };
1673 } else |err| switch (err) {1697 } else |err| switch (err) {
1674 // We only give SOCK_NONBLOCK when I/O mode is async, in which case this error
1675 // is handled by os.accept4.
1676 error.WouldBlock => unreachable,1698 error.WouldBlock => unreachable,
1677 else => |e| return e,1699 else => |e| return e,
1678 }1700 }
lib/std/os.zig+34-90
...@@ -314,8 +314,8 @@ pub const ReadError = error{...@@ -314,8 +314,8 @@ pub const ReadError = error{
314314
315/// Returns the number of bytes that were read, which can be less than315/// Returns the number of bytes that were read, which can be less than
316/// buf.len. If 0 bytes were read, that means EOF.316/// buf.len. If 0 bytes were read, that means EOF.
317/// If the application has a global event loop enabled, EAGAIN is handled317/// If `fd` is opened in non blocking mode, the function will return error.WouldBlock
318/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.318/// when EAGAIN is received.
319///319///
320/// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000`320/// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000`
321/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as321/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
...@@ -366,12 +366,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {...@@ -366,12 +366,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
366 EINTR => continue,366 EINTR => continue,
367 EINVAL => unreachable,367 EINVAL => unreachable,
368 EFAULT => unreachable,368 EFAULT => unreachable,
369 EAGAIN => if (std.event.Loop.instance) |loop| {369 EAGAIN => return error.WouldBlock,
370 loop.waitUntilFdReadable(fd);
371 continue;
372 } else {
373 return error.WouldBlock;
374 },
375 EBADF => return error.NotOpenForReading, // Can be a race condition.370 EBADF => return error.NotOpenForReading, // Can be a race condition.
376 EIO => return error.InputOutput,371 EIO => return error.InputOutput,
377 EISDIR => return error.IsDir,372 EISDIR => return error.IsDir,
...@@ -387,8 +382,8 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {...@@ -387,8 +382,8 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
387382
388/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.383/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
389///384///
390/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled385/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
391/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.386/// return error.WouldBlock when EAGAIN is received.
392/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are387/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
393/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.388/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
394///389///
...@@ -428,12 +423,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {...@@ -428,12 +423,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
428 EINTR => continue,423 EINTR => continue,
429 EINVAL => unreachable,424 EINVAL => unreachable,
430 EFAULT => unreachable,425 EFAULT => unreachable,
431 EAGAIN => if (std.event.Loop.instance) |loop| {426 EAGAIN => return error.WouldBlock,
432 loop.waitUntilFdReadable(fd);
433 continue;
434 } else {
435 return error.WouldBlock;
436 },
437 EBADF => return error.NotOpenForReading, // can be a race condition427 EBADF => return error.NotOpenForReading, // can be a race condition
438 EIO => return error.InputOutput,428 EIO => return error.InputOutput,
439 EISDIR => return error.IsDir,429 EISDIR => return error.IsDir,
...@@ -450,8 +440,8 @@ pub const PReadError = ReadError || error{Unseekable};...@@ -450,8 +440,8 @@ pub const PReadError = ReadError || error{Unseekable};
450///440///
451/// Retries when interrupted by a signal.441/// Retries when interrupted by a signal.
452///442///
453/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled443/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
454/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.444/// return error.WouldBlock when EAGAIN is received.
455/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are445/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
456/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.446/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
457pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {447pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
...@@ -492,12 +482,7 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {...@@ -492,12 +482,7 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
492 EINTR => continue,482 EINTR => continue,
493 EINVAL => unreachable,483 EINVAL => unreachable,
494 EFAULT => unreachable,484 EFAULT => unreachable,
495 EAGAIN => if (std.event.Loop.instance) |loop| {485 EAGAIN => return error.WouldBlock,
496 loop.waitUntilFdReadable(fd);
497 continue;
498 } else {
499 return error.WouldBlock;
500 },
501 EBADF => return error.NotOpenForReading, // Can be a race condition.486 EBADF => return error.NotOpenForReading, // Can be a race condition.
502 EIO => return error.InputOutput,487 EIO => return error.InputOutput,
503 EISDIR => return error.IsDir,488 EISDIR => return error.IsDir,
...@@ -586,8 +571,8 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {...@@ -586,8 +571,8 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
586///571///
587/// Retries when interrupted by a signal.572/// Retries when interrupted by a signal.
588///573///
589/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled574/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
590/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.575/// return error.WouldBlock when EAGAIN is received.
591/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are576/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
592/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.577/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
593///578///
...@@ -637,12 +622,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {...@@ -637,12 +622,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
637 EINTR => continue,622 EINTR => continue,
638 EINVAL => unreachable,623 EINVAL => unreachable,
639 EFAULT => unreachable,624 EFAULT => unreachable,
640 EAGAIN => if (std.event.Loop.instance) |loop| {625 EAGAIN => return error.WouldBlock,
641 loop.waitUntilFdReadable(fd);
642 continue;
643 } else {
644 return error.WouldBlock;
645 },
646 EBADF => return error.NotOpenForReading, // can be a race condition626 EBADF => return error.NotOpenForReading, // can be a race condition
647 EIO => return error.InputOutput,627 EIO => return error.InputOutput,
648 EISDIR => return error.IsDir,628 EISDIR => return error.IsDir,
...@@ -687,8 +667,8 @@ pub const WriteError = error{...@@ -687,8 +667,8 @@ pub const WriteError = error{
687/// another write() call to transfer the remaining bytes. The subsequent call will either667/// another write() call to transfer the remaining bytes. The subsequent call will either
688/// transfer further bytes or may result in an error (e.g., if the disk is now full).668/// transfer further bytes or may result in an error (e.g., if the disk is now full).
689///669///
690/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled670/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
691/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.671/// return error.WouldBlock when EAGAIN is received.
692/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are672/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
693/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.673/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
694///674///
...@@ -741,12 +721,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {...@@ -741,12 +721,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
741 EINTR => continue,721 EINTR => continue,
742 EINVAL => unreachable,722 EINVAL => unreachable,
743 EFAULT => unreachable,723 EFAULT => unreachable,
744 EAGAIN => if (std.event.Loop.instance) |loop| {724 EAGAIN => return error.WouldBlock,
745 loop.waitUntilFdWritable(fd);
746 continue;
747 } else {
748 return error.WouldBlock;
749 },
750 EBADF => return error.NotOpenForWriting, // can be a race condition.725 EBADF => return error.NotOpenForWriting, // can be a race condition.
751 EDESTADDRREQ => unreachable, // `connect` was never called.726 EDESTADDRREQ => unreachable, // `connect` was never called.
752 EDQUOT => return error.DiskQuota,727 EDQUOT => return error.DiskQuota,
...@@ -772,8 +747,8 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {...@@ -772,8 +747,8 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
772/// another write() call to transfer the remaining bytes. The subsequent call will either747/// another write() call to transfer the remaining bytes. The subsequent call will either
773/// transfer further bytes or may result in an error (e.g., if the disk is now full).748/// transfer further bytes or may result in an error (e.g., if the disk is now full).
774///749///
775/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled750/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
776/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.751/// return error.WouldBlock when EAGAIN is received.k`.
777/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are752/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
778/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.753/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
779///754///
...@@ -814,12 +789,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {...@@ -814,12 +789,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
814 EINTR => continue,789 EINTR => continue,
815 EINVAL => unreachable,790 EINVAL => unreachable,
816 EFAULT => unreachable,791 EFAULT => unreachable,
817 EAGAIN => if (std.event.Loop.instance) |loop| {792 EAGAIN => return error.WouldBlock,
818 loop.waitUntilFdWritable(fd);
819 continue;
820 } else {
821 return error.WouldBlock;
822 },
823 EBADF => return error.NotOpenForWriting, // Can be a race condition.793 EBADF => return error.NotOpenForWriting, // Can be a race condition.
824 EDESTADDRREQ => unreachable, // `connect` was never called.794 EDESTADDRREQ => unreachable, // `connect` was never called.
825 EDQUOT => return error.DiskQuota,795 EDQUOT => return error.DiskQuota,
...@@ -847,8 +817,8 @@ pub const PWriteError = WriteError || error{Unseekable};...@@ -847,8 +817,8 @@ pub const PWriteError = WriteError || error{Unseekable};
847/// another write() call to transfer the remaining bytes. The subsequent call will either817/// another write() call to transfer the remaining bytes. The subsequent call will either
848/// transfer further bytes or may result in an error (e.g., if the disk is now full).818/// transfer further bytes or may result in an error (e.g., if the disk is now full).
849///819///
850/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled820/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
851/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.821/// return error.WouldBlock when EAGAIN is received.
852/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are822/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
853/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.823/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
854///824///
...@@ -905,12 +875,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {...@@ -905,12 +875,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
905 EINTR => continue,875 EINTR => continue,
906 EINVAL => unreachable,876 EINVAL => unreachable,
907 EFAULT => unreachable,877 EFAULT => unreachable,
908 EAGAIN => if (std.event.Loop.instance) |loop| {878 EAGAIN => return error.WouldBlock,
909 loop.waitUntilFdWritable(fd);
910 continue;
911 } else {
912 return error.WouldBlock;
913 },
914 EBADF => return error.NotOpenForWriting, // Can be a race condition.879 EBADF => return error.NotOpenForWriting, // Can be a race condition.
915 EDESTADDRREQ => unreachable, // `connect` was never called.880 EDESTADDRREQ => unreachable, // `connect` was never called.
916 EDQUOT => return error.DiskQuota,881 EDQUOT => return error.DiskQuota,
...@@ -939,8 +904,8 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {...@@ -939,8 +904,8 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
939/// another write() call to transfer the remaining bytes. The subsequent call will either904/// another write() call to transfer the remaining bytes. The subsequent call will either
940/// transfer further bytes or may result in an error (e.g., if the disk is now full).905/// transfer further bytes or may result in an error (e.g., if the disk is now full).
941///906///
942/// If the application has a global event loop enabled, EAGAIN is handled907/// If `fd` is opened in non blocking mode, the function will
943/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.908/// return error.WouldBlock when EAGAIN is received.
944///909///
945/// The following systems do not have this syscall, and will return partial writes if more than one910/// The following systems do not have this syscall, and will return partial writes if more than one
946/// vector is provided:911/// vector is provided:
...@@ -993,12 +958,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz...@@ -993,12 +958,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
993 EINTR => continue,958 EINTR => continue,
994 EINVAL => unreachable,959 EINVAL => unreachable,
995 EFAULT => unreachable,960 EFAULT => unreachable,
996 EAGAIN => if (std.event.Loop.instance) |loop| {961 EAGAIN => return error.WouldBlock,
997 loop.waitUntilFdWritable(fd);
998 continue;
999 } else {
1000 return error.WouldBlock;
1001 },
1002 EBADF => return error.NotOpenForWriting, // Can be a race condition.962 EBADF => return error.NotOpenForWriting, // Can be a race condition.
1003 EDESTADDRREQ => unreachable, // `connect` was never called.963 EDESTADDRREQ => unreachable, // `connect` was never called.
1004 EDQUOT => return error.DiskQuota,964 EDQUOT => return error.DiskQuota,
...@@ -2846,8 +2806,8 @@ pub const AcceptError = error{...@@ -2846,8 +2806,8 @@ pub const AcceptError = error{
2846} || UnexpectedError;2806} || UnexpectedError;
28472807
2848/// Accept a connection on a socket.2808/// Accept a connection on a socket.
2849/// If the application has a global event loop enabled, EAGAIN is handled2809/// If `sockfd` is opened in non blocking mode, the function will
2850/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.2810/// return error.WouldBlock when EAGAIN is received.
2851pub fn accept(2811pub fn accept(
2852 /// This argument is a socket that has been created with `socket`, bound to a local address2812 /// This argument is a socket that has been created with `socket`, bound to a local address
2853 /// with `bind`, and is listening for connections after a `listen`.2813 /// with `bind`, and is listening for connections after a `listen`.
...@@ -2890,12 +2850,7 @@ pub fn accept(...@@ -2890,12 +2850,7 @@ pub fn accept(
2890 return fd;2850 return fd;
2891 },2851 },
2892 EINTR => continue,2852 EINTR => continue,
2893 EAGAIN => if (std.event.Loop.instance) |loop| {2853 EAGAIN => return error.WouldBlock,
2894 loop.waitUntilFdReadable(sockfd);
2895 continue;
2896 } else {
2897 return error.WouldBlock;
2898 },
2899 EBADF => unreachable, // always a race condition2854 EBADF => unreachable, // always a race condition
2900 ECONNABORTED => return error.ConnectionAborted,2855 ECONNABORTED => return error.ConnectionAborted,
2901 EFAULT => unreachable,2856 EFAULT => unreachable,
...@@ -3081,6 +3036,8 @@ pub const ConnectError = error{...@@ -3081,6 +3036,8 @@ pub const ConnectError = error{
3081} || UnexpectedError;3036} || UnexpectedError;
30823037
3083/// Initiate a connection on a socket.3038/// Initiate a connection on a socket.
3039/// If `sockfd` is opened in non blocking mode, the function will
3040/// return error.WouldBlock when EAGAIN or EINPROGRESS is received.
3084pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {3041pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
3085 if (builtin.os.tag == .windows) {3042 if (builtin.os.tag == .windows) {
3086 const rc = windows.ws2_32.connect(sockfd, sock_addr, len);3043 const rc = windows.ws2_32.connect(sockfd, sock_addr, len);
...@@ -3113,11 +3070,7 @@ pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) Con...@@ -3113,11 +3070,7 @@ pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) Con
3113 EADDRINUSE => return error.AddressInUse,3070 EADDRINUSE => return error.AddressInUse,
3114 EADDRNOTAVAIL => return error.AddressNotAvailable,3071 EADDRNOTAVAIL => return error.AddressNotAvailable,
3115 EAFNOSUPPORT => return error.AddressFamilyNotSupported,3072 EAFNOSUPPORT => return error.AddressFamilyNotSupported,
3116 EAGAIN, EINPROGRESS => {3073 EAGAIN, EINPROGRESS => return error.WouldBlock,
3117 const loop = std.event.Loop.instance orelse return error.WouldBlock;
3118 loop.waitUntilFdWritable(sockfd);
3119 return getsockoptError(sockfd);
3120 },
3121 EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.3074 EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
3122 EBADF => unreachable, // sockfd is not a valid open file descriptor.3075 EBADF => unreachable, // sockfd is not a valid open file descriptor.
3123 ECONNREFUSED => return error.ConnectionRefused,3076 ECONNREFUSED => return error.ConnectionRefused,
...@@ -4620,14 +4573,8 @@ pub fn sendto(...@@ -4620,14 +4573,8 @@ pub fn sendto(
4620 const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);4573 const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
4621 switch (errno(rc)) {4574 switch (errno(rc)) {
4622 0 => return @intCast(usize, rc),4575 0 => return @intCast(usize, rc),
4623
4624 EACCES => return error.AccessDenied,4576 EACCES => return error.AccessDenied,
4625 EAGAIN => if (std.event.Loop.instance) |loop| {4577 EAGAIN => return error.WouldBlock,
4626 loop.waitUntilFdWritable(sockfd);
4627 continue;
4628 } else {
4629 return error.WouldBlock;
4630 },
4631 EALREADY => return error.FastOpenAlreadyInProgress,4578 EALREADY => return error.FastOpenAlreadyInProgress,
4632 EBADF => unreachable, // always a race condition4579 EBADF => unreachable, // always a race condition
4633 ECONNRESET => return error.ConnectionResetByPeer,4580 ECONNRESET => return error.ConnectionResetByPeer,
...@@ -5106,6 +5053,8 @@ pub const RecvFromError = error{...@@ -5106,6 +5053,8 @@ pub const RecvFromError = error{
5106 SystemResources,5053 SystemResources,
5107} || UnexpectedError;5054} || UnexpectedError;
51085055
5056/// If `sockfd` is opened in non blocking mode, the function will
5057/// return error.WouldBlock when EAGAIN is received.
5109pub fn recvfrom(5058pub fn recvfrom(
5110 sockfd: fd_t,5059 sockfd: fd_t,
5111 buf: []u8,5060 buf: []u8,
...@@ -5123,12 +5072,7 @@ pub fn recvfrom(...@@ -5123,12 +5072,7 @@ pub fn recvfrom(
5123 ENOTCONN => unreachable,5072 ENOTCONN => unreachable,
5124 ENOTSOCK => unreachable,5073 ENOTSOCK => unreachable,
5125 EINTR => continue,5074 EINTR => continue,
5126 EAGAIN => if (std.event.Loop.instance) |loop| {5075 EAGAIN => return error.WouldBlock,
5127 loop.waitUntilFdReadable(sockfd);
5128 continue;
5129 } else {
5130 return error.WouldBlock;
5131 },
5132 ENOMEM => return error.SystemResources,5076 ENOMEM => return error.SystemResources,
5133 ECONNREFUSED => return error.ConnectionRefused,5077 ECONNREFUSED => return error.ConnectionRefused,
5134 else => |err| return unexpectedErrno(err),5078 else => |err| return unexpectedErrno(err),
lib/std/os/uefi/tables/system_table.zig+1-1
...@@ -35,7 +35,7 @@ pub const SystemTable = extern struct {...@@ -35,7 +35,7 @@ pub const SystemTable = extern struct {
35 runtime_services: *RuntimeServices,35 runtime_services: *RuntimeServices,
36 boot_services: ?*BootServices,36 boot_services: ?*BootServices,
37 number_of_table_entries: usize,37 number_of_table_entries: usize,
38 configuration_table: *ConfigurationTable,38 configuration_table: [*]ConfigurationTable,
3939
40 pub const signature: u64 = 0x5453595320494249;40 pub const signature: u64 = 0x5453595320494249;
41 pub const revision_1_02: u32 = (1 << 16) | 2;41 pub const revision_1_02: u32 = (1 << 16) | 2;
lib/std/os/windows.zig+17-1
...@@ -764,6 +764,7 @@ pub const DeleteFileError = error{...@@ -764,6 +764,7 @@ pub const DeleteFileError = error{
764 Unexpected,764 Unexpected,
765 NotDir,765 NotDir,
766 IsDir,766 IsDir,
767 DirNotEmpty,
767};768};
768769
769pub const DeleteFileOptions = struct {770pub const DeleteFileOptions = struct {
...@@ -818,7 +819,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil...@@ -818,7 +819,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
818 0,819 0,
819 );820 );
820 switch (rc) {821 switch (rc) {
821 .SUCCESS => return CloseHandle(tmp_handle),822 .SUCCESS => CloseHandle(tmp_handle),
822 .OBJECT_NAME_INVALID => unreachable,823 .OBJECT_NAME_INVALID => unreachable,
823 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,824 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
824 .INVALID_PARAMETER => unreachable,825 .INVALID_PARAMETER => unreachable,
...@@ -826,6 +827,21 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil...@@ -826,6 +827,21 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
826 .NOT_A_DIRECTORY => return error.NotDir,827 .NOT_A_DIRECTORY => return error.NotDir,
827 else => return unexpectedStatus(rc),828 else => return unexpectedStatus(rc),
828 }829 }
830
831 // If a directory fails to be deleted, CloseHandle will still report success
832 // Check if the directory still exists and return error.DirNotEmpty if true
833 if (options.remove_dir) {
834 var basic_info: FILE_BASIC_INFORMATION = undefined;
835 switch (ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
836 .SUCCESS => return error.DirNotEmpty,
837 .OBJECT_NAME_NOT_FOUND => return,
838 .OBJECT_PATH_NOT_FOUND => return,
839 .INVALID_PARAMETER => unreachable,
840 .ACCESS_DENIED => return error.AccessDenied,
841 .OBJECT_PATH_SYNTAX_BAD => unreachable,
842 else => |urc| return unexpectedStatus(urc),
843 }
844 }
829}845}
830846
831pub const MoveFileError = error{ FileNotFound, Unexpected };847pub const MoveFileError = error{ FileNotFound, Unexpected };
lib/std/zig/ast.zig+9
...@@ -823,6 +823,15 @@ pub const Node = struct {...@@ -823,6 +823,15 @@ pub const Node = struct {
823 }823 }
824 }824 }
825825
826 pub fn findFirstWithId(self: *Node, id: Id) ?*Node {
827 if (self.id == id) return self;
828 var child_i: usize = 0;
829 while (self.iterate(child_i)) |child| : (child_i += 1) {
830 if (child.findFirstWithId(id)) |result| return result;
831 }
832 return null;
833 }
834
826 pub fn dump(self: *Node, indent: usize) void {835 pub fn dump(self: *Node, indent: usize) void {
827 {836 {
828 var i: usize = 0;837 var i: usize = 0;
lib/std/zig/parser_test.zig+325-3
...@@ -1301,8 +1301,10 @@ test "zig fmt: array literal with hint" {...@@ -1301,8 +1301,10 @@ test "zig fmt: array literal with hint" {
1301 \\const a = []u8{1301 \\const a = []u8{
1302 \\ 1, 2,1302 \\ 1, 2,
1303 \\ 3, 4,1303 \\ 3, 4,
1304 \\ 5, 6, // blah1304 \\ 5,
1305 \\ 7, 8,1305 \\ 6, // blah
1306 \\ 7,
1307 \\ 8,
1306 \\};1308 \\};
1307 \\const a = []u8{1309 \\const a = []u8{
1308 \\ 1, 2,1310 \\ 1, 2,
...@@ -1372,7 +1374,7 @@ test "zig fmt: multiline string parameter in fn call with trailing comma" {...@@ -1372,7 +1374,7 @@ test "zig fmt: multiline string parameter in fn call with trailing comma" {
1372 \\ \\ZIG_C_HEADER_FILES {}1374 \\ \\ZIG_C_HEADER_FILES {}
1373 \\ \\ZIG_DIA_GUIDS_LIB {}1375 \\ \\ZIG_DIA_GUIDS_LIB {}
1374 \\ \\1376 \\ \\
1375 \\ ,1377 \\ ,
1376 \\ std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR),1378 \\ std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR),
1377 \\ std.cstr.toSliceConst(c.ZIG_CXX_COMPILER),1379 \\ std.cstr.toSliceConst(c.ZIG_CXX_COMPILER),
1378 \\ std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),1380 \\ std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),
...@@ -3321,6 +3323,326 @@ test "zig fmt: Don't add extra newline after if" {...@@ -3321,6 +3323,326 @@ test "zig fmt: Don't add extra newline after if" {
3321 );3323 );
3322}3324}
33233325
3326test "zig fmt: comments in ternary ifs" {
3327 try testCanonical(
3328 \\const x = if (true) {
3329 \\ 1;
3330 \\} else if (false)
3331 \\ // Comment
3332 \\ 0;
3333 \\const y = if (true)
3334 \\ // Comment
3335 \\ 1
3336 \\else
3337 \\ 0;
3338 \\
3339 \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
3340 \\
3341 );
3342}
3343
3344test "zig fmt: test comments in field access chain" {
3345 try testCanonical(
3346 \\pub const str = struct {
3347 \\ pub const Thing = more.more //
3348 \\ .more() //
3349 \\ .more().more() //
3350 \\ .more() //
3351 \\ // .more() //
3352 \\ .more() //
3353 \\ .more();
3354 \\ data: Data,
3355 \\};
3356 \\
3357 \\pub const str = struct {
3358 \\ pub const Thing = more.more //
3359 \\ .more() //
3360 \\ // .more() //
3361 \\ // .more() //
3362 \\ // .more() //
3363 \\ .more() //
3364 \\ .more();
3365 \\ data: Data,
3366 \\};
3367 \\
3368 \\pub const str = struct {
3369 \\ pub const Thing = more //
3370 \\ .more //
3371 \\ .more() //
3372 \\ .more();
3373 \\ data: Data,
3374 \\};
3375 \\
3376 );
3377}
3378
3379test "zig fmt: Indent comma correctly after multiline string literals in arg list (trailing comma)" {
3380 try testCanonical(
3381 \\fn foo() void {
3382 \\ z.display_message_dialog(
3383 \\ *const [323:0]u8,
3384 \\ \\Message Text
3385 \\ \\------------
3386 \\ \\xxxxxxxxxxxx
3387 \\ \\xxxxxxxxxxxx
3388 \\ ,
3389 \\ g.GtkMessageType.GTK_MESSAGE_WARNING,
3390 \\ null,
3391 \\ );
3392 \\
3393 \\ z.display_message_dialog(*const [323:0]u8,
3394 \\ \\Message Text
3395 \\ \\------------
3396 \\ \\xxxxxxxxxxxx
3397 \\ \\xxxxxxxxxxxx
3398 \\ , g.GtkMessageType.GTK_MESSAGE_WARNING, null);
3399 \\}
3400 \\
3401 );
3402}
3403
3404test "zig fmt: Control flow statement as body of blockless if" {
3405 try testCanonical(
3406 \\pub fn main() void {
3407 \\ const zoom_node = if (focused_node == layout_first)
3408 \\ if (it.next()) {
3409 \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
3410 \\ } else null
3411 \\ else
3412 \\ focused_node;
3413 \\
3414 \\ const zoom_node = if (focused_node == layout_first) while (it.next()) |node| {
3415 \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
3416 \\ } else null else
3417 \\ focused_node;
3418 \\
3419 \\ const zoom_node = if (focused_node == layout_first)
3420 \\ if (it.next()) {
3421 \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
3422 \\ } else null;
3423 \\
3424 \\ const zoom_node = if (focused_node == layout_first) while (it.next()) |node| {
3425 \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
3426 \\ };
3427 \\
3428 \\ const zoom_node = if (focused_node == layout_first) for (nodes) |node| {
3429 \\ break node;
3430 \\ };
3431 \\
3432 \\ const zoom_node = if (focused_node == layout_first) switch (nodes) {
3433 \\ 0 => 0,
3434 \\ } else
3435 \\ focused_node;
3436 \\}
3437 \\
3438 );
3439}
3440
3441test "zig fmt: " {
3442 try testCanonical(
3443 \\pub fn sendViewTags(self: Self) void {
3444 \\ var it = ViewStack(View).iterator(self.output.views.first, std.math.maxInt(u32));
3445 \\ while (it.next()) |node|
3446 \\ view_tags.append(node.view.current_tags) catch {
3447 \\ c.wl_resource_post_no_memory(self.wl_resource);
3448 \\ log.crit(.river_status, "out of memory", .{});
3449 \\ return;
3450 \\ };
3451 \\}
3452 \\
3453 );
3454}
3455
3456test "zig fmt: allow trailing line comments to do manual array formatting" {
3457 try testCanonical(
3458 \\fn foo() void {
3459 \\ self.code.appendSliceAssumeCapacity(&[_]u8{
3460 \\ 0x55, // push rbp
3461 \\ 0x48, 0x89, 0xe5, // mov rbp, rsp
3462 \\ 0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc)
3463 \\ });
3464 \\
3465 \\ di_buf.appendAssumeCapacity(&[_]u8{
3466 \\ 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header
3467 \\ DW.AT_stmt_list, DW_FORM_data4, // form value pairs
3468 \\ DW.AT_low_pc, DW_FORM_addr,
3469 \\ DW.AT_high_pc, DW_FORM_addr,
3470 \\ DW.AT_name, DW_FORM_strp,
3471 \\ DW.AT_comp_dir, DW_FORM_strp,
3472 \\ DW.AT_producer, DW_FORM_strp,
3473 \\ DW.AT_language, DW_FORM_data2,
3474 \\ 0, 0, // sentinel
3475 \\ });
3476 \\
3477 \\ self.code.appendSliceAssumeCapacity(&[_]u8{
3478 \\ 0x55, // push rbp
3479 \\ 0x48, 0x89, 0xe5, // mov rbp, rsp
3480 \\ // How do we handle this?
3481 \\ //0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc)
3482 \\ // Here's a blank line, should that be allowed?
3483 \\
3484 \\ 0x48, 0x89, 0xe5,
3485 \\ 0x33, 0x45,
3486 \\ // Now the comment breaks a single line -- how do we handle this?
3487 \\ 0x88,
3488 \\ });
3489 \\}
3490 \\
3491 );
3492}
3493
3494test "zig fmt: multiline string literals should play nice with array initializers" {
3495 try testCanonical(
3496 \\fn main() void {
3497 \\ var a = .{.{.{.{.{.{.{.{
3498 \\ 0,
3499 \\ }}}}}}}};
3500 \\ myFunc(.{
3501 \\ "aaaaaaa", "bbbbbb", "ccccc",
3502 \\ "dddd", ("eee"), ("fff"),
3503 \\ ("gggg"),
3504 \\ // Line comment
3505 \\ \\Multiline String Literals can be quite long
3506 \\ ,
3507 \\ \\Multiline String Literals can be quite long
3508 \\ \\Multiline String Literals can be quite long
3509 \\ ,
3510 \\ \\Multiline String Literals can be quite long
3511 \\ \\Multiline String Literals can be quite long
3512 \\ \\Multiline String Literals can be quite long
3513 \\ \\Multiline String Literals can be quite long
3514 \\ ,
3515 \\ (
3516 \\ \\Multiline String Literals can be quite long
3517 \\ ),
3518 \\ .{
3519 \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3520 \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3521 \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3522 \\ },
3523 \\ .{(
3524 \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3525 \\ )},
3526 \\ .{
3527 \\ "xxxxxxx", "xxx",
3528 \\ (
3529 \\ \\ xxx
3530 \\ ),
3531 \\ "xxx", "xxx",
3532 \\ },
3533 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, .{ "xxxxxxx", "xxx", "xxx", "xxx" },
3534 \\ "aaaaaaa", "bbbbbb", "ccccc", // -
3535 \\ "dddd", ("eee"), ("fff"),
3536 \\ .{
3537 \\ "xxx", "xxx",
3538 \\ (
3539 \\ \\ xxx
3540 \\ ),
3541 \\ "xxxxxxxxxxxxxx", "xxx",
3542 \\ },
3543 \\ .{
3544 \\ (
3545 \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3546 \\ ),
3547 \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3548 \\ },
3549 \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3550 \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3551 \\ });
3552 \\}
3553 \\
3554 );
3555}
3556
3557test "zig fmt: use of comments and Multiline string literals may force the parameters over multiple lines" {
3558 try testCanonical(
3559 \\pub fn makeMemUndefined(qzz: []u8) i1 {
3560 \\ cases.add( // fixed bug #2032
3561 \\ "compile diagnostic string for top level decl type",
3562 \\ \\export fn entry() void {
3563 \\ \\ var foo: u32 = @This(){};
3564 \\ \\}
3565 \\ , &[_][]const u8{
3566 \\ "tmp.zig:2:27: error: type 'u32' does not support array initialization",
3567 \\ });
3568 \\ @compileError(
3569 \\ \\ unknown-length pointers and C pointers cannot be hashed deeply.
3570 \\ \\ Consider providing your own hash function.
3571 \\ \\ unknown-length pointers and C pointers cannot be hashed deeply.
3572 \\ \\ Consider providing your own hash function.
3573 \\ );
3574 \\ return @intCast(i1, doMemCheckClientRequestExpr(0, // default return
3575 \\ .MakeMemUndefined, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0));
3576 \\}
3577 \\
3578 \\// This looks like garbage don't do this
3579 \\const rparen = tree.prevToken(
3580 \\// the first token for the annotation expressions is the left
3581 \\// parenthesis, hence the need for two prevToken
3582 \\ if (fn_proto.getAlignExpr()) |align_expr|
3583 \\ tree.prevToken(tree.prevToken(align_expr.firstToken()))
3584 \\else if (fn_proto.getSectionExpr()) |section_expr|
3585 \\ tree.prevToken(tree.prevToken(section_expr.firstToken()))
3586 \\else if (fn_proto.getCallconvExpr()) |callconv_expr|
3587 \\ tree.prevToken(tree.prevToken(callconv_expr.firstToken()))
3588 \\else switch (fn_proto.return_type) {
3589 \\ .Explicit => |node| node.firstToken(),
3590 \\ .InferErrorSet => |node| tree.prevToken(node.firstToken()),
3591 \\ .Invalid => unreachable,
3592 \\});
3593 \\
3594 );
3595}
3596
3597test "zig fmt: single argument trailing commas in @builtins()" {
3598 try testCanonical(
3599 \\pub fn foo(qzz: []u8) i1 {
3600 \\ @panic(
3601 \\ foo,
3602 \\ );
3603 \\ panic(
3604 \\ foo,
3605 \\ );
3606 \\ @panic(
3607 \\ foo,
3608 \\ bar,
3609 \\ );
3610 \\}
3611 \\
3612 );
3613}
3614
3615test "zig fmt: trailing comma should force multiline 1 column" {
3616 try testTransform(
3617 \\pub const UUID_NULL: uuid_t = [16]u8{0,0,0,0,};
3618 \\
3619 ,
3620 \\pub const UUID_NULL: uuid_t = [16]u8{
3621 \\ 0,
3622 \\ 0,
3623 \\ 0,
3624 \\ 0,
3625 \\};
3626 \\
3627 );
3628}
3629
3630test "zig fmt: function params should align nicely" {
3631 try testCanonical(
3632 \\pub fn foo() void {
3633 \\ cases.addRuntimeSafety("slicing operator with sentinel",
3634 \\ \\const std = @import("std");
3635 \\ ++ check_panic_msg ++
3636 \\ \\pub fn main() void {
3637 \\ \\ var buf = [4]u8{'a','b','c',0};
3638 \\ \\ const slice = buf[0..:0];
3639 \\ \\}
3640 \\ );
3641 \\}
3642 \\
3643 );
3644}
3645
3324const std = @import("std");3646const std = @import("std");
3325const mem = std.mem;3647const mem = std.mem;
3326const warn = std.debug.warn;3648const warn = std.debug.warn;
lib/std/zig/render.zig+241-139
...@@ -522,7 +522,11 @@ fn renderExpression(...@@ -522,7 +522,11 @@ fn renderExpression(
522 break :blk if (loc.line == 0) op_space else Space.Newline;522 break :blk if (loc.line == 0) op_space else Space.Newline;
523 };523 };
524524
525 try renderToken(tree, ais, infix_op_node.op_token, after_op_space);525 {
526 ais.pushIndent();
527 defer ais.popIndent();
528 try renderToken(tree, ais, infix_op_node.op_token, after_op_space);
529 }
526 ais.pushIndentOneShot();530 ais.pushIndentOneShot();
527 return renderExpression(allocator, ais, tree, infix_op_node.rhs, space);531 return renderExpression(allocator, ais, tree, infix_op_node.rhs, space);
528 },532 },
...@@ -710,141 +714,194 @@ fn renderExpression(...@@ -710,141 +714,194 @@ fn renderExpression(
710 .node => |node| tree.nextToken(node.lastToken()),714 .node => |node| tree.nextToken(node.lastToken()),
711 };715 };
712716
713 if (exprs.len == 0) {717 switch (lhs) {
714 switch (lhs) {718 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
715 .dot => |dot| try renderToken(tree, ais, dot, Space.None),719 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
716 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),720 }
717 }
718
719 {
720 ais.pushIndent();
721 defer ais.popIndent();
722 try renderToken(tree, ais, lbrace, Space.None);
723 }
724721
722 if (exprs.len == 0) {
723 try renderToken(tree, ais, lbrace, Space.None);
725 return renderToken(tree, ais, rtoken, space);724 return renderToken(tree, ais, rtoken, space);
726 }725 }
727 if (exprs.len == 1 and tree.token_ids[exprs[0].*.lastToken() + 1] == .RBrace) {726
727 if (exprs.len == 1 and exprs[0].tag != .MultilineStringLiteral and tree.token_ids[exprs[0].*.lastToken() + 1] == .RBrace) {
728 const expr = exprs[0];728 const expr = exprs[0];
729729
730 switch (lhs) {
731 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
732 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
733 }
734 try renderToken(tree, ais, lbrace, Space.None);730 try renderToken(tree, ais, lbrace, Space.None);
735 try renderExpression(allocator, ais, tree, expr, Space.None);731 try renderExpression(allocator, ais, tree, expr, Space.None);
736 return renderToken(tree, ais, rtoken, space);732 return renderToken(tree, ais, rtoken, space);
737 }733 }
738734
739 switch (lhs) {
740 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
741 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
742 }
743
744 // scan to find row size735 // scan to find row size
745 const maybe_row_size: ?usize = blk: {736 if (rowSize(tree, exprs, rtoken) != null) {
746 var count: usize = 1;
747 for (exprs) |expr, i| {
748 if (i + 1 < exprs.len) {
749 const expr_last_token = expr.lastToken() + 1;
750 const loc = tree.tokenLocation(tree.token_locs[expr_last_token].end, exprs[i + 1].firstToken());
751 if (loc.line != 0) break :blk count;
752 count += 1;
753 } else {
754 const expr_last_token = expr.lastToken();
755 const loc = tree.tokenLocation(tree.token_locs[expr_last_token].end, rtoken);
756 if (loc.line == 0) {
757 // all on one line
758 const src_has_trailing_comma = trailblk: {
759 const maybe_comma = tree.prevToken(rtoken);
760 break :trailblk tree.token_ids[maybe_comma] == .Comma;
761 };
762 if (src_has_trailing_comma) {
763 break :blk 1; // force row size 1
764 } else {
765 break :blk null; // no newlines
766 }
767 }
768 break :blk count;
769 }
770 }
771 unreachable;
772 };
773
774 if (maybe_row_size) |row_size| {
775 // A place to store the width of each expression and its column's maximum
776 var widths = try allocator.alloc(usize, exprs.len + row_size);
777 defer allocator.free(widths);
778 mem.set(usize, widths, 0);
779
780 var expr_widths = widths[0 .. widths.len - row_size];
781 var column_widths = widths[widths.len - row_size ..];
782
783 // Null ais for counting the printed length of each expression
784 var counting_stream = std.io.countingOutStream(std.io.null_out_stream);
785 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, counting_stream.writer());
786
787 for (exprs) |expr, i| {
788 counting_stream.bytes_written = 0;
789 try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
790 const width = @intCast(usize, counting_stream.bytes_written);
791 const col = i % row_size;
792 column_widths[col] = std.math.max(column_widths[col], width);
793 expr_widths[i] = width;
794 }
795
796 {737 {
797 ais.pushIndentNextLine();738 ais.pushIndentNextLine();
798 defer ais.popIndent();739 defer ais.popIndent();
799 try renderToken(tree, ais, lbrace, Space.Newline);740 try renderToken(tree, ais, lbrace, Space.Newline);
800741
801 var col: usize = 1;742 var expr_index: usize = 0;
802 for (exprs) |expr, i| {743 while (rowSize(tree, exprs[expr_index..], rtoken)) |row_size| {
803 if (i + 1 < exprs.len) {744 const row_exprs = exprs[expr_index..];
804 const next_expr = exprs[i + 1];745 // A place to store the width of each expression and its column's maximum
805 try renderExpression(allocator, ais, tree, expr, Space.None);746 var widths = try allocator.alloc(usize, row_exprs.len + row_size);
806747 defer allocator.free(widths);
807 const comma = tree.nextToken(expr.*.lastToken());748 mem.set(usize, widths, 0);
808749
809 if (col != row_size) {750 var expr_newlines = try allocator.alloc(bool, row_exprs.len);
810 try renderToken(tree, ais, comma, Space.Space); // ,751 defer allocator.free(expr_newlines);
811752 mem.set(bool, expr_newlines, false);
812 const padding = column_widths[i % row_size] - expr_widths[i];753
813 try ais.writer().writeByteNTimes(' ', padding);754 var expr_widths = widths[0 .. widths.len - row_size];
814755 var column_widths = widths[widths.len - row_size ..];
815 col += 1;756
816 continue;757 // Find next row with trailing comment (if any) to end the current section
758 var section_end = sec_end: {
759 var this_line_first_expr: usize = 0;
760 var this_line_size = rowSize(tree, row_exprs, rtoken);
761 for (row_exprs) |expr, i| {
762 // Ignore comment on first line of this section
763 if (i == 0 or tree.tokensOnSameLine(row_exprs[0].firstToken(), expr.lastToken())) continue;
764 // Track start of line containing comment
765 if (!tree.tokensOnSameLine(row_exprs[this_line_first_expr].firstToken(), expr.lastToken())) {
766 this_line_first_expr = i;
767 this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rtoken);
768 }
769
770 const maybe_comma = expr.lastToken() + 1;
771 const maybe_comment = expr.lastToken() + 2;
772 if (maybe_comment < tree.token_ids.len) {
773 if (tree.token_ids[maybe_comma] == .Comma and
774 tree.token_ids[maybe_comment] == .LineComment and
775 tree.tokensOnSameLine(expr.lastToken(), maybe_comment))
776 {
777 var comment_token_loc = tree.token_locs[maybe_comment];
778 const comment_is_empty = mem.trimRight(u8, tree.tokenSliceLoc(comment_token_loc), " ").len == 2;
779 if (!comment_is_empty) {
780 // Found row ending in comment
781 break :sec_end i - this_line_size.? + 1;
782 }
783 }
784 }
817 }785 }
818 col = 1;786 break :sec_end row_exprs.len;
787 };
788 expr_index += section_end;
789
790 const section_exprs = row_exprs[0..section_end];
791
792 // Null stream for counting the printed length of each expression
793 var line_find_stream = std.io.findByteOutStream('\n', std.io.null_out_stream);
794 var counting_stream = std.io.countingOutStream(line_find_stream.writer());
795 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, counting_stream.writer());
796
797 // Calculate size of columns in current section
798 var column_counter: usize = 0;
799 var single_line = true;
800 for (section_exprs) |expr, i| {
801 if (i + 1 < section_exprs.len) {
802 counting_stream.bytes_written = 0;
803 line_find_stream.byte_found = false;
804 try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
805 const width = @intCast(usize, counting_stream.bytes_written);
806 expr_widths[i] = width;
807 expr_newlines[i] = line_find_stream.byte_found;
808
809 if (!line_find_stream.byte_found) {
810 const column = column_counter % row_size;
811 column_widths[column] = std.math.max(column_widths[column], width);
812
813 const expr_last_token = expr.*.lastToken() + 1;
814 const next_expr = section_exprs[i + 1];
815 const loc = tree.tokenLocation(tree.token_locs[expr_last_token].start, next_expr.*.firstToken());
816 if (loc.line == 0) {
817 column_counter += 1;
818 } else {
819 single_line = false;
820 column_counter = 0;
821 }
822 } else {
823 single_line = false;
824 column_counter = 0;
825 }
826 } else {
827 counting_stream.bytes_written = 0;
828 try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
829 const width = @intCast(usize, counting_stream.bytes_written);
830 expr_widths[i] = width;
831 expr_newlines[i] = line_find_stream.byte_found;
832
833 if (!line_find_stream.byte_found) {
834 const column = column_counter % row_size;
835 column_widths[column] = std.math.max(column_widths[column], width);
836 }
837 break;
838 }
839 }
819840
820 if (tree.token_ids[tree.nextToken(comma)] != .MultilineStringLiteralLine) {841 // Render exprs in current section
842 column_counter = 0;
843 var last_col_index: usize = row_size - 1;
844 for (section_exprs) |expr, i| {
845 if (i + 1 < section_exprs.len) {
846 const next_expr = section_exprs[i + 1];
847 try renderExpression(allocator, ais, tree, expr, Space.None);
848
849 const comma = tree.nextToken(expr.*.lastToken());
850
851 if (column_counter != last_col_index) {
852 if (!expr_newlines[i] and !expr_newlines[i + 1]) {
853 // Neither the current or next expression is multiline
854 try renderToken(tree, ais, comma, Space.Space); // ,
855 assert(column_widths[column_counter % row_size] >= expr_widths[i]);
856 const padding = column_widths[column_counter % row_size] - expr_widths[i];
857 try ais.writer().writeByteNTimes(' ', padding);
858
859 column_counter += 1;
860 continue;
861 }
862 }
863 if (single_line and row_size != 1) {
864 try renderToken(tree, ais, comma, Space.Space); // ,
865 continue;
866 }
867
868 column_counter = 0;
821 try renderToken(tree, ais, comma, Space.Newline); // ,869 try renderToken(tree, ais, comma, Space.Newline); // ,
870 try renderExtraNewline(tree, ais, next_expr);
822 } else {871 } else {
823 try renderToken(tree, ais, comma, Space.None); // ,872 const maybe_comma = tree.nextToken(expr.*.lastToken());
873 if (tree.token_ids[maybe_comma] == .Comma) {
874 try renderExpression(allocator, ais, tree, expr, Space.None); // ,
875 try renderToken(tree, ais, maybe_comma, Space.Newline); // ,
876 } else {
877 try renderExpression(allocator, ais, tree, expr, Space.Comma); // ,
878 }
824 }879 }
880 }
825881
826 try renderExtraNewline(tree, ais, next_expr);882 if (expr_index == exprs.len) {
827 } else {883 break;
828 try renderExpression(allocator, ais, tree, expr, Space.Comma); // ,
829 }884 }
830 }885 }
831 }886 }
832 return renderToken(tree, ais, rtoken, space);
833 } else {
834 try renderToken(tree, ais, lbrace, Space.Space);
835 for (exprs) |expr, i| {
836 if (i + 1 < exprs.len) {
837 const next_expr = exprs[i + 1];
838 try renderExpression(allocator, ais, tree, expr, Space.None);
839 const comma = tree.nextToken(expr.*.lastToken());
840 try renderToken(tree, ais, comma, Space.Space); // ,
841 } else {
842 try renderExpression(allocator, ais, tree, expr, Space.Space);
843 }
844 }
845887
846 return renderToken(tree, ais, rtoken, space);888 return renderToken(tree, ais, rtoken, space);
847 }889 }
890
891 // Single line
892 try renderToken(tree, ais, lbrace, Space.Space);
893 for (exprs) |expr, i| {
894 if (i + 1 < exprs.len) {
895 const next_expr = exprs[i + 1];
896 try renderExpression(allocator, ais, tree, expr, Space.None);
897 const comma = tree.nextToken(expr.*.lastToken());
898 try renderToken(tree, ais, comma, Space.Space); // ,
899 } else {
900 try renderExpression(allocator, ais, tree, expr, Space.Space);
901 }
902 }
903
904 return renderToken(tree, ais, rtoken, space);
848 },905 },
849906
850 .StructInitializer, .StructInitializerDot => {907 .StructInitializer, .StructInitializerDot => {
...@@ -1004,21 +1061,29 @@ fn renderExpression(...@@ -1004,21 +1061,29 @@ fn renderExpression(
1004 };1061 };
10051062
1006 if (src_has_trailing_comma) {1063 if (src_has_trailing_comma) {
1007 try renderToken(tree, ais, lparen, Space.Newline);1064 {
1008
1009 const params = call.params();
1010 for (params) |param_node, i| {
1011 ais.pushIndent();1065 ais.pushIndent();
1012 defer ais.popIndent();1066 defer ais.popIndent();
10131067
1014 if (i + 1 < params.len) {1068 try renderToken(tree, ais, lparen, Space.Newline); // (
1015 const next_node = params[i + 1];1069 const params = call.params();
1016 try renderExpression(allocator, ais, tree, param_node, Space.None);1070 for (params) |param_node, i| {
1017 const comma = tree.nextToken(param_node.lastToken());1071 if (i + 1 < params.len) {
1018 try renderToken(tree, ais, comma, Space.Newline); // ,1072 const next_node = params[i + 1];
1019 try renderExtraNewline(tree, ais, next_node);1073 try renderExpression(allocator, ais, tree, param_node, Space.None);
1020 } else {1074
1021 try renderExpression(allocator, ais, tree, param_node, Space.Comma);1075 // Unindent the comma for multiline string literals
1076 const maybe_multiline_string = param_node.firstToken();
1077 const is_multiline_string = tree.token_ids[maybe_multiline_string] == .MultilineStringLiteralLine;
1078 if (is_multiline_string) ais.popIndent();
1079 defer if (is_multiline_string) ais.pushIndent();
1080
1081 const comma = tree.nextToken(param_node.lastToken());
1082 try renderToken(tree, ais, comma, Space.Newline); // ,
1083 try renderExtraNewline(tree, ais, next_node);
1084 } else {
1085 try renderExpression(allocator, ais, tree, param_node, Space.Comma);
1086 }
1022 }1087 }
1023 }1088 }
1024 return renderToken(tree, ais, call.rtoken, space);1089 return renderToken(tree, ais, call.rtoken, space);
...@@ -1028,17 +1093,20 @@ fn renderExpression(...@@ -1028,17 +1093,20 @@ fn renderExpression(
10281093
1029 const params = call.params();1094 const params = call.params();
1030 for (params) |param_node, i| {1095 for (params) |param_node, i| {
1031 if (param_node.*.tag == .MultilineStringLiteral) ais.pushIndentOneShot();1096 const maybe_comment = param_node.firstToken() - 1;
1097 const maybe_multiline_string = param_node.firstToken();
1098 if (tree.token_ids[maybe_multiline_string] == .MultilineStringLiteralLine or tree.token_ids[maybe_comment] == .LineComment) {
1099 ais.pushIndentOneShot();
1100 }
10321101
1033 try renderExpression(allocator, ais, tree, param_node, Space.None);1102 try renderExpression(allocator, ais, tree, param_node, Space.None);
10341103
1035 if (i + 1 < params.len) {1104 if (i + 1 < params.len) {
1036 const next_param = params[i + 1];
1037 const comma = tree.nextToken(param_node.lastToken());1105 const comma = tree.nextToken(param_node.lastToken());
1038 try renderToken(tree, ais, comma, Space.Space);1106 try renderToken(tree, ais, comma, Space.Space);
1039 }1107 }
1040 }1108 }
1041 return renderToken(tree, ais, call.rtoken, space);1109 return renderToken(tree, ais, call.rtoken, space); // )
1042 },1110 },
10431111
1044 .ArrayAccess => {1112 .ArrayAccess => {
...@@ -1429,7 +1497,7 @@ fn renderExpression(...@@ -1429,7 +1497,7 @@ fn renderExpression(
1429 try renderToken(tree, ais, builtin_call.builtin_token, Space.None); // @name1497 try renderToken(tree, ais, builtin_call.builtin_token, Space.None); // @name
14301498
1431 const src_params_trailing_comma = blk: {1499 const src_params_trailing_comma = blk: {
1432 if (builtin_call.params_len < 2) break :blk false;1500 if (builtin_call.params_len == 0) break :blk false;
1433 const last_node = builtin_call.params()[builtin_call.params_len - 1];1501 const last_node = builtin_call.params()[builtin_call.params_len - 1];
1434 const maybe_comma = tree.nextToken(last_node.lastToken());1502 const maybe_comma = tree.nextToken(last_node.lastToken());
1435 break :blk tree.token_ids[maybe_comma] == .Comma;1503 break :blk tree.token_ids[maybe_comma] == .Comma;
...@@ -1443,6 +1511,10 @@ fn renderExpression(...@@ -1443,6 +1511,10 @@ fn renderExpression(
1443 // render all on one line, no trailing comma1511 // render all on one line, no trailing comma
1444 const params = builtin_call.params();1512 const params = builtin_call.params();
1445 for (params) |param_node, i| {1513 for (params) |param_node, i| {
1514 const maybe_comment = param_node.firstToken() - 1;
1515 if (param_node.*.tag == .MultilineStringLiteral or tree.token_ids[maybe_comment] == .LineComment) {
1516 ais.pushIndentOneShot();
1517 }
1446 try renderExpression(allocator, ais, tree, param_node, Space.None);1518 try renderExpression(allocator, ais, tree, param_node, Space.None);
14471519
1448 if (i + 1 < params.len) {1520 if (i + 1 < params.len) {
...@@ -1494,19 +1566,20 @@ fn renderExpression(...@@ -1494,19 +1566,20 @@ fn renderExpression(
1494 assert(tree.token_ids[lparen] == .LParen);1566 assert(tree.token_ids[lparen] == .LParen);
14951567
1496 const rparen = tree.prevToken(1568 const rparen = tree.prevToken(
1497 // the first token for the annotation expressions is the left1569 // the first token for the annotation expressions is the left
1498 // parenthesis, hence the need for two prevToken1570 // parenthesis, hence the need for two prevToken
1499 if (fn_proto.getAlignExpr()) |align_expr|1571 if (fn_proto.getAlignExpr()) |align_expr|
1500 tree.prevToken(tree.prevToken(align_expr.firstToken()))1572 tree.prevToken(tree.prevToken(align_expr.firstToken()))
1501 else if (fn_proto.getSectionExpr()) |section_expr|1573 else if (fn_proto.getSectionExpr()) |section_expr|
1502 tree.prevToken(tree.prevToken(section_expr.firstToken()))1574 tree.prevToken(tree.prevToken(section_expr.firstToken()))
1503 else if (fn_proto.getCallconvExpr()) |callconv_expr|1575 else if (fn_proto.getCallconvExpr()) |callconv_expr|
1504 tree.prevToken(tree.prevToken(callconv_expr.firstToken()))1576 tree.prevToken(tree.prevToken(callconv_expr.firstToken()))
1505 else switch (fn_proto.return_type) {1577 else switch (fn_proto.return_type) {
1506 .Explicit => |node| node.firstToken(),1578 .Explicit => |node| node.firstToken(),
1507 .InferErrorSet => |node| tree.prevToken(node.firstToken()),1579 .InferErrorSet => |node| tree.prevToken(node.firstToken()),
1508 .Invalid => unreachable,1580 .Invalid => unreachable,
1509 });1581 },
1582 );
1510 assert(tree.token_ids[rparen] == .RParen);1583 assert(tree.token_ids[rparen] == .RParen);
15111584
1512 const src_params_trailing_comma = blk: {1585 const src_params_trailing_comma = blk: {
...@@ -1758,7 +1831,7 @@ fn renderExpression(...@@ -1758,7 +1831,7 @@ fn renderExpression(
1758 }1831 }
17591832
1760 if (while_node.payload) |payload| {1833 if (while_node.payload) |payload| {
1761 const payload_space = Space.Space; //if (while_node.continue_expr != null) Space.Space else block_start_space;1834 const payload_space = if (while_node.continue_expr != null) Space.Space else block_start_space;
1762 try renderExpression(allocator, ais, tree, payload, payload_space);1835 try renderExpression(allocator, ais, tree, payload, payload_space);
1763 }1836 }
17641837
...@@ -1873,7 +1946,12 @@ fn renderExpression(...@@ -1873,7 +1946,12 @@ fn renderExpression(
18731946
1874 if (src_has_newline) {1947 if (src_has_newline) {
1875 const after_rparen_space = if (if_node.payload == null) Space.Newline else Space.Space;1948 const after_rparen_space = if (if_node.payload == null) Space.Newline else Space.Space;
1876 try renderToken(tree, ais, rparen, after_rparen_space); // )1949
1950 {
1951 ais.pushIndent();
1952 defer ais.popIndent();
1953 try renderToken(tree, ais, rparen, after_rparen_space); // )
1954 }
18771955
1878 if (if_node.payload) |payload| {1956 if (if_node.payload) |payload| {
1879 try renderExpression(allocator, ais, tree, payload, Space.Newline);1957 try renderExpression(allocator, ais, tree, payload, Space.Newline);
...@@ -2558,3 +2636,27 @@ fn copyFixingWhitespace(ais: anytype, slice: []const u8) @TypeOf(ais.*).Error!vo...@@ -2558,3 +2636,27 @@ fn copyFixingWhitespace(ais: anytype, slice: []const u8) @TypeOf(ais.*).Error!vo
2558 else => try ais.writer().writeByte(byte),2636 else => try ais.writer().writeByte(byte),
2559 };2637 };
2560}2638}
2639
2640fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize {
2641 const first_token = exprs[0].firstToken();
2642 const first_loc = tree.tokenLocation(tree.token_locs[first_token].start, rtoken);
2643 if (first_loc.line == 0) {
2644 const maybe_comma = tree.prevToken(rtoken);
2645 if (tree.token_ids[maybe_comma] == .Comma)
2646 return 1;
2647 return null; // no newlines
2648 }
2649
2650 var count: usize = 1;
2651 for (exprs) |expr, i| {
2652 if (i + 1 < exprs.len) {
2653 const expr_last_token = expr.lastToken() + 1;
2654 const loc = tree.tokenLocation(tree.token_locs[expr_last_token].start, exprs[i + 1].firstToken());
2655 if (loc.line != 0) return count;
2656 count += 1;
2657 } else {
2658 return count;
2659 }
2660 }
2661 unreachable;
2662}
lib/std/zig/tokenizer.zig+9
...@@ -1195,6 +1195,7 @@ pub const Tokenizer = struct {...@@ -1195,6 +1195,7 @@ pub const Tokenizer = struct {
1195 },1195 },
1196 .num_dot_hex => switch (c) {1196 .num_dot_hex => switch (c) {
1197 '.' => {1197 '.' => {
1198 result.id = .IntegerLiteral;
1198 self.index -= 1;1199 self.index -= 1;
1199 state = .start;1200 state = .start;
1200 break;1201 break;
...@@ -1758,6 +1759,14 @@ test "correctly parse pointer assignment" {...@@ -1758,6 +1759,14 @@ test "correctly parse pointer assignment" {
1758 });1759 });
1759}1760}
17601761
1762test "tokenizer - range literals" {
1763 testTokenize("0...9", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
1764 testTokenize("'0'...'9'", &[_]Token.Id{ .CharLiteral, .Ellipsis3, .CharLiteral });
1765 testTokenize("0x00...0x09", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
1766 testTokenize("0b00...0b11", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
1767 testTokenize("0o00...0o11", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
1768}
1769
1761test "tokenizer - number literals decimal" {1770test "tokenizer - number literals decimal" {
1762 testTokenize("0", &[_]Token.Id{.IntegerLiteral});1771 testTokenize("0", &[_]Token.Id{.IntegerLiteral});
1763 testTokenize("1", &[_]Token.Id{.IntegerLiteral});1772 testTokenize("1", &[_]Token.Id{.IntegerLiteral});
src/tokenizer.cpp+3-3
...@@ -1225,9 +1225,6 @@ void tokenize(Buf *buf, Tokenization *out) {...@@ -1225,9 +1225,6 @@ void tokenize(Buf *buf, Tokenization *out) {
1225 invalid_char_error(&t, c);1225 invalid_char_error(&t, c);
1226 break;1226 break;
1227 }1227 }
1228 if (t.radix != 16 && t.radix != 10) {
1229 invalid_char_error(&t, c);
1230 }
1231 t.state = TokenizeStateNumberDot;1228 t.state = TokenizeStateNumberDot;
1232 break;1229 break;
1233 }1230 }
...@@ -1281,6 +1278,9 @@ void tokenize(Buf *buf, Tokenization *out) {...@@ -1281,6 +1278,9 @@ void tokenize(Buf *buf, Tokenization *out) {
1281 t.state = TokenizeStateStart;1278 t.state = TokenizeStateStart;
1282 continue;1279 continue;
1283 }1280 }
1281 if (t.radix != 16 && t.radix != 10) {
1282 invalid_char_error(&t, c);
1283 }
1284 t.pos -= 1;1284 t.pos -= 1;
1285 t.state = TokenizeStateFloatFractionNoUnderscore;1285 t.state = TokenizeStateFloatFractionNoUnderscore;
1286 assert(t.cur_tok->id == TokenIdIntLiteral);1286 assert(t.cur_tok->id == TokenIdIntLiteral);