Skip to content

Commit

Permalink
build for freebsd
Browse files Browse the repository at this point in the history
uses @cryptocode mentioned here mitchellh#67 (comment)
as a starting point
  • Loading branch information
gigamaax committed Feb 4, 2025
1 parent 31eed4e commit a008bf7
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 78 deletions.
2 changes: 1 addition & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub fn build(b: *std.Build) !void {
// Our tests require libc on Linux and Mac. Note that libxev itself
// does NOT require libc.
const test_libc = switch (target.result.os.tag) {
.linux, .macos => true,
.freebsd, .linux, .macos => true,
else => false,
};

Expand Down
153 changes: 81 additions & 72 deletions src/backend/kqueue.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ const ThreadPool = main.ThreadPool;

const log = std.log.scoped(.libxev_kqueue);

const NOTE_EXIT_FLAGS = switch (builtin.os.tag) {
.ios, .macos => posix.system.NOTE_EXIT | posix.system.NOTE_EXITSTATUS,
.freebsd => posix.system.NOTE_EXIT,
else => @compileError("kqueue not supported yet for target OS"),
};

pub const Loop = struct {
const TimerHeap = heap.Intrusive(Timer, void, Timer.less);
const TaskCompletionQueue = queue_mpsc.Intrusive(Completion);
Expand Down Expand Up @@ -286,30 +292,30 @@ pub const Loop = struct {

// Add our event so that we wake up when our mach port receives an
// event. We have to add here because we need a stable self pointer.
const events = [_]Kevent{.{
.ident = @as(usize, @intCast(self.mach_port.port)),
.filter = posix.system.EVFILT_MACHPORT,
.flags = posix.system.EV_ADD | posix.system.EV_ENABLE,
.fflags = posix.system.MACH_RCV_MSG,
.data = 0,
.udata = 0,
.ext = .{
@intFromPtr(&self.mach_port_buffer),
self.mach_port_buffer.len,
},
}};
const n = kevent_syscall(
self.kqueue_fd,
&events,
events[0..0],
null,
) catch |err| {
// We reset initialization because we can't do anything
// safely unless we get this mach port registered!
self.flags.init = false;
return err;
};
assert(n == 0);
// const events = [_]Kevent{.{
// .ident = @as(usize, @intCast(self.mach_port.port)),
// .filter = posix.system.EVFILT_MACHPORT,
// .flags = posix.system.EV_ADD | posix.system.EV_ENABLE,
// .fflags = posix.system.MACH_RCV_MSG,
// .data = 0,
// .udata = 0,
// .ext = .{
// @intFromPtr(&self.mach_port_buffer),
// self.mach_port_buffer.len,
// },
// }};
// const n = kevent_syscall(
// self.kqueue_fd,
// &events,
// events[0..0],
// null,
// ) catch |err| {
// // We reset initialization because we can't do anything
// // safely unless we get this mach port registered!
// self.flags.init = false;
// return err;
// };
// assert(n == 0);
}

// The list of events, used as both a changelist and eventlist.
Expand Down Expand Up @@ -765,10 +771,10 @@ pub const Loop = struct {
break :action .{ .kevent = {} };
},

.machport => action: {
ev.* = c.kevent().?;
break :action .{ .kevent = {} };
},
// .machport => action: {
// ev.* = c.kevent().?;
// break :action .{ .kevent = {} };
// },

.proc => action: {
ev.* = c.kevent().?;
Expand Down Expand Up @@ -1045,28 +1051,28 @@ pub const Completion = struct {
.udata = @intFromPtr(self),
}),

.machport => kevent: {
// We can't use |*v| above because it crahses the Zig
// compiler (as of 0.11.0-dev.1413). We can retry another time.
const v = &self.op.machport;
const slice: []u8 = switch (v.buffer) {
.slice => |slice| slice,
.array => |*arr| arr,
};

// The kevent below waits for a machport to have a message
// available AND automatically reads the message into the
// buffer since MACH_RCV_MSG is set.
break :kevent .{
.ident = @intCast(v.port),
.filter = posix.system.EVFILT_MACHPORT,
.flags = posix.system.EV_ADD | posix.system.EV_ENABLE,
.fflags = posix.system.MACH_RCV_MSG,
.data = 0,
.udata = @intFromPtr(self),
.ext = .{ @intFromPtr(slice.ptr), slice.len },
};
},
// .machport => kevent: {
// // We can't use |*v| above because it crahses the Zig
// // compiler (as of 0.11.0-dev.1413). We can retry another time.
// const v = &self.op.machport;
// const slice: []u8 = switch (v.buffer) {
// .slice => |slice| slice,
// .array => |*arr| arr,
// };

// // The kevent below waits for a machport to have a message
// // available AND automatically reads the message into the
// // buffer since MACH_RCV_MSG is set.
// break :kevent .{
// .ident = @intCast(v.port),
// .filter = posix.system.EVFILT_MACHPORT,
// .flags = posix.system.EV_ADD | posix.system.EV_ENABLE,
// .fflags = posix.system.MACH_RCV_MSG,
// .data = 0,
// .udata = @intFromPtr(self),
// .ext = .{ @intFromPtr(slice.ptr), slice.len },
// };
// },

.proc => |v| kevent_init(.{
.ident = @intCast(v.pid),
Expand Down Expand Up @@ -1214,16 +1220,16 @@ pub const Completion = struct {
// Our machport operation ALWAYS has MACH_RCV set so there
// is no operation to perform. kqueue automatically reads in
// the mach message into the read buffer.
.machport => .{
.machport = {},
},
// .machport => .{
// .machport = {},
// },

// For proc watching, it is identical to the syscall result.
.proc => res: {
const ev = ev_ orelse break :res .{ .proc = ProcError.MissingKevent };

// If we have the exit status, we read it.
if (ev.fflags & (posix.system.NOTE_EXIT | posix.system.NOTE_EXITSTATUS) > 0) {
if (ev.fflags & NOTE_EXIT_FLAGS > 0) {
const data: u32 = @intCast(ev.data);
if (posix.W.IFEXITED(data)) break :res .{
.proc = posix.W.EXITSTATUS(data),
Expand Down Expand Up @@ -1325,13 +1331,13 @@ pub const Completion = struct {
},
},

.machport => .{
.machport = switch (errno) {
.SUCCESS => {},
.CANCELED => error.Canceled,
else => |err| posix.unexpectedErrno(err),
},
},
// .machport => .{
// .machport = switch (errno) {
// .SUCCESS => {},
// .CANCELED => error.Canceled,
// else => |err| posix.unexpectedErrno(err),
// },
// },

.proc => .{
.proc = switch (errno) {
Expand Down Expand Up @@ -1400,7 +1406,7 @@ pub const OperationType = enum {
shutdown,
timer,
cancel,
machport,
// machport,
proc,
};

Expand Down Expand Up @@ -1486,14 +1492,14 @@ pub const Operation = union(OperationType) {
c: *Completion,
},

machport: struct {
port: posix.system.mach_port_name_t,
buffer: ReadBuffer,
},
// machport: struct {
// port: posix.system.mach_port_name_t,
// buffer: ReadBuffer,
// },

proc: struct {
pid: posix.pid_t,
flags: u32 = posix.system.NOTE_EXIT | posix.system.NOTE_EXITSTATUS,
flags: u32 = NOTE_EXIT_FLAGS,
},
};

Expand All @@ -1513,7 +1519,7 @@ pub const Result = union(OperationType) {
shutdown: ShutdownError!void,
timer: TimerError!TimerTrigger,
cancel: CancelError!void,
machport: MachPortError!void,
// machport: MachPortError!void,
proc: ProcError!u32,
};

Expand Down Expand Up @@ -1674,6 +1680,7 @@ const Timer = struct {
/// This lets us support both Mac and non-Mac platforms.
const Kevent = switch (builtin.os.tag) {
.ios, .macos => posix.system.kevent64_s,
.freebsd => std.c.Kevent,
else => @compileError("kqueue not supported yet for target OS"),
};

Expand Down Expand Up @@ -2356,6 +2363,8 @@ test "kqueue: socket accept/connect/send/recv/close" {
}

test "kqueue: file IO on thread pool" {
if (builtin.os.tag != .macos) return error.SkipZigTest;

const testing = std.testing;

var tpool = main.ThreadPool.init(.{});
Expand Down Expand Up @@ -2472,10 +2481,10 @@ test "kqueue: mach port" {
var called = false;
var c_wait: xev.Completion = .{
.op = .{
.machport = .{
.port = mach_port,
.buffer = .{ .array = undefined },
},
//.machport = .{
// .port = mach_port,
// .buffer = .{ .array = undefined },
//},
},

.userdata = &called,
Expand Down
2 changes: 1 addition & 1 deletion src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub const Backend = enum {
pub fn default() Backend {
return @as(?Backend, switch (builtin.os.tag) {
.linux => .io_uring,
.ios, .macos => .kqueue,
.freebsd, .ios, .macos => .kqueue,
.wasi => .wasi_poll,
.windows => .iocp,
else => null,
Expand Down
10 changes: 7 additions & 3 deletions src/watcher/async.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@ const assert = std.debug.assert;
const posix = std.posix;
const common = @import("common.zig");

pub extern "c" fn eventfd(initval: c_uint, flags: c_uint) c_int;

pub fn Async(comptime xev: type) type {
return switch (xev.backend) {
// Supported, uses eventfd
.io_uring,
.epoll,
.kqueue,
=> AsyncEventFd(xev),

// Supported, uses the backend API
.wasi_poll => AsyncLoopState(xev, xev.Loop.threaded),

// Supported, uses mach ports
.kqueue => AsyncMachPort(xev),
// .kqueue => AsyncMachPort(xev),
.iocp => AsyncIOCP(xev),
};
}
Expand All @@ -36,7 +39,8 @@ fn AsyncEventFd(comptime xev: type) type {
/// to be woken up. The completion must be allocated in advance.
pub fn init() !Self {
return .{
.fd = try std.posix.eventfd(0, std.os.linux.EFD.CLOEXEC),
// .fd = try std.posix.eventfd(0, std.os.linux.EFD.CLOEXEC),
.fd = eventfd(0, 0),
};
}

Expand Down Expand Up @@ -152,7 +156,7 @@ fn AsyncMachPort(comptime xev: type) type {
) posix.system.kern_return_t;

/// The mach port
port: posix.system.mach_port_name_t,
// port: posix.system.mach_port_name_t,

/// Create a new async. An async can be assigned to exactly one loop
/// to be woken up. The completion must be allocated in advance.
Expand Down
3 changes: 3 additions & 0 deletions src/watcher/file.zig
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ pub fn File(comptime xev: type) type {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
// windows: std.fs.File is not opened with OVERLAPPED flag.
if (builtin.os.tag == .windows) return error.SkipZigTest;
if (builtin.os.tag == .freebsd) return error.SkipZigTest;

const testing = std.testing;

Expand Down Expand Up @@ -369,6 +370,7 @@ pub fn File(comptime xev: type) type {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
// windows: std.fs.File is not opened with OVERLAPPED flag.
if (builtin.os.tag == .windows) return error.SkipZigTest;
if (builtin.os.tag == .freebsd) return error.SkipZigTest;

const testing = std.testing;

Expand Down Expand Up @@ -441,6 +443,7 @@ pub fn File(comptime xev: type) type {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
// windows: std.fs.File is not opened with OVERLAPPED flag.
if (builtin.os.tag == .windows) return error.SkipZigTest;
if (builtin.os.tag == .freebsd) return error.SkipZigTest;

const testing = std.testing;

Expand Down
8 changes: 7 additions & 1 deletion src/watcher/process.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ const linux = std.os.linux;
const posix = std.posix;
const common = @import("common.zig");

const NOTE_EXIT_FLAGS = switch (builtin.os.tag) {
.ios, .macos => posix.system.NOTE_EXIT | posix.system.NOTE_EXITSTATUS,
.freebsd => posix.system.NOTE_EXIT,
else => @compileError("not supported yet for target OS"),
};

/// Process management, such as waiting for process exit.
pub fn Process(comptime xev: type) type {
return switch (xev.backend) {
Expand Down Expand Up @@ -177,7 +183,7 @@ fn ProcessKqueue(comptime xev: type) type {
.op = .{
.proc = .{
.pid = self.pid,
.flags = posix.system.NOTE_EXIT | posix.system.NOTE_EXITSTATUS,
.flags = NOTE_EXIT_FLAGS,
},
},

Expand Down

0 comments on commit a008bf7

Please sign in to comment.