authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-02 00:41:19-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-02 00:41:19-04:00
log2272a07ca0661547a6889aa7f1b6262fef5dad85
treec8e093b7c72ac3e1fc2e62931167294941becc7a
parent45bce27b8fecda4fba1c22dd191030af29ccbc6f

std.event.Loop: promote the fs thread to be available for all OS's


6 files changed, 137 insertions(+), 193 deletions(-)

lib/std/debug.zig+48-42
...@@ -112,39 +112,43 @@ pub fn detectTTYConfig() TTY.Config {...@@ -112,39 +112,43 @@ pub fn detectTTYConfig() TTY.Config {
112/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.112/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
113/// TODO multithreaded awareness113/// TODO multithreaded awareness
114pub fn dumpCurrentStackTrace(start_addr: ?usize) void {114pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
115 const stderr = getStderrStream();115 noasync {
116 if (builtin.strip_debug_info) {116 const stderr = getStderrStream();
117 noasync stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;117 if (builtin.strip_debug_info) {
118 return;118 stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
119 return;
120 }
121 const debug_info = getSelfDebugInfo() catch |err| {
122 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return;
123 return;
124 };
125 writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(), start_addr) catch |err| {
126 stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return;
127 return;
128 };
119 }129 }
120 const debug_info = getSelfDebugInfo() catch |err| {
121 noasync stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return;
122 return;
123 };
124 writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(), start_addr) catch |err| {
125 noasync stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return;
126 return;
127 };
128}130}
129131
130/// Tries to print the stack trace starting from the supplied base pointer to stderr,132/// Tries to print the stack trace starting from the supplied base pointer to stderr,
131/// unbuffered, and ignores any error returned.133/// unbuffered, and ignores any error returned.
132/// TODO multithreaded awareness134/// TODO multithreaded awareness
133pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {135pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
134 const stderr = getStderrStream();136 noasync {
135 if (builtin.strip_debug_info) {137 const stderr = getStderrStream();
136 noasync stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;138 if (builtin.strip_debug_info) {
137 return;139 stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
138 }140 return;
139 const debug_info = getSelfDebugInfo() catch |err| {141 }
140 noasync stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return;142 const debug_info = getSelfDebugInfo() catch |err| {
141 return;143 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return;
142 };144 return;
143 const tty_config = detectTTYConfig();145 };
144 printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return;146 const tty_config = detectTTYConfig();
145 var it = StackIterator.init(null, bp);147 printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return;
146 while (it.next()) |return_address| {148 var it = StackIterator.init(null, bp);
147 printSourceAtAddress(debug_info, stderr, return_address - 1, tty_config) catch return;149 while (it.next()) |return_address| {
150 printSourceAtAddress(debug_info, stderr, return_address - 1, tty_config) catch return;
151 }
148 }152 }
149}153}
150154
...@@ -199,19 +203,21 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace...@@ -199,19 +203,21 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace
199/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.203/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
200/// TODO multithreaded awareness204/// TODO multithreaded awareness
201pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void {205pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void {
202 const stderr = getStderrStream();206 noasync {
203 if (builtin.strip_debug_info) {207 const stderr = getStderrStream();
204 noasync stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;208 if (builtin.strip_debug_info) {
205 return;209 stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
210 return;
211 }
212 const debug_info = getSelfDebugInfo() catch |err| {
213 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return;
214 return;
215 };
216 writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, detectTTYConfig()) catch |err| {
217 stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return;
218 return;
219 };
206 }220 }
207 const debug_info = getSelfDebugInfo() catch |err| {
208 noasync stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return;
209 return;
210 };
211 writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, detectTTYConfig()) catch |err| {
212 noasync stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return;
213 return;
214 };
215}221}
216222
217/// This function invokes undefined behavior when `ok` is `false`.223/// This function invokes undefined behavior when `ok` is `false`.
...@@ -255,7 +261,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c...@@ -255,7 +261,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
255 resetSegfaultHandler();261 resetSegfaultHandler();
256 }262 }
257263
258 switch (panic_stage) {264 noasync switch (panic_stage) {
259 0 => {265 0 => {
260 panic_stage = 1;266 panic_stage = 1;
261267
...@@ -267,7 +273,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c...@@ -267,7 +273,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
267 defer held.release();273 defer held.release();
268274
269 const stderr = getStderrStream();275 const stderr = getStderrStream();
270 noasync stderr.print(format ++ "\n", args) catch os.abort();276 stderr.print(format ++ "\n", args) catch os.abort();
271 if (trace) |t| {277 if (trace) |t| {
272 dumpStackTrace(t.*);278 dumpStackTrace(t.*);
273 }279 }
...@@ -292,12 +298,12 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c...@@ -292,12 +298,12 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
292 // we're still holding the mutex but that's fine as we're going to298 // we're still holding the mutex but that's fine as we're going to
293 // call abort()299 // call abort()
294 const stderr = getStderrStream();300 const stderr = getStderrStream();
295 noasync stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();301 stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();
296 },302 },
297 else => {303 else => {
298 // Panicked while printing "Panicked during a panic."304 // Panicked while printing "Panicked during a panic."
299 },305 },
300 }306 };
301307
302 os.abort();308 os.abort();
303}309}
lib/std/event/loop.zig+69-144
...@@ -4,19 +4,27 @@ const root = @import("root");...@@ -4,19 +4,27 @@ const root = @import("root");
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const testing = std.testing;5const testing = std.testing;
6const mem = std.mem;6const mem = std.mem;
7const AtomicRmwOp = builtin.AtomicRmwOp;
8const AtomicOrder = builtin.AtomicOrder;
9const os = std.os;7const os = std.os;
10const windows = os.windows;8const windows = os.windows;
11const maxInt = std.math.maxInt;9const maxInt = std.math.maxInt;
12const Thread = std.Thread;10const Thread = std.Thread;
1311
12const is_windows = std.Target.current.os.tag == .windows;
13
14pub const Loop = struct {14pub const Loop = struct {
15 next_tick_queue: std.atomic.Queue(anyframe),15 next_tick_queue: std.atomic.Queue(anyframe),
16 os_data: OsData,16 os_data: OsData,
17 final_resume_node: ResumeNode,17 final_resume_node: ResumeNode,
18 pending_event_count: usize,18 pending_event_count: usize,
19 extra_threads: []*Thread,19 extra_threads: []*Thread,
20 /// TODO change this to a pool of configurable number of threads
21 /// and rename it to be not file-system-specific. it will become
22 /// a thread pool for turning non-CPU-bound blocking things into
23 /// async things. A fallback for any missing OS-specific API.
24 fs_thread: *Thread,
25 fs_queue: std.atomic.Queue(Request),
26 fs_end_request: Request.Node,
27 fs_thread_wakeup: std.ResetEvent,
2028
21 /// For resources that have the same lifetime as the `Loop`.29 /// For resources that have the same lifetime as the `Loop`.
22 /// This is only used by `Loop` for the thread pool and associated resources.30 /// This is only used by `Loop` for the thread pool and associated resources.
...@@ -143,7 +151,12 @@ pub const Loop = struct {...@@ -143,7 +151,12 @@ pub const Loop = struct {
143 .handle = undefined,151 .handle = undefined,
144 .overlapped = ResumeNode.overlapped_init,152 .overlapped = ResumeNode.overlapped_init,
145 },153 },
154 .fs_end_request = .{ .data = .{ .msg = .end, .finish = .NoAction } },
155 .fs_queue = std.atomic.Queue(Request).init(),
156 .fs_thread = undefined,
157 .fs_thread_wakeup = std.ResetEvent.init(),
146 };158 };
159 errdefer self.fs_thread_wakeup.deinit();
147 errdefer self.arena.deinit();160 errdefer self.arena.deinit();
148161
149 // We need at least one of these in case the fs thread wants to use onNextTick162 // We need at least one of these in case the fs thread wants to use onNextTick
...@@ -158,10 +171,19 @@ pub const Loop = struct {...@@ -158,10 +171,19 @@ pub const Loop = struct {
158171
159 try self.initOsData(extra_thread_count);172 try self.initOsData(extra_thread_count);
160 errdefer self.deinitOsData();173 errdefer self.deinitOsData();
174
175 if (!builtin.single_threaded) {
176 self.fs_thread = try Thread.spawn(self, posixFsRun);
177 }
178 errdefer if (!builtin.single_threaded) {
179 self.posixFsRequest(&self.fs_end_request);
180 self.fs_thread.wait();
181 };
161 }182 }
162183
163 pub fn deinit(self: *Loop) void {184 pub fn deinit(self: *Loop) void {
164 self.deinitOsData();185 self.deinitOsData();
186 self.fs_thread_wakeup.deinit();
165 self.arena.deinit();187 self.arena.deinit();
166 self.* = undefined;188 self.* = undefined;
167 }189 }
...@@ -173,21 +195,10 @@ pub const Loop = struct {...@@ -173,21 +195,10 @@ pub const Loop = struct {
173 const wakeup_bytes = [_]u8{0x1} ** 8;195 const wakeup_bytes = [_]u8{0x1} ** 8;
174196
175 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {197 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {
176 switch (builtin.os.tag) {198 noasync switch (builtin.os.tag) {
177 .linux => {199 .linux => {
178 self.os_data.fs_queue = std.atomic.Queue(Request).init();
179 self.os_data.fs_queue_item = 0;
180 // we need another thread for the file system because Linux does not have an async
181 // file system I/O API.
182 self.os_data.fs_end_request = Request.Node{
183 .data = Request{
184 .msg = .end,
185 .finish = .NoAction,
186 },
187 };
188
189 errdefer {200 errdefer {
190 while (self.available_eventfd_resume_nodes.pop()) |node| noasync os.close(node.data.eventfd);201 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);
191 }202 }
192 for (self.eventfd_resume_nodes) |*eventfd_node| {203 for (self.eventfd_resume_nodes) |*eventfd_node| {
193 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{204 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
...@@ -206,10 +217,10 @@ pub const Loop = struct {...@@ -206,10 +217,10 @@ pub const Loop = struct {
206 }217 }
207218
208 self.os_data.epollfd = try os.epoll_create1(os.EPOLL_CLOEXEC);219 self.os_data.epollfd = try os.epoll_create1(os.EPOLL_CLOEXEC);
209 errdefer noasync os.close(self.os_data.epollfd);220 errdefer os.close(self.os_data.epollfd);
210221
211 self.os_data.final_eventfd = try os.eventfd(0, os.EFD_CLOEXEC | os.EFD_NONBLOCK);222 self.os_data.final_eventfd = try os.eventfd(0, os.EFD_CLOEXEC | os.EFD_NONBLOCK);
212 errdefer noasync os.close(self.os_data.final_eventfd);223 errdefer os.close(self.os_data.final_eventfd);
213224
214 self.os_data.final_eventfd_event = os.epoll_event{225 self.os_data.final_eventfd_event = os.epoll_event{
215 .events = os.EPOLLIN,226 .events = os.EPOLLIN,
...@@ -222,12 +233,6 @@ pub const Loop = struct {...@@ -222,12 +233,6 @@ pub const Loop = struct {
222 &self.os_data.final_eventfd_event,233 &self.os_data.final_eventfd_event,
223 );234 );
224235
225 self.os_data.fs_thread = try Thread.spawn(self, posixFsRun);
226 errdefer {
227 self.posixFsRequest(&self.os_data.fs_end_request);
228 self.os_data.fs_thread.wait();
229 }
230
231 if (builtin.single_threaded) {236 if (builtin.single_threaded) {
232 assert(extra_thread_count == 0);237 assert(extra_thread_count == 0);
233 return;238 return;
...@@ -236,7 +241,7 @@ pub const Loop = struct {...@@ -236,7 +241,7 @@ pub const Loop = struct {
236 var extra_thread_index: usize = 0;241 var extra_thread_index: usize = 0;
237 errdefer {242 errdefer {
238 // writing 8 bytes to an eventfd cannot fail243 // writing 8 bytes to an eventfd cannot fail
239 const amt = noasync os.write(self.os_data.final_eventfd, &wakeup_bytes) catch unreachable;244 const amt = os.write(self.os_data.final_eventfd, &wakeup_bytes) catch unreachable;
240 assert(amt == wakeup_bytes.len);245 assert(amt == wakeup_bytes.len);
241 while (extra_thread_index != 0) {246 while (extra_thread_index != 0) {
242 extra_thread_index -= 1;247 extra_thread_index -= 1;
...@@ -249,22 +254,7 @@ pub const Loop = struct {...@@ -249,22 +254,7 @@ pub const Loop = struct {
249 },254 },
250 .macosx, .freebsd, .netbsd, .dragonfly => {255 .macosx, .freebsd, .netbsd, .dragonfly => {
251 self.os_data.kqfd = try os.kqueue();256 self.os_data.kqfd = try os.kqueue();
252 errdefer noasync os.close(self.os_data.kqfd);257 errdefer os.close(self.os_data.kqfd);
253
254 self.os_data.fs_kqfd = try os.kqueue();
255 errdefer noasync os.close(self.os_data.fs_kqfd);
256
257 self.os_data.fs_queue = std.atomic.Queue(Request).init();
258 // we need another thread for the file system because Darwin does not have an async
259 // file system I/O API.
260 self.os_data.fs_end_request = Request.Node{
261 .prev = undefined,
262 .next = undefined,
263 .data = Request{
264 .msg = .end,
265 .finish = .NoAction,
266 },
267 };
268258
269 const empty_kevs = &[0]os.Kevent{};259 const empty_kevs = &[0]os.Kevent{};
270260
...@@ -310,30 +300,6 @@ pub const Loop = struct {...@@ -310,30 +300,6 @@ pub const Loop = struct {
310 self.os_data.final_kevent.flags = os.EV_ENABLE;300 self.os_data.final_kevent.flags = os.EV_ENABLE;
311 self.os_data.final_kevent.fflags = os.NOTE_TRIGGER;301 self.os_data.final_kevent.fflags = os.NOTE_TRIGGER;
312302
313 self.os_data.fs_kevent_wake = os.Kevent{
314 .ident = 0,
315 .filter = os.EVFILT_USER,
316 .flags = os.EV_ADD | os.EV_ENABLE,
317 .fflags = os.NOTE_TRIGGER,
318 .data = 0,
319 .udata = undefined,
320 };
321
322 self.os_data.fs_kevent_wait = os.Kevent{
323 .ident = 0,
324 .filter = os.EVFILT_USER,
325 .flags = os.EV_ADD | os.EV_CLEAR,
326 .fflags = 0,
327 .data = 0,
328 .udata = undefined,
329 };
330
331 self.os_data.fs_thread = try Thread.spawn(self, posixFsRun);
332 errdefer {
333 self.posixFsRequest(&self.os_data.fs_end_request);
334 self.os_data.fs_thread.wait();
335 }
336
337 if (builtin.single_threaded) {303 if (builtin.single_threaded) {
338 assert(extra_thread_count == 0);304 assert(extra_thread_count == 0);
339 return;305 return;
...@@ -401,25 +367,24 @@ pub const Loop = struct {...@@ -401,25 +367,24 @@ pub const Loop = struct {
401 }367 }
402 },368 },
403 else => {},369 else => {},
404 }370 };
405 }371 }
406372
407 fn deinitOsData(self: *Loop) void {373 fn deinitOsData(self: *Loop) void {
408 switch (builtin.os.tag) {374 noasync switch (builtin.os.tag) {
409 .linux => {375 .linux => {
410 noasync os.close(self.os_data.final_eventfd);376 os.close(self.os_data.final_eventfd);
411 while (self.available_eventfd_resume_nodes.pop()) |node| noasync os.close(node.data.eventfd);377 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);
412 noasync os.close(self.os_data.epollfd);378 os.close(self.os_data.epollfd);
413 },379 },
414 .macosx, .freebsd, .netbsd, .dragonfly => {380 .macosx, .freebsd, .netbsd, .dragonfly => {
415 noasync os.close(self.os_data.kqfd);381 os.close(self.os_data.kqfd);
416 noasync os.close(self.os_data.fs_kqfd);
417 },382 },
418 .windows => {383 .windows => {
419 windows.CloseHandle(self.os_data.io_port);384 windows.CloseHandle(self.os_data.io_port);
420 },385 },
421 else => {},386 else => {},
422 }387 };
423 }388 }
424389
425 /// resume_node must live longer than the anyframe that it holds a reference to.390 /// resume_node must live longer than the anyframe that it holds a reference to.
...@@ -635,7 +600,7 @@ pub const Loop = struct {...@@ -635,7 +600,7 @@ pub const Loop = struct {
635 .freebsd,600 .freebsd,
636 .netbsd,601 .netbsd,
637 .dragonfly,602 .dragonfly,
638 => self.os_data.fs_thread.wait(),603 => self.fs_thread.wait(),
639 else => {},604 else => {},
640 }605 }
641606
...@@ -672,23 +637,25 @@ pub const Loop = struct {...@@ -672,23 +637,25 @@ pub const Loop = struct {
672637
673 /// call finishOneEvent when done638 /// call finishOneEvent when done
674 pub fn beginOneEvent(self: *Loop) void {639 pub fn beginOneEvent(self: *Loop) void {
675 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);640 _ = @atomicRmw(usize, &self.pending_event_count, .Add, 1, .SeqCst);
676 }641 }
677642
678 pub fn finishOneEvent(self: *Loop) void {643 pub fn finishOneEvent(self: *Loop) void {
679 const prev = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);644 noasync {
680 if (prev == 1) {645 const prev = @atomicRmw(usize, &self.pending_event_count, .Sub, 1, .SeqCst);
646 if (prev != 1) return;
647
681 // cause all the threads to stop648 // cause all the threads to stop
649 self.posixFsRequest(&self.fs_end_request);
650
682 switch (builtin.os.tag) {651 switch (builtin.os.tag) {
683 .linux => {652 .linux => {
684 self.posixFsRequest(&self.os_data.fs_end_request);
685 // writing 8 bytes to an eventfd cannot fail653 // writing 8 bytes to an eventfd cannot fail
686 const amt = noasync os.write(self.os_data.final_eventfd, &wakeup_bytes) catch unreachable;654 const amt = os.write(self.os_data.final_eventfd, &wakeup_bytes) catch unreachable;
687 assert(amt == wakeup_bytes.len);655 assert(amt == wakeup_bytes.len);
688 return;656 return;
689 },657 },
690 .macosx, .freebsd, .netbsd, .dragonfly => {658 .macosx, .freebsd, .netbsd, .dragonfly => {
691 self.posixFsRequest(&self.os_data.fs_end_request);
692 const final_kevent = @as(*const [1]os.Kevent, &self.os_data.final_kevent);659 const final_kevent = @as(*const [1]os.Kevent, &self.os_data.final_kevent);
693 const empty_kevs = &[0]os.Kevent{};660 const empty_kevs = &[0]os.Kevent{};
694 // cannot fail because we already added it and this just enables it661 // cannot fail because we already added it and this just enables it
...@@ -1041,73 +1008,55 @@ pub const Loop = struct {...@@ -1041,73 +1008,55 @@ pub const Loop = struct {
10411008
1042 fn posixFsRequest(self: *Loop, request_node: *Request.Node) void {1009 fn posixFsRequest(self: *Loop, request_node: *Request.Node) void {
1043 self.beginOneEvent(); // finished in posixFsRun after processing the msg1010 self.beginOneEvent(); // finished in posixFsRun after processing the msg
1044 self.os_data.fs_queue.put(request_node);1011 self.fs_queue.put(request_node);
1045 switch (builtin.os.tag) {1012 self.fs_thread_wakeup.set();
1046 .macosx, .freebsd, .netbsd, .dragonfly => {
1047 const fs_kevs = @as(*const [1]os.Kevent, &self.os_data.fs_kevent_wake);
1048 const empty_kevs = &[0]os.Kevent{};
1049 _ = os.kevent(self.os_data.fs_kqfd, fs_kevs, empty_kevs, null) catch unreachable;
1050 },
1051 .linux => {
1052 @atomicStore(i32, &self.os_data.fs_queue_item, 1, AtomicOrder.SeqCst);
1053 const rc = os.linux.futex_wake(&self.os_data.fs_queue_item, os.linux.FUTEX_WAKE, 1);
1054 switch (os.linux.getErrno(rc)) {
1055 0 => {},
1056 os.EINVAL => unreachable,
1057 else => unreachable,
1058 }
1059 },
1060 else => @compileError("Unsupported OS"),
1061 }
1062 }1013 }
10631014
1064 fn posixFsCancel(self: *Loop, request_node: *Request.Node) void {1015 fn posixFsCancel(self: *Loop, request_node: *Request.Node) void {
1065 if (self.os_data.fs_queue.remove(request_node)) {1016 if (self.fs_queue.remove(request_node)) {
1066 self.finishOneEvent();1017 self.finishOneEvent();
1067 }1018 }
1068 }1019 }
10691020
1070 // TODO make this whole function noasync
1071 // https://github.com/ziglang/zig/issues/3157
1072 fn posixFsRun(self: *Loop) void {1021 fn posixFsRun(self: *Loop) void {
1073 while (true) {1022 noasync while (true) {
1074 if (builtin.os.tag == .linux) {1023 self.fs_thread_wakeup.reset();
1075 @atomicStore(i32, &self.os_data.fs_queue_item, 0, .SeqCst);1024 while (self.fs_queue.get()) |node| {
1076 }
1077 while (self.os_data.fs_queue.get()) |node| {
1078 switch (node.data.msg) {1025 switch (node.data.msg) {
1079 .end => return,1026 .end => return,
1080 .read => |*msg| {1027 .read => |*msg| {
1081 msg.result = noasync os.read(msg.fd, msg.buf);1028 msg.result = os.read(msg.fd, msg.buf);
1082 },1029 },
1083 .readv => |*msg| {1030 .readv => |*msg| {
1084 msg.result = noasync os.readv(msg.fd, msg.iov);1031 msg.result = os.readv(msg.fd, msg.iov);
1085 },1032 },
1086 .write => |*msg| {1033 .write => |*msg| {
1087 msg.result = noasync os.write(msg.fd, msg.bytes);1034 msg.result = os.write(msg.fd, msg.bytes);
1088 },1035 },
1089 .writev => |*msg| {1036 .writev => |*msg| {
1090 msg.result = noasync os.writev(msg.fd, msg.iov);1037 msg.result = os.writev(msg.fd, msg.iov);
1091 },1038 },
1092 .pwritev => |*msg| {1039 .pwritev => |*msg| {
1093 msg.result = noasync os.pwritev(msg.fd, msg.iov, msg.offset);1040 msg.result = os.pwritev(msg.fd, msg.iov, msg.offset);
1094 },1041 },
1095 .pread => |*msg| {1042 .pread => |*msg| {
1096 msg.result = noasync os.pread(msg.fd, msg.buf, msg.offset);1043 msg.result = os.pread(msg.fd, msg.buf, msg.offset);
1097 },1044 },
1098 .preadv => |*msg| {1045 .preadv => |*msg| {
1099 msg.result = noasync os.preadv(msg.fd, msg.iov, msg.offset);1046 msg.result = os.preadv(msg.fd, msg.iov, msg.offset);
1100 },1047 },
1101 .open => |*msg| {1048 .open => |*msg| {
1102 msg.result = noasync os.openZ(msg.path, msg.flags, msg.mode);1049 if (is_windows) unreachable; // TODO
1050 msg.result = os.openZ(msg.path, msg.flags, msg.mode);
1103 },1051 },
1104 .openat => |*msg| {1052 .openat => |*msg| {
1105 msg.result = noasync os.openatZ(msg.fd, msg.path, msg.flags, msg.mode);1053 if (is_windows) unreachable; // TODO
1054 msg.result = os.openatZ(msg.fd, msg.path, msg.flags, msg.mode);
1106 },1055 },
1107 .faccessat => |*msg| {1056 .faccessat => |*msg| {
1108 msg.result = noasync os.faccessatZ(msg.dirfd, msg.path, msg.mode, msg.flags);1057 msg.result = os.faccessatZ(msg.dirfd, msg.path, msg.mode, msg.flags);
1109 },1058 },
1110 .close => |*msg| noasync os.close(msg.fd),1059 .close => |*msg| os.close(msg.fd),
1111 }1060 }
1112 switch (node.data.finish) {1061 switch (node.data.finish) {
1113 .TickNode => |*tick_node| self.onNextTick(tick_node),1062 .TickNode => |*tick_node| self.onNextTick(tick_node),
...@@ -1115,22 +1064,8 @@ pub const Loop = struct {...@@ -1115,22 +1064,8 @@ pub const Loop = struct {
1115 }1064 }
1116 self.finishOneEvent();1065 self.finishOneEvent();
1117 }1066 }
1118 switch (builtin.os.tag) {1067 self.fs_thread_wakeup.wait();
1119 .linux => {1068 };
1120 const rc = os.linux.futex_wait(&self.os_data.fs_queue_item, os.linux.FUTEX_WAIT, 0, null);
1121 switch (os.linux.getErrno(rc)) {
1122 0, os.EINTR, os.EAGAIN => continue,
1123 else => unreachable,
1124 }
1125 },
1126 .macosx, .freebsd, .netbsd, .dragonfly => {
1127 const fs_kevs = @as(*const [1]os.Kevent, &self.os_data.fs_kevent_wait);
1128 var out_kevs: [1]os.Kevent = undefined;
1129 _ = os.kevent(self.os_data.fs_kqfd, fs_kevs, out_kevs[0..], null) catch unreachable;
1130 },
1131 else => @compileError("Unsupported OS"),
1132 }
1133 }
1134 }1069 }
11351070
1136 const OsData = switch (builtin.os.tag) {1071 const OsData = switch (builtin.os.tag) {
...@@ -1146,22 +1081,12 @@ pub const Loop = struct {...@@ -1146,22 +1081,12 @@ pub const Loop = struct {
1146 const KEventData = struct {1081 const KEventData = struct {
1147 kqfd: i32,1082 kqfd: i32,
1148 final_kevent: os.Kevent,1083 final_kevent: os.Kevent,
1149 fs_kevent_wake: os.Kevent,
1150 fs_kevent_wait: os.Kevent,
1151 fs_thread: *Thread,
1152 fs_kqfd: i32,
1153 fs_queue: std.atomic.Queue(Request),
1154 fs_end_request: Request.Node,
1155 };1084 };
11561085
1157 const LinuxOsData = struct {1086 const LinuxOsData = struct {
1158 epollfd: i32,1087 epollfd: i32,
1159 final_eventfd: i32,1088 final_eventfd: i32,
1160 final_eventfd_event: os.linux.epoll_event,1089 final_eventfd_event: os.linux.epoll_event,
1161 fs_thread: *Thread,
1162 fs_queue_item: i32,
1163 fs_queue: std.atomic.Queue(Request),
1164 fs_end_request: Request.Node,
1165 };1090 };
11661091
1167 pub const Request = struct {1092 pub const Request = struct {
...@@ -1302,11 +1227,11 @@ test "std.event.Loop - basic" {...@@ -1302,11 +1227,11 @@ test "std.event.Loop - basic" {
1302 loop.run();1227 loop.run();
1303}1228}
13041229
1305async fn testEventLoop() i32 {1230fn testEventLoop() i32 {
1306 return 1234;1231 return 1234;
1307}1232}
13081233
1309async fn testEventLoop2(h: anyframe->i32, did_it: *bool) void {1234fn testEventLoop2(h: anyframe->i32, did_it: *bool) void {
1310 const value = await h;1235 const value = await h;
1311 testing.expect(value == 1234);1236 testing.expect(value == 1234);
1312 did_it.* = true;1237 did_it.* = true;
lib/std/fs.zig+1-1
...@@ -734,7 +734,7 @@ pub const Dir = struct {...@@ -734,7 +734,7 @@ pub const Dir = struct {
734 .dir = self.fd,734 .dir = self.fd,
735 .access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | read_flag,735 .access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | read_flag,
736 .share_access = switch (flags.lock) {736 .share_access = switch (flags.lock) {
737 .None => @as(?w.ULONG, null),737 .None => w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
738 .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,738 .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
739 .Exclusive => w.FILE_SHARE_DELETE,739 .Exclusive => w.FILE_SHARE_DELETE,
740 },740 },
lib/std/os.zig+16-3
...@@ -854,8 +854,11 @@ pub const OpenError = error{...@@ -854,8 +854,11 @@ pub const OpenError = error{
854854
855/// Open and possibly create a file. Keeps trying if it gets interrupted.855/// Open and possibly create a file. Keeps trying if it gets interrupted.
856/// See also `openC`.856/// See also `openC`.
857/// TODO support windows
858pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t {857pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t {
858 if (std.Target.current.os.tag == .windows) {
859 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
860 return openW(&file_path_w, flags, perm);
861 }
859 const file_path_c = try toPosixPath(file_path);862 const file_path_c = try toPosixPath(file_path);
860 return openZ(&file_path_c, flags, perm);863 return openZ(&file_path_c, flags, perm);
861}864}
...@@ -864,8 +867,11 @@ pub const openC = @compileError("deprecated: renamed to openZ");...@@ -864,8 +867,11 @@ pub const openC = @compileError("deprecated: renamed to openZ");
864867
865/// Open and possibly create a file. Keeps trying if it gets interrupted.868/// Open and possibly create a file. Keeps trying if it gets interrupted.
866/// See also `open`.869/// See also `open`.
867/// TODO support windows
868pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: usize) OpenError!fd_t {870pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: usize) OpenError!fd_t {
871 if (std.Target.current.os.tag == .windows) {
872 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
873 return openW(&file_path_w, flags, perm);
874 }
869 while (true) {875 while (true) {
870 const rc = system.open(file_path, flags, perm);876 const rc = system.open(file_path, flags, perm);
871 switch (errno(rc)) {877 switch (errno(rc)) {
...@@ -895,6 +901,13 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: usize) OpenError!fd_t {...@@ -895,6 +901,13 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: usize) OpenError!fd_t {
895 }901 }
896}902}
897903
904/// Windows-only. The path parameter is
905/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
906/// Translates the POSIX open API call to a Windows API call.
907pub fn openW(file_path_w: []const u16, flags: u32, perm: usize) OpenError!fd_t {
908 @compileError("TODO implement openW for windows");
909}
910
898/// Open and possibly create a file. Keeps trying if it gets interrupted.911/// Open and possibly create a file. Keeps trying if it gets interrupted.
899/// `file_path` is relative to the open directory handle `dir_fd`.912/// `file_path` is relative to the open directory handle `dir_fd`.
900/// See also `openatC`.913/// See also `openatC`.
...@@ -1683,7 +1696,7 @@ pub fn renameatW(...@@ -1683,7 +1696,7 @@ pub fn renameatW(
1683 .dir = old_dir_fd,1696 .dir = old_dir_fd,
1684 .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE,1697 .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE,
1685 .creation = windows.FILE_OPEN,1698 .creation = windows.FILE_OPEN,
1686 .enable_async_io = false,1699 .io_mode = .blocking,
1687 }) catch |err| switch (err) {1700 }) catch |err| switch (err) {
1688 error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.1701 error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.
1689 else => |e| return e,1702 else => |e| return e,
lib/std/os/windows.zig+2-2
...@@ -116,10 +116,10 @@ pub const OpenFileOptions = struct {...@@ -116,10 +116,10 @@ pub const OpenFileOptions = struct {
116/// TODO when share_access_nonblocking is false, this implementation uses116/// TODO when share_access_nonblocking is false, this implementation uses
117/// untinterruptible sleep() to block. This is not the final iteration of the API.117/// untinterruptible sleep() to block. This is not the final iteration of the API.
118pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {118pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {
119 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {119 if (mem.eql(u16, sub_path_w, ".")) {
120 return error.IsDir;120 return error.IsDir;
121 }121 }
122 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {122 if (mem.eql(u16, sub_path_w, "..")) {
123 return error.IsDir;123 return error.IsDir;
124 }124 }
125125
lib/std/reset_event.zig+1-1
...@@ -52,7 +52,7 @@ pub const ResetEvent = struct {...@@ -52,7 +52,7 @@ pub const ResetEvent = struct {
5252
53 /// Wait for the event to be set by blocking the current thread.53 /// Wait for the event to be set by blocking the current thread.
54 /// A timeout in nanoseconds can be provided as a hint for how54 /// A timeout in nanoseconds can be provided as a hint for how
55 /// long the thread should block on the unset event before throwind error.TimedOut.55 /// long the thread should block on the unset event before throwing error.TimedOut.
56 pub fn timedWait(self: *ResetEvent, timeout_ns: u64) !void {56 pub fn timedWait(self: *ResetEvent, timeout_ns: u64) !void {
57 return self.os_event.wait(timeout_ns);57 return self.os_event.wait(timeout_ns);
58 }58 }