@@ -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.
@@ -360,34 +356,12 @@ pub const Loop = struct {
360356 self .thread_pool_completions .init ();
361357 }
362358
363- if (comptime builtin .target .os .tag == .macos ) {
364- // Add our event so that we wake up when our mach port receives an
365- // event. We have to add here because we need a stable self pointer.
366- const events = [_ ]Kevent {.{
367- .ident = @as (usize , @intCast (self .mach_port .port )),
368- .filter = posix .system .EVFILT .MACHPORT ,
369- .flags = posix .system .EV .ADD | posix .system .EV .ENABLE ,
370- .fflags = darwin .MACH_RCV_MSG ,
371- .data = 0 ,
372- .udata = 0 ,
373- .ext = .{
374- @intFromPtr (& self .mach_port_buffer ),
375- self .mach_port_buffer .len ,
376- },
377- }};
378- const n = kevent_syscall (
379- self .kqueue_fd ,
380- & events ,
381- events [0.. 0],
382- null ,
383- ) catch | err | {
384- // We reset initialization because we can't do anything
385- // safely unless we get this mach port registered!
386- self .flags .init = false ;
387- return err ;
388- };
389- assert (n == 0 );
390- }
359+ self .wakeup_state .setup (self .kqueue_fd ) catch | err | {
360+ // We reset initialization because we can't do anything
361+ // safely unless we get this mach port registered!
362+ self .flags .init = false ;
363+ return err ;
364+ };
391365 }
392366
393367 // The list of events, used as both a changelist and eventlist.
@@ -1037,7 +1011,7 @@ pub const Loop = struct {
10371011 /// Sends an empty message to this loop's mach port so that it wakes
10381012 /// up if it is blocking on kevent().
10391013 fn wakeup (self : * Loop ) ! void {
1040- try self .mach_port . notify ();
1014+ try self .wakeup_state . wakeup ();
10411015 }
10421016};
10431017
@@ -1508,6 +1482,76 @@ pub const Completion = struct {
15081482 }
15091483};
15101484
1485+ /// The struct used for loop wakeup. This is only internal state.
1486+ const Wakeup = if (builtin .os .tag .isDarwin ()) struct {
1487+ const Self = @This ();
1488+
1489+ /// The mach port that this kqueue always has a filter for. Writing
1490+ /// an empty message to this port can be used to wake up the loop
1491+ /// at any time. Waking up the loop via this port won't trigger any
1492+ /// particular completion, it just forces tick to cycle.
1493+ mach_port : xev.Async ,
1494+ mach_port_buffer : [32 ]u8 = undefined ,
1495+
1496+ fn init () ! Self {
1497+ const mach_port = try xev .Async .init ();
1498+ errdefer mach_port .deinit ();
1499+ return .{ .mach_port = mach_port };
1500+ }
1501+
1502+ fn deinit (self : * Self ) void {
1503+ self .mach_port .deinit ();
1504+ }
1505+
1506+ fn setup (self : * Self , kqueue_fd : posix.fd_t ) ! void {
1507+ const events = [_ ]Kevent {.{
1508+ .ident = @as (usize , @intCast (self .mach_port .port )),
1509+ .filter = std .c .EVFILT .MACHPORT ,
1510+ .flags = std .c .EV .ADD | std .c .EV .ENABLE ,
1511+ .fflags = darwin .MACH_RCV_MSG ,
1512+ .data = 0 ,
1513+ .udata = 0 ,
1514+ .ext = .{
1515+ @intFromPtr (& self .mach_port_buffer ),
1516+ self .mach_port_buffer .len ,
1517+ },
1518+ }};
1519+ const n = try kevent_syscall (
1520+ kqueue_fd ,
1521+ & events ,
1522+ events [0.. 0],
1523+ null ,
1524+ );
1525+ assert (n == 0 );
1526+ }
1527+
1528+ fn wakeup (self : * Self ) ! void {
1529+ try self .mach_port .notify ();
1530+ }
1531+ } else struct {
1532+ // TODO: We should use eventfd for FreeBSD. Until this is
1533+ // implemented, loop wakeup will crash on BSD.
1534+ const Self = @This ();
1535+
1536+ fn init () ! Self {
1537+ return .{};
1538+ }
1539+
1540+ fn deinit (self : * Self ) void {
1541+ _ = self ;
1542+ }
1543+
1544+ fn setup (self : * Self , kqueue_fd : posix.fd_t ) ! void {
1545+ _ = self ;
1546+ _ = kqueue_fd ;
1547+ }
1548+
1549+ fn wakeup (self : * Self ) ! void {
1550+ _ = self ;
1551+ @panic ("wakeup not implemented on this platform" );
1552+ }
1553+ };
1554+
15111555pub const OperationType = enum {
15121556 noop ,
15131557 accept ,
0 commit comments