From 845103fc9e9260e4aad188d029d06c61e631848f Mon Sep 17 00:00:00 2001 From: Elaine Gibson Date: Mon, 3 Aug 2026 11:52:08 +0100 Subject: [PATCH] std.Io.Threaded: implement park and unpark for haiku --- lib/std/Io/Threaded.zig | 74 ++++++++++++++++++++++++++++++++++++++--- lib/std/c.zig | 8 +++++ lib/std/c/haiku.zig | 7 ++++ 3 files changed, 84 insertions(+), 5 deletions(-) diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index c6473678a20326c606cabf29bd16e27af9ffd843..6f63dd5c9e4eaa601073addf93fe3fdfe9a5dc51 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -829,6 +829,7 @@ const Thread = struct { /// Always released when `Status.cancelation` is set to `.parked`. futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn, unpark_flag: UnparkFlag, + park_tid: if (ParkTid == std.Thread.Id) void else ParkTid, csprng: Csprng, @@ -1220,7 +1221,7 @@ const Thread = struct { parking_futex.removeCanceledWaiter(futex_waiter); } if (need_unpark_flag) setUnparkFlag(&thread.unpark_flag); - unpark(&.{thread.id}, null); + unpark(&.{if (ParkTid == std.Thread.Id) thread.id else thread.park_tid}, null); return false; }, @@ -1749,6 +1750,7 @@ fn worker(t: *Threaded) void { .cancel_protection = .unblocked, .futex_waiter = undefined, .unpark_flag = unpark_flag_init, + .park_tid = if (ParkTid == std.Thread.Id) {} else getParkTid(), .csprng = .uninitialized, }; Thread.current = &thread; @@ -17430,6 +17432,7 @@ const use_parking_futex = switch (native_os) { .windows => true, // RtlWaitOnAddress is a userland implementation anyway .netbsd => true, // NetBSD has `futex(2)`, but it's historically been quite buggy. TODO: evaluate whether it's okay to use now. .illumos => true, // Illumos has no futex mechanism + .haiku => true, // Haiku has no futex mechanism else => false, }; const use_parking_sleep = switch (native_os) { @@ -17475,7 +17478,7 @@ const parking_futex = struct { const Waiter = struct { node: std.DoublyLinkedList.Node, address: usize, - tid: std.Thread.Id, + tid: ParkTid, /// `thread_status.cancelation` is `.parked` while the thread is waiting. The single thread /// which atomically updates it (to `.none` or `.canceling`) is responsible for: /// @@ -17516,7 +17519,7 @@ const parking_futex = struct { // Put the threadlocal access outside of the critical section. const opt_thread = Thread.current; - const self_tid = if (opt_thread) |thread| thread.id else std.Thread.getCurrentId(); + const self_tid = getParkTid(); var waiter: Waiter = .{ .node = undefined, // populated by list append @@ -17764,7 +17767,12 @@ const parking_sleep = struct { }, } } + // Uncancelable sleep; we expect not to be manually unparked. + + // On systems where parking the thread requires a one-time setup operation (e.g. creating a + // semaphore), we need to ensure that setup is done before we call `park`. + _ = getParkTid(); var dummy_flag: UnparkFlag = unpark_flag_init; if (park(timeout, null, if (need_unpark_flag) &dummy_flag)) { unreachable; // unexpected unpark @@ -17803,7 +17811,7 @@ const ParkingMutex = struct { /// Never modified once the `Waiter` is in the linked list. next: ?*Waiter, /// Never modified once the `Waiter` is in the linked list. - tid: std.Thread.Id, + tid: ParkTid, }; fn lock(m: *ParkingMutex) void { state: switch (State.unlocked) { // assume 'unlocked' to optimize for uncontended case @@ -17819,7 +17827,7 @@ const ParkingMutex = struct { .locked_once, _ => |last_state| { const old_waiter = last_state.waiter(); - const self_tid = if (Thread.current) |t| t.id else std.Thread.getCurrentId(); + const self_tid = getParkTid(); var waiter: Waiter = .{ .next = old_waiter, .unpark_flag = unpark_flag_init, @@ -17947,9 +17955,36 @@ fn setUnparkFlag(f: *UnparkFlag) void { /// but it seems that someone at Microsoft forgot how big their TIDs are supposed to be. const UnparkTid = switch (native_os) { .windows => usize, + else => ParkTid, +}; + +const ParkTid = switch (native_os) { + .haiku => std.c.sem_id, else => std.Thread.Id, }; +threadlocal var park_sem: std.c.sem_id = -1; + +fn getParkTid() ParkTid { + switch (native_os) { + .haiku => { + if (park_sem == -1) { + park_sem = std.c._kern_create_sem(0, null); + if (park_sem < 0) @panic("_kern_create_sem failed"); + _ = std.c.on_exit_thread(destroyParkSem, null); + } + return park_sem; + }, + else => { + return if (Thread.current) |thread| thread.id else std.Thread.getCurrentId(); + }, + } +} + +fn destroyParkSem(_: ?*anyopaque) callconv(.c) void { + _ = std.c._kern_delete_sem(park_sem); +} + fn park( timeout: Io.Timeout, /// This value has no semantic effect, but may allow the OS to optimize the operation. @@ -18015,6 +18050,27 @@ fn park( } }, .illumos => @panic("TODO: illumos lwp_park"), + .haiku => { + const timeout_flags: u32, const timeout_us = switch (timeout) { + .none => .{ 0, 0 }, + .deadline => |deadline| .{ + if (deadline.clock == .real) std.c.B_ABSOLUTE_TIMEOUT | std.c.B_TIMEOUT_REAL_TIME_BASE else std.c.B_ABSOLUTE_TIMEOUT, + deadline.raw.toMicroseconds(), + }, + .duration => |duration| .{ + if (duration.clock == .real) std.c.B_ABSOLUTE_TIMEOUT | std.c.B_TIMEOUT_REAL_TIME_BASE else std.c.B_ABSOLUTE_TIMEOUT, + nowPosix(duration.clock).addDuration(duration.raw).toMicroseconds(), + }, + }; + while (true) { + switch (std.c._kern_acquire_sem_etc(park_sem, 1, timeout_flags, timeout_us)) { + 0 => return, + std.c.E.B_TIMED_OUT => return error.Timeout, + std.c.E.B_INTERRUPTED => {}, + else => unreachable, + } + } + }, else => comptime unreachable, } } @@ -18057,6 +18113,14 @@ fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void { } }, .illumos => @panic("TODO: illumos lwp_unpark"), + .haiku => { + for (tids) |tid| { + switch (std.c._kern_release_sem_etc(tid, 1, 0)) { + 0 => {}, + else => recoverableOsBugDetected(), + } + } + }, else => comptime unreachable, } } diff --git a/lib/std/c.zig b/lib/std/c.zig index 7d9a4b1d49caa7755db57477cdf9a6703ff3ecf2..a586082ec6e0318f54f80beaa3e2e49bf4bc0c5d 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -2991,6 +2991,7 @@ pub const SIG = switch (native_os) { pub const UNBLOCK = 2; pub const SETMASK = 3; + pub const IO: SIG = .POLL; pub const IOT: SIG = .ABRT; HUP = 1, @@ -11267,15 +11268,22 @@ pub const signalfd_siginfo = illumos.signalfd_siginfo; pub const taskid_t = illumos.taskid_t; pub const zoneid_t = illumos.zoneid_t; +pub const B_ABSOLUTE_TIMEOUT = haiku.B_ABSOLUTE_TIMEOUT; pub const B_OS_NAME_LENGTH = haiku.B_OS_NAME_LENGTH; +pub const B_TIMEOUT_REAL_TIME_BASE = haiku.B_TIMEOUT_REAL_TIME_BASE; pub const DirEnt = haiku.DirEnt; +pub const _kern_acquire_sem_etc = haiku._kern_acquire_sem_etc; +pub const _kern_create_sem = haiku._kern_create_sem; +pub const _kern_delete_sem = haiku._kern_delete_sem; pub const _kern_open_dir = haiku._kern_open_dir; pub const _kern_read_dir = haiku._kern_read_dir; pub const _kern_read_stat = haiku._kern_read_stat; +pub const _kern_release_sem_etc = haiku._kern_release_sem_etc; pub const _kern_rewind_dir = haiku._kern_rewind_dir; pub const area_id = haiku.area_id; pub const find_thread = haiku.find_thread; pub const get_system_info = haiku.get_system_info; +pub const on_exit_thread = haiku.on_exit_thread; pub const port_id = haiku.port_id; pub const readv_pos = haiku.readv_pos; pub const sem_id = haiku.sem_id; diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig index 768935574d2912e79ea21f5bf1b4cd1f6f80b800..dd8bb8abf621324ba3ff2c9765f17524e30624c6 100644 --- a/lib/std/c/haiku.zig +++ b/lib/std/c/haiku.zig @@ -11,12 +11,19 @@ comptime { } pub const B_OS_NAME_LENGTH = 32; +pub const B_ABSOLUTE_TIMEOUT = 0x10; +pub const B_TIMEOUT_REAL_TIME_BASE = 0x40; +pub extern "root" fn _kern_create_sem(count: c_int, name: ?[*:0]const u8) sem_id; +pub extern "root" fn _kern_delete_sem(id: sem_id) status_t; +pub extern "root" fn _kern_acquire_sem_etc(id: sem_id, count: u32, flags: u32, timeout: i64) status_t; +pub extern "root" fn _kern_release_sem_etc(id: sem_id, count: u32, flags: u32) status_t; pub extern "root" fn _kern_open_dir(fd: fd_t, path: [*:0]const u8) fd_t; pub extern "root" fn _kern_read_dir(fd: fd_t, buffer: [*]u8, bufferSize: usize, maxCount: u32) isize; pub extern "root" fn _kern_rewind_dir(fd: fd_t) status_t; pub extern "root" fn _kern_read_stat(fd: fd_t, path: [*:0]const u8, traverseLink: bool, stat: *std.c.Stat, statSize: usize) status_t; +pub extern "root" fn on_exit_thread(callback: *const fn (?*anyopaque) callconv(.c) void, data: ?*anyopaque) status_t; pub extern "root" fn find_thread(name: ?[*:0]const u8) thread_id; pub extern "root" fn get_system_info(info: *system_info) status_t; -- 2.54.0