authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-08 02:43:30-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-08 02:46:10-04:00
log50d70d5f498470790f6d58b5e3018e0d89c2c9f3
tree9dcbba4e8d95931cd43c618676612815685c7932
parentc15a6fa9d0e11398f65e8ecc1903e07f4c57add6

tests passing with kqueue on macos


4 files changed, 454 insertions(+), 66 deletions(-)

std/c/darwin.zig+56
...@@ -6,6 +6,13 @@ pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, b...@@ -6,6 +6,13 @@ pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, b
6pub extern "c" fn mach_absolute_time() u64;6pub extern "c" fn mach_absolute_time() u64;
7pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void;7pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void;
88
9pub extern "c" fn kqueue() c_int;
10pub extern "c" fn kevent(kq: c_int, changelist: [*]const Kevent, nchanges: c_int,
11 eventlist: [*]Kevent, nevents: c_int, timeout: ?*const timespec) c_int;
12
13pub extern "c" fn kevent64(kq: c_int, changelist: [*]const kevent64_s, nchanges: c_int,
14 eventlist: [*]kevent64_s, nevents: c_int, flags: c_uint, timeout: ?*const timespec) c_int;
15
9pub use @import("../os/darwin_errno.zig");16pub use @import("../os/darwin_errno.zig");
1017
11pub const _errno = __error;18pub const _errno = __error;
...@@ -86,3 +93,52 @@ pub const pthread_attr_t = extern struct {...@@ -86,3 +93,52 @@ pub const pthread_attr_t = extern struct {
86 __sig: c_long,93 __sig: c_long,
87 __opaque: [56]u8,94 __opaque: [56]u8,
88};95};
96
97/// Renamed from `kevent` to `Kevent` to avoid conflict with function name.
98pub const Kevent = extern struct {
99 ident: usize,
100 filter: i16,
101 flags: u16,
102 fflags: u32,
103 data: isize,
104 udata: usize,
105};
106
107// sys/types.h on macos uses #pragma pack(4) so these checks are
108// to make sure the struct is laid out the same. These values were
109// produced from C code using the offsetof macro.
110const std = @import("../index.zig");
111const assert = std.debug.assert;
112
113comptime {
114 assert(@offsetOf(Kevent, "ident") == 0);
115 assert(@offsetOf(Kevent, "filter") == 8);
116 assert(@offsetOf(Kevent, "flags") == 10);
117 assert(@offsetOf(Kevent, "fflags") == 12);
118 assert(@offsetOf(Kevent, "data") == 16);
119 assert(@offsetOf(Kevent, "udata") == 24);
120}
121
122pub const kevent64_s = extern struct {
123 ident: u64,
124 filter: i16,
125 flags: u16,
126 fflags: u32,
127 data: i64,
128 udata: u64,
129 ext: [2]u64,
130};
131
132// sys/types.h on macos uses #pragma pack() so these checks are
133// to make sure the struct is laid out the same. These values were
134// produced from C code using the offsetof macro.
135comptime {
136 assert(@offsetOf(kevent64_s, "ident") == 0);
137 assert(@offsetOf(kevent64_s, "filter") == 8);
138 assert(@offsetOf(kevent64_s, "flags") == 10);
139 assert(@offsetOf(kevent64_s, "fflags") == 12);
140 assert(@offsetOf(kevent64_s, "data") == 16);
141 assert(@offsetOf(kevent64_s, "udata") == 24);
142 assert(@offsetOf(kevent64_s, "ext") == 32);
143}
144
std/event.zig+211-66
...@@ -118,6 +118,11 @@ pub const Loop = struct {...@@ -118,6 +118,11 @@ pub const Loop = struct {
118 extra_threads: []*std.os.Thread,118 extra_threads: []*std.os.Thread,
119 final_resume_node: ResumeNode,119 final_resume_node: ResumeNode,
120120
121 // pre-allocated eventfds. all permanently active.
122 // this is how we send promises to be resumed on other threads.
123 available_eventfd_resume_nodes: std.atomic.Stack(ResumeNode.EventFd),
124 eventfd_resume_nodes: []std.atomic.Stack(ResumeNode.EventFd).Node,
125
121 pub const NextTickNode = std.atomic.QueueMpsc(promise).Node;126 pub const NextTickNode = std.atomic.QueueMpsc(promise).Node;
122127
123 pub const ResumeNode = struct {128 pub const ResumeNode = struct {
...@@ -130,10 +135,17 @@ pub const Loop = struct {...@@ -130,10 +135,17 @@ pub const Loop = struct {
130 EventFd,135 EventFd,
131 };136 };
132137
133 pub const EventFd = struct {138 pub const EventFd = switch (builtin.os) {
134 base: ResumeNode,139 builtin.Os.macosx => struct {
135 epoll_op: u32,140 base: ResumeNode,
136 eventfd: i32,141 kevent: posix.Kevent,
142 },
143 builtin.Os.linux => struct {
144 base: ResumeNode,
145 epoll_op: u32,
146 eventfd: i32,
147 },
148 else => @compileError("unsupported OS"),
137 };149 };
138 };150 };
139151
...@@ -168,36 +180,41 @@ pub const Loop = struct {...@@ -168,36 +180,41 @@ pub const Loop = struct {
168 .id = ResumeNode.Id.Stop,180 .id = ResumeNode.Id.Stop,
169 .handle = undefined,181 .handle = undefined,
170 },182 },
183 .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(),
184 .eventfd_resume_nodes = undefined,
171 };185 };
172 try self.initOsData(thread_count);186 const extra_thread_count = thread_count - 1;
187 self.eventfd_resume_nodes = try self.allocator.alloc(
188 std.atomic.Stack(ResumeNode.EventFd).Node,
189 extra_thread_count,
190 );
191 errdefer self.allocator.free(self.eventfd_resume_nodes);
192
193 self.extra_threads = try self.allocator.alloc(*std.os.Thread, extra_thread_count);
194 errdefer self.allocator.free(self.extra_threads);
195
196 try self.initOsData(extra_thread_count);
173 errdefer self.deinitOsData();197 errdefer self.deinitOsData();
174 }198 }
175199
176 /// must call stop before deinit200 /// must call stop before deinit
177 pub fn deinit(self: *Loop) void {201 pub fn deinit(self: *Loop) void {
178 self.deinitOsData();202 self.deinitOsData();
203 self.allocator.free(self.extra_threads);
179 }204 }
180205
181 const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError ||206 const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError ||
182 std.os.SpawnThreadError || std.os.LinuxEpollCtlError;207 std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError;
183208
184 const wakeup_bytes = []u8{0x1} ** 8;209 const wakeup_bytes = []u8{0x1} ** 8;
185210
186 fn initOsData(self: *Loop, thread_count: usize) InitOsDataError!void {211 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {
187 switch (builtin.os) {212 switch (builtin.os) {
188 builtin.Os.linux => {213 builtin.Os.linux => {
189 const extra_thread_count = thread_count - 1;
190 self.os_data.available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init();
191 self.os_data.eventfd_resume_nodes = try self.allocator.alloc(
192 std.atomic.Stack(ResumeNode.EventFd).Node,
193 extra_thread_count,
194 );
195 errdefer self.allocator.free(self.os_data.eventfd_resume_nodes);
196
197 errdefer {214 errdefer {
198 while (self.os_data.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);215 while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);
199 }216 }
200 for (self.os_data.eventfd_resume_nodes) |*eventfd_node| {217 for (self.eventfd_resume_nodes) |*eventfd_node| {
201 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{218 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
202 .data = ResumeNode.EventFd{219 .data = ResumeNode.EventFd{
203 .base = ResumeNode{220 .base = ResumeNode{
...@@ -209,7 +226,7 @@ pub const Loop = struct {...@@ -209,7 +226,7 @@ pub const Loop = struct {
209 },226 },
210 .next = undefined,227 .next = undefined,
211 };228 };
212 self.os_data.available_eventfd_resume_nodes.push(eventfd_node);229 self.available_eventfd_resume_nodes.push(eventfd_node);
213 }230 }
214231
215 self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC);232 self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC);
...@@ -228,15 +245,84 @@ pub const Loop = struct {...@@ -228,15 +245,84 @@ pub const Loop = struct {
228 self.os_data.final_eventfd,245 self.os_data.final_eventfd,
229 &self.os_data.final_eventfd_event,246 &self.os_data.final_eventfd_event,
230 );247 );
231 self.extra_threads = try self.allocator.alloc(*std.os.Thread, extra_thread_count);
232 errdefer self.allocator.free(self.extra_threads);
233248
234 var extra_thread_index: usize = 0;249 var extra_thread_index: usize = 0;
235 errdefer {250 errdefer {
251 // writing 8 bytes to an eventfd cannot fail
252 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
253 while (extra_thread_index != 0) {
254 extra_thread_index -= 1;
255 self.extra_threads[extra_thread_index].wait();
256 }
257 }
258 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {
259 self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun);
260 }
261 },
262 builtin.Os.macosx => {
263 self.os_data.kqfd = try std.os.bsdKQueue();
264 errdefer std.os.close(self.os_data.kqfd);
265
266 self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count);
267 errdefer self.allocator.free(self.os_data.kevents);
268
269 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
270
271 for (self.eventfd_resume_nodes) |*eventfd_node, i| {
272 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
273 .data = ResumeNode.EventFd{
274 .base = ResumeNode{
275 .id = ResumeNode.Id.EventFd,
276 .handle = undefined,
277 },
278 // this one is for sending events
279 .kevent = posix.Kevent {
280 .ident = i,
281 .filter = posix.EVFILT_USER,
282 .flags = posix.EV_CLEAR|posix.EV_ADD|posix.EV_DISABLE,
283 .fflags = 0,
284 .data = 0,
285 .udata = @ptrToInt(&eventfd_node.data.base),
286 },
287 },
288 .next = undefined,
289 };
290 self.available_eventfd_resume_nodes.push(eventfd_node);
291 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent);
292 _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);
293 eventfd_node.data.kevent.flags = posix.EV_CLEAR|posix.EV_ENABLE;
294 eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER;
295 // this one is for waiting for events
296 self.os_data.kevents[i] = posix.Kevent {
297 .ident = i,
298 .filter = posix.EVFILT_USER,
299 .flags = 0,
300 .fflags = 0,
301 .data = 0,
302 .udata = @ptrToInt(&eventfd_node.data.base),
303 };
304 }
305
306 // Pre-add so that we cannot get error.SystemResources
307 // later when we try to activate it.
308 self.os_data.final_kevent = posix.Kevent{
309 .ident = extra_thread_count,
310 .filter = posix.EVFILT_USER,
311 .flags = posix.EV_ADD | posix.EV_DISABLE,
312 .fflags = 0,
313 .data = 0,
314 .udata = @ptrToInt(&self.final_resume_node),
315 };
316 const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent);
317 _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);
318 self.os_data.final_kevent.flags = posix.EV_ENABLE;
319 self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER;
320
321 var extra_thread_index: usize = 0;
322 errdefer {
323 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch unreachable;
236 while (extra_thread_index != 0) {324 while (extra_thread_index != 0) {
237 extra_thread_index -= 1;325 extra_thread_index -= 1;
238 // writing 8 bytes to an eventfd cannot fail
239 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
240 self.extra_threads[extra_thread_index].wait();326 self.extra_threads[extra_thread_index].wait();
241 }327 }
242 }328 }
...@@ -252,10 +338,12 @@ pub const Loop = struct {...@@ -252,10 +338,12 @@ pub const Loop = struct {
252 switch (builtin.os) {338 switch (builtin.os) {
253 builtin.Os.linux => {339 builtin.Os.linux => {
254 std.os.close(self.os_data.final_eventfd);340 std.os.close(self.os_data.final_eventfd);
255 while (self.os_data.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);341 while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);
256 std.os.close(self.os_data.epollfd);342 std.os.close(self.os_data.epollfd);
257 self.allocator.free(self.os_data.eventfd_resume_nodes);343 self.allocator.free(self.eventfd_resume_nodes);
258 self.allocator.free(self.extra_threads);344 },
345 builtin.Os.macosx => {
346 self.allocator.free(self.os_data.kevents);
259 },347 },
260 else => {},348 else => {},
261 }349 }
...@@ -332,21 +420,38 @@ pub const Loop = struct {...@@ -332,21 +420,38 @@ pub const Loop = struct {
332 continue :start_over;420 continue :start_over;
333 }421 }
334422
335 // non-last node, stick it in the epoll set so that423 // non-last node, stick it in the epoll/kqueue set so that
336 // other threads can get to it424 // other threads can get to it
337 if (self.os_data.available_eventfd_resume_nodes.pop()) |resume_stack_node| {425 if (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| {
338 const eventfd_node = &resume_stack_node.data;426 const eventfd_node = &resume_stack_node.data;
339 eventfd_node.base.handle = handle;427 eventfd_node.base.handle = handle;
340 // the pending count is already accounted for428 switch (builtin.os) {
341 const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET;429 builtin.Os.macosx => {
342 self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch |_| {430 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);
343 // fine, we didn't need it anyway431 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
344 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);432 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch |_| {
345 self.os_data.available_eventfd_resume_nodes.push(resume_stack_node);433 // fine, we didn't need it anyway
346 resume handle;434 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
347 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);435 self.available_eventfd_resume_nodes.push(resume_stack_node);
348 continue :start_over;436 resume handle;
349 };437 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
438 continue :start_over;
439 };
440 },
441 builtin.Os.linux => {
442 // the pending count is already accounted for
443 const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET;
444 self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch |_| {
445 // fine, we didn't need it anyway
446 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
447 self.available_eventfd_resume_nodes.push(resume_stack_node);
448 resume handle;
449 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
450 continue :start_over;
451 };
452 },
453 else => @compileError("unsupported OS"),
454 }
350 } else {455 } else {
351 // threads are too busy, can't add another eventfd to wake one up456 // threads are too busy, can't add another eventfd to wake one up
352 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);457 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
...@@ -359,35 +464,74 @@ pub const Loop = struct {...@@ -359,35 +464,74 @@ pub const Loop = struct {
359 const pending_event_count = @atomicLoad(usize, &self.pending_event_count, AtomicOrder.SeqCst);464 const pending_event_count = @atomicLoad(usize, &self.pending_event_count, AtomicOrder.SeqCst);
360 if (pending_event_count == 0) {465 if (pending_event_count == 0) {
361 // cause all the threads to stop466 // cause all the threads to stop
362 // writing 8 bytes to an eventfd cannot fail467 switch (builtin.os) {
363 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;468 builtin.Os.linux => {
364 return;469 // writing 8 bytes to an eventfd cannot fail
470 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
471 return;
472 },
473 builtin.Os.macosx => {
474 const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);
475 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
476 // cannot fail because we already added it and this just enables it
477 _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable;
478 return;
479 },
480 else => @compileError("unsupported OS"),
481 }
365 }482 }
366483
367 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);484 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
368 }485 }
369486
370 // only process 1 event so we don't steal from other threads487 switch (builtin.os) {
371 var events: [1]std.os.linux.epoll_event = undefined;488 builtin.Os.linux => {
372 const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);489 // only process 1 event so we don't steal from other threads
373 for (events[0..count]) |ev| {490 var events: [1]std.os.linux.epoll_event = undefined;
374 const resume_node = @intToPtr(*ResumeNode, ev.data.ptr);491 const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);
375 const handle = resume_node.handle;492 for (events[0..count]) |ev| {
376 const resume_node_id = resume_node.id;493 const resume_node = @intToPtr(*ResumeNode, ev.data.ptr);
377 switch (resume_node_id) {494 const handle = resume_node.handle;
378 ResumeNode.Id.Basic => {},495 const resume_node_id = resume_node.id;
379 ResumeNode.Id.Stop => return,496 switch (resume_node_id) {
380 ResumeNode.Id.EventFd => {497 ResumeNode.Id.Basic => {},
381 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);498 ResumeNode.Id.Stop => return,
382 event_fd_node.epoll_op = posix.EPOLL_CTL_MOD;499 ResumeNode.Id.EventFd => {
383 const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node);500 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);
384 self.os_data.available_eventfd_resume_nodes.push(stack_node);501 event_fd_node.epoll_op = posix.EPOLL_CTL_MOD;
385 },502 const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node);
386 }503 self.available_eventfd_resume_nodes.push(stack_node);
387 resume handle;504 },
388 if (resume_node_id == ResumeNode.Id.EventFd) {505 }
389 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);506 resume handle;
390 }507 if (resume_node_id == ResumeNode.Id.EventFd) {
508 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
509 }
510 }
511 },
512 builtin.Os.macosx => {
513 var eventlist: [1]posix.Kevent = undefined;
514 const count = std.os.bsdKEvent(self.os_data.kqfd, self.os_data.kevents, eventlist[0..], null) catch unreachable;
515 for (eventlist[0..count]) |ev| {
516 const resume_node = @intToPtr(*ResumeNode, ev.udata);
517 const handle = resume_node.handle;
518 const resume_node_id = resume_node.id;
519 switch (resume_node_id) {
520 ResumeNode.Id.Basic => {},
521 ResumeNode.Id.Stop => return,
522 ResumeNode.Id.EventFd => {
523 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);
524 const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node);
525 self.available_eventfd_resume_nodes.push(stack_node);
526 },
527 }
528 resume handle;
529 if (resume_node_id == ResumeNode.Id.EventFd) {
530 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
531 }
532 }
533 },
534 else => @compileError("unsupported OS"),
391 }535 }
392 }536 }
393 }537 }
...@@ -395,12 +539,13 @@ pub const Loop = struct {...@@ -395,12 +539,13 @@ pub const Loop = struct {
395 const OsData = switch (builtin.os) {539 const OsData = switch (builtin.os) {
396 builtin.Os.linux => struct {540 builtin.Os.linux => struct {
397 epollfd: i32,541 epollfd: i32,
398 // pre-allocated eventfds. all permanently active.
399 // this is how we send promises to be resumed on other threads.
400 available_eventfd_resume_nodes: std.atomic.Stack(ResumeNode.EventFd),
401 eventfd_resume_nodes: []std.atomic.Stack(ResumeNode.EventFd).Node,
402 final_eventfd: i32,542 final_eventfd: i32,
403 final_eventfd_event: posix.epoll_event,543 final_eventfd_event: std.os.linux.epoll_event,
544 },
545 builtin.Os.macosx => struct {
546 kqfd: i32,
547 final_kevent: posix.Kevent,
548 kevents: []posix.Kevent,
404 },549 },
405 else => struct {},550 else => struct {},
406 };551 };
std/os/darwin.zig+131
...@@ -264,6 +264,119 @@ pub const SIGUSR1 = 30;...@@ -264,6 +264,119 @@ pub const SIGUSR1 = 30;
264/// user defined signal 2264/// user defined signal 2
265pub const SIGUSR2 = 31;265pub const SIGUSR2 = 31;
266266
267pub const KEVENT_FLAG_NONE = 0x000; /// no flag value
268pub const KEVENT_FLAG_IMMEDIATE = 0x001; /// immediate timeout
269pub const KEVENT_FLAG_ERROR_EVENTS = 0x002; /// output events only include change
270
271pub const EV_ADD = 0x0001; /// add event to kq (implies enable)
272pub const EV_DELETE = 0x0002; /// delete event from kq
273pub const EV_ENABLE = 0x0004; /// enable event
274pub const EV_DISABLE = 0x0008; /// disable event (not reported)
275
276pub const EV_ONESHOT = 0x0010; /// only report one occurrence
277pub const EV_CLEAR = 0x0020; /// clear event state after reporting
278
279/// force immediate event output
280/// ... with or without EV_ERROR
281/// ... use KEVENT_FLAG_ERROR_EVENTS
282/// on syscalls supporting flags
283pub const EV_RECEIPT = 0x0040;
284
285pub const EV_DISPATCH = 0x0080; /// disable event after reporting
286pub const EV_UDATA_SPECIFIC = 0x0100; /// unique kevent per udata value
287
288/// ... in combination with EV_DELETE
289/// will defer delete until udata-specific
290/// event enabled. EINPROGRESS will be
291/// returned to indicate the deferral
292pub const EV_DISPATCH2 = EV_DISPATCH | EV_UDATA_SPECIFIC;
293
294/// report that source has vanished
295/// ... only valid with EV_DISPATCH2
296pub const EV_VANISHED = 0x0200;
297
298pub const EV_SYSFLAGS = 0xF000; /// reserved by system
299pub const EV_FLAG0 = 0x1000; /// filter-specific flag
300pub const EV_FLAG1 = 0x2000; /// filter-specific flag
301pub const EV_EOF = 0x8000; /// EOF detected
302pub const EV_ERROR = 0x4000; /// error, data contains errno
303
304pub const EV_POLL = EV_FLAG0;
305pub const EV_OOBAND = EV_FLAG1;
306
307pub const EVFILT_READ = -1;
308pub const EVFILT_WRITE = -2;
309pub const EVFILT_AIO = -3; /// attached to aio requests
310pub const EVFILT_VNODE = -4; /// attached to vnodes
311pub const EVFILT_PROC = -5; /// attached to struct proc
312pub const EVFILT_SIGNAL = -6; /// attached to struct proc
313pub const EVFILT_TIMER = -7; /// timers
314pub const EVFILT_MACHPORT = -8; /// Mach portsets
315pub const EVFILT_FS = -9; /// Filesystem events
316pub const EVFILT_USER = -10; /// User events
317pub const EVFILT_VM = -12; /// Virtual memory events
318
319pub const EVFILT_EXCEPT = -15; /// Exception events
320
321pub const EVFILT_SYSCOUNT = 17;
322
323/// On input, NOTE_TRIGGER causes the event to be triggered for output.
324pub const NOTE_TRIGGER = 0x01000000;
325
326pub const NOTE_FFNOP = 0x00000000; /// ignore input fflags
327pub const NOTE_FFAND = 0x40000000; /// and fflags
328pub const NOTE_FFOR = 0x80000000; /// or fflags
329pub const NOTE_FFCOPY = 0xc0000000; /// copy fflags
330pub const NOTE_FFCTRLMASK = 0xc0000000; /// mask for operations
331pub const NOTE_FFLAGSMASK = 0x00ffffff;
332
333pub const NOTE_LOWAT = 0x00000001; /// low water mark
334
335pub const NOTE_OOB = 0x00000002; /// OOB data
336
337pub const NOTE_DELETE = 0x00000001; /// vnode was removed
338pub const NOTE_WRITE = 0x00000002; /// data contents changed
339pub const NOTE_EXTEND = 0x00000004; /// size increased
340pub const NOTE_ATTRIB = 0x00000008; /// attributes changed
341pub const NOTE_LINK = 0x00000010; /// link count changed
342pub const NOTE_RENAME = 0x00000020; /// vnode was renamed
343pub const NOTE_REVOKE = 0x00000040; /// vnode access was revoked
344pub const NOTE_NONE = 0x00000080; /// No specific vnode event: to test for EVFILT_READ activation
345pub const NOTE_FUNLOCK = 0x00000100; /// vnode was unlocked by flock(2)
346
347pub const NOTE_EXIT = 0x80000000; /// process exited
348pub const NOTE_FORK = 0x40000000; /// process forked
349pub const NOTE_EXEC = 0x20000000; /// process exec'd
350pub const NOTE_SIGNAL = 0x08000000; /// shared with EVFILT_SIGNAL
351pub const NOTE_EXITSTATUS = 0x04000000; /// exit status to be returned, valid for child process only
352pub const NOTE_EXIT_DETAIL = 0x02000000; /// provide details on reasons for exit
353
354pub const NOTE_PDATAMASK = 0x000fffff; /// mask for signal & exit status
355pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK);
356
357pub const NOTE_EXIT_DETAIL_MASK = 0x00070000;
358pub const NOTE_EXIT_DECRYPTFAIL = 0x00010000;
359pub const NOTE_EXIT_MEMORY = 0x00020000;
360pub const NOTE_EXIT_CSERROR = 0x00040000;
361
362
363pub const NOTE_VM_PRESSURE = 0x80000000; /// will react on memory pressure
364pub const NOTE_VM_PRESSURE_TERMINATE = 0x40000000; /// will quit on memory pressure, possibly after cleaning up dirty state
365pub const NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000; /// will quit immediately on memory pressure
366pub const NOTE_VM_ERROR = 0x10000000; /// there was an error
367
368pub const NOTE_SECONDS = 0x00000001; /// data is seconds
369pub const NOTE_USECONDS = 0x00000002; /// data is microseconds
370pub const NOTE_NSECONDS = 0x00000004; /// data is nanoseconds
371pub const NOTE_ABSOLUTE = 0x00000008; /// absolute timeout
372
373pub const NOTE_LEEWAY = 0x00000010; /// ext[1] holds leeway for power aware timers
374pub const NOTE_CRITICAL = 0x00000020; /// system does minimal timer coalescing
375pub const NOTE_BACKGROUND = 0x00000040; /// system does maximum timer coalescing
376pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080;
377pub const NOTE_MACHTIME = 0x00000100; /// data is mach absolute time units
378
379
267fn wstatus(x: i32) i32 {380fn wstatus(x: i32) i32 {
268 return x & 0o177;381 return x & 0o177;
269}382}
...@@ -385,6 +498,20 @@ pub fn getdirentries64(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usi...@@ -385,6 +498,20 @@ pub fn getdirentries64(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usi
385 return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep)));498 return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep)));
386}499}
387500
501pub fn kqueue() usize {
502 return errnoWrap(c.kqueue());
503}
504
505pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize {
506 return errnoWrap(c.kevent(kq, changelist.ptr, @intCast(c_int, changelist.len), eventlist.ptr, @intCast(c_int, eventlist.len), timeout,));
507}
508
509pub fn kevent64(kq: i32, changelist: []const kevent64_s, eventlist: []kevent64_s, flags: u32,
510 timeout: ?*const timespec) usize
511{
512 return errnoWrap(c.kevent64(kq, changelist.ptr, changelist.len, eventlist.ptr, eventlist.len, flags, timeout));
513}
514
388pub fn mkdir(path: [*]const u8, mode: u32) usize {515pub fn mkdir(path: [*]const u8, mode: u32) usize {
389 return errnoWrap(c.mkdir(path, mode));516 return errnoWrap(c.mkdir(path, mode));
390}517}
...@@ -474,6 +601,10 @@ pub const dirent = c.dirent;...@@ -474,6 +601,10 @@ pub const dirent = c.dirent;
474pub const sa_family_t = c.sa_family_t;601pub const sa_family_t = c.sa_family_t;
475pub const sockaddr = c.sockaddr;602pub const sockaddr = c.sockaddr;
476603
604/// Renamed from `kevent` to `Kevent` to avoid conflict with the syscall.
605pub const Kevent = c.Kevent;
606pub const kevent64_s = c.kevent64_s;
607
477/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.608/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
478pub const Sigaction = struct {609pub const Sigaction = struct {
479 handler: extern fn (i32) void,610 handler: extern fn (i32) void,
std/os/index.zig+56
...@@ -2787,3 +2787,59 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize {...@@ -2787,3 +2787,59 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize {
2787 }2787 }
2788 }2788 }
2789}2789}
2790
2791pub const BsdKQueueError = error {
2792 /// The per-process limit on the number of open file descriptors has been reached.
2793 ProcessFdQuotaExceeded,
2794
2795 /// The system-wide limit on the total number of open files has been reached.
2796 SystemFdQuotaExceeded,
2797
2798 Unexpected,
2799};
2800
2801pub fn bsdKQueue() BsdKQueueError!i32 {
2802 const rc = posix.kqueue();
2803 const err = posix.getErrno(rc);
2804 switch (err) {
2805 0 => return @intCast(i32, rc),
2806 posix.EMFILE => return BsdKQueueError.ProcessFdQuotaExceeded,
2807 posix.ENFILE => return BsdKQueueError.SystemFdQuotaExceeded,
2808 else => return unexpectedErrorPosix(err),
2809 }
2810}
2811
2812pub const BsdKEventError = error {
2813 /// The process does not have permission to register a filter.
2814 AccessDenied,
2815
2816 /// The event could not be found to be modified or deleted.
2817 EventNotFound,
2818
2819 /// No memory was available to register the event.
2820 SystemResources,
2821
2822 /// The specified process to attach to does not exist.
2823 ProcessNotFound,
2824};
2825
2826pub fn bsdKEvent(kq: i32, changelist: []const posix.Kevent, eventlist: []posix.Kevent,
2827 timeout: ?*const posix.timespec) BsdKEventError!usize
2828{
2829 while (true) {
2830 const rc = posix.kevent(kq, changelist, eventlist, timeout);
2831 const err = posix.getErrno(rc);
2832 switch (err) {
2833 0 => return rc,
2834 posix.EACCES => return BsdKEventError.AccessDenied,
2835 posix.EFAULT => unreachable,
2836 posix.EBADF => unreachable,
2837 posix.EINTR => continue,
2838 posix.EINVAL => unreachable,
2839 posix.ENOENT => return BsdKEventError.EventNotFound,
2840 posix.ENOMEM => return BsdKEventError.SystemResources,
2841 posix.ESRCH => return BsdKEventError.ProcessNotFound,
2842 else => unreachable,
2843 }
2844 }
2845}