@@ -45,12 +45,8 @@ pub const Loop = struct {
4545 /// The fd of the kqueue.
4646 kqueue_fd : posix.fd_t ,
4747
48- /// The mach port that this kqueue always has a filter for. Writing
49- /// an empty message to this port can be used to wake up the loop
50- /// at any time. Waking up the loop via this port won't trigger any
51- /// particular completion, it just forces tick to cycle.
52- mach_port : xev.Async ,
53- mach_port_buffer : [32 ]u8 = undefined ,
48+ /// The wakeup mechanism (mach ports on Apple, eventfd on BSD).
49+ wakeup_state : Wakeup ,
5450
5551 /// The number of active completions. This DOES NOT include completions that
5652 /// are queued in the submissions queue.
@@ -105,12 +101,12 @@ pub const Loop = struct {
105101 const fd = try posix .kqueue ();
106102 errdefer posix .close (fd );
107103
108- const mach_port = try xev . Async .init ();
109- errdefer mach_port .deinit ();
104+ const wakeup_state : Wakeup = try .init ();
105+ errdefer wakeup_state .deinit ();
110106
111107 var res : Loop = .{
112108 .kqueue_fd = fd ,
113- .mach_port = mach_port ,
109+ .wakeup_state = wakeup_state ,
114110 .thread_pool = options .thread_pool ,
115111 .thread_pool_completions = undefined ,
116112 .cached_now = undefined ,
@@ -124,7 +120,7 @@ pub const Loop = struct {
124120 /// were unprocessed are lost -- their callbacks will never be called.
125121 pub fn deinit (self : * Loop ) void {
126122 posix .close (self .kqueue_fd );
127- if ( comptime builtin . os . tag == .macos ) self .mach_port .deinit ();
123+ self .wakeup_state .deinit ();
128124 }
129125
130126 /// Stop the loop. This can only be called from the main thread.
@@ -320,34 +316,12 @@ pub const Loop = struct {
320316 self .thread_pool_completions .init ();
321317 }
322318
323- if (comptime builtin .target .os .tag == .macos ) {
324- // Add our event so that we wake up when our mach port receives an
325- // event. We have to add here because we need a stable self pointer.
326- const events = [_ ]Kevent {.{
327- .ident = @as (usize , @intCast (self .mach_port .port )),
328- .filter = std .c .EVFILT .MACHPORT ,
329- .flags = std .c .EV .ADD | std .c .EV .ENABLE ,
330- .fflags = darwin .MACH_RCV_MSG ,
331- .data = 0 ,
332- .udata = 0 ,
333- .ext = .{
334- @intFromPtr (& self .mach_port_buffer ),
335- self .mach_port_buffer .len ,
336- },
337- }};
338- const n = kevent_syscall (
339- self .kqueue_fd ,
340- & events ,
341- events [0.. 0],
342- null ,
343- ) catch | err | {
344- // We reset initialization because we can't do anything
345- // safely unless we get this mach port registered!
346- self .flags .init = false ;
347- return err ;
348- };
349- assert (n == 0 );
350- }
319+ self .wakeup_state .setup (self .kqueue_fd ) catch | err | {
320+ // We reset initialization because we can't do anything
321+ // safely unless we get this mach port registered!
322+ self .flags .init = false ;
323+ return err ;
324+ };
351325 }
352326
353327 // The list of events, used as both a changelist and eventlist.
@@ -982,7 +956,7 @@ pub const Loop = struct {
982956 /// Sends an empty message to this loop's mach port so that it wakes
983957 /// up if it is blocking on kevent().
984958 fn wakeup (self : * Loop ) ! void {
985- try self .mach_port . notify ();
959+ try self .wakeup_state . wakeup ();
986960 }
987961};
988962
@@ -1449,6 +1423,76 @@ pub const Completion = struct {
14491423 }
14501424};
14511425
1426+ /// The struct used for loop wakeup. This is only internal state.
1427+ const Wakeup = if (builtin .os .tag .isDarwin ()) struct {
1428+ const Self = @This ();
1429+
1430+ /// The mach port that this kqueue always has a filter for. Writing
1431+ /// an empty message to this port can be used to wake up the loop
1432+ /// at any time. Waking up the loop via this port won't trigger any
1433+ /// particular completion, it just forces tick to cycle.
1434+ mach_port : xev.Async ,
1435+ mach_port_buffer : [32 ]u8 = undefined ,
1436+
1437+ fn init () ! Self {
1438+ const mach_port = try xev .Async .init ();
1439+ errdefer mach_port .deinit ();
1440+ return .{ .mach_port = mach_port };
1441+ }
1442+
1443+ fn deinit (self : * Self ) void {
1444+ self .mach_port .deinit ();
1445+ }
1446+
1447+ fn setup (self : * Self , kqueue_fd : posix.fd_t ) ! void {
1448+ const events = [_ ]Kevent {.{
1449+ .ident = @as (usize , @intCast (self .mach_port .port )),
1450+ .filter = std .c .EVFILT .MACHPORT ,
1451+ .flags = std .c .EV .ADD | std .c .EV .ENABLE ,
1452+ .fflags = darwin .MACH_RCV_MSG ,
1453+ .data = 0 ,
1454+ .udata = 0 ,
1455+ .ext = .{
1456+ @intFromPtr (& self .mach_port_buffer ),
1457+ self .mach_port_buffer .len ,
1458+ },
1459+ }};
1460+ const n = try kevent_syscall (
1461+ kqueue_fd ,
1462+ & events ,
1463+ events [0.. 0],
1464+ null ,
1465+ );
1466+ assert (n == 0 );
1467+ }
1468+
1469+ fn wakeup (self : * Self ) ! void {
1470+ try self .mach_port .notify ();
1471+ }
1472+ } else struct {
1473+ // TODO: We should use eventfd for FreeBSD. Until this is
1474+ // implemented, loop wakeup will crash on BSD.
1475+ const Self = @This ();
1476+
1477+ fn init () ! Self {
1478+ return .{};
1479+ }
1480+
1481+ fn deinit (self : * Self ) void {
1482+ _ = self ;
1483+ }
1484+
1485+ fn setup (self : * Self , kqueue_fd : posix.fd_t ) ! void {
1486+ _ = self ;
1487+ _ = kqueue_fd ;
1488+ }
1489+
1490+ fn wakeup (self : * Self ) ! void {
1491+ _ = self ;
1492+ @panic ("wakeup not implemented on this platform" );
1493+ }
1494+ };
1495+
14521496pub const OperationType = enum {
14531497 noop ,
14541498 accept ,
0 commit comments