authorgravatar for ypsvlq@gmail.comElaine Gibson <ypsvlq@gmail.com> 2026-08-03 11:52:08+01:00
committergravatar for ypsvlq@gmail.comElaine Gibson <ypsvlq@gmail.com> 2026-08-03 11:52:08+01:00
log845103fc9e9260e4aad188d029d06c61e631848f
tree794c19f7b7cdd17af909714bf9747037c56845cb
parentad6a79f327f6762baf9661341330c5a190c6d718

std.Io.Threaded: implement park and unpark for haiku


3 files changed, 84 insertions(+), 5 deletions(-)

lib/std/Io/Threaded.zig+69-5
......@@ -829,6 +829,7 @@ const Thread = struct {
829829 /// Always released when `Status.cancelation` is set to `.parked`.
830830 futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,
831831 unpark_flag: UnparkFlag,
832 park_tid: if (ParkTid == std.Thread.Id) void else ParkTid,
832833
833834 csprng: Csprng,
834835
......@@ -1220,7 +1221,7 @@ const Thread = struct {
12201221 parking_futex.removeCanceledWaiter(futex_waiter);
12211222 }
12221223 if (need_unpark_flag) setUnparkFlag(&thread.unpark_flag);
1223 unpark(&.{thread.id}, null);
1224 unpark(&.{if (ParkTid == std.Thread.Id) thread.id else thread.park_tid}, null);
12241225 return false;
12251226 },
12261227
......@@ -1749,6 +1750,7 @@ fn worker(t: *Threaded) void {
17491750 .cancel_protection = .unblocked,
17501751 .futex_waiter = undefined,
17511752 .unpark_flag = unpark_flag_init,
1753 .park_tid = if (ParkTid == std.Thread.Id) {} else getParkTid(),
17521754 .csprng = .uninitialized,
17531755 };
17541756 Thread.current = &thread;
......@@ -17430,6 +17432,7 @@ const use_parking_futex = switch (native_os) {
1743017432 .windows => true, // RtlWaitOnAddress is a userland implementation anyway
1743117433 .netbsd => true, // NetBSD has `futex(2)`, but it's historically been quite buggy. TODO: evaluate whether it's okay to use now.
1743217434 .illumos => true, // Illumos has no futex mechanism
17435 .haiku => true, // Haiku has no futex mechanism
1743317436 else => false,
1743417437};
1743517438const use_parking_sleep = switch (native_os) {
......@@ -17475,7 +17478,7 @@ const parking_futex = struct {
1747517478 const Waiter = struct {
1747617479 node: std.DoublyLinkedList.Node,
1747717480 address: usize,
17478 tid: std.Thread.Id,
17481 tid: ParkTid,
1747917482 /// `thread_status.cancelation` is `.parked` while the thread is waiting. The single thread
1748017483 /// which atomically updates it (to `.none` or `.canceling`) is responsible for:
1748117484 ///
......@@ -17516,7 +17519,7 @@ const parking_futex = struct {
1751617519
1751717520 // Put the threadlocal access outside of the critical section.
1751817521 const opt_thread = Thread.current;
17519 const self_tid = if (opt_thread) |thread| thread.id else std.Thread.getCurrentId();
17522 const self_tid = getParkTid();
1752017523
1752117524 var waiter: Waiter = .{
1752217525 .node = undefined, // populated by list append
......@@ -17764,7 +17767,12 @@ const parking_sleep = struct {
1776417767 },
1776517768 }
1776617769 }
17770
1776717771 // Uncancelable sleep; we expect not to be manually unparked.
17772
17773 // On systems where parking the thread requires a one-time setup operation (e.g. creating a
17774 // semaphore), we need to ensure that setup is done before we call `park`.
17775 _ = getParkTid();
1776817776 var dummy_flag: UnparkFlag = unpark_flag_init;
1776917777 if (park(timeout, null, if (need_unpark_flag) &dummy_flag)) {
1777017778 unreachable; // unexpected unpark
......@@ -17803,7 +17811,7 @@ const ParkingMutex = struct {
1780317811 /// Never modified once the `Waiter` is in the linked list.
1780417812 next: ?*Waiter,
1780517813 /// Never modified once the `Waiter` is in the linked list.
17806 tid: std.Thread.Id,
17814 tid: ParkTid,
1780717815 };
1780817816 fn lock(m: *ParkingMutex) void {
1780917817 state: switch (State.unlocked) { // assume 'unlocked' to optimize for uncontended case
......@@ -17819,7 +17827,7 @@ const ParkingMutex = struct {
1781917827
1782017828 .locked_once, _ => |last_state| {
1782117829 const old_waiter = last_state.waiter();
17822 const self_tid = if (Thread.current) |t| t.id else std.Thread.getCurrentId();
17830 const self_tid = getParkTid();
1782317831 var waiter: Waiter = .{
1782417832 .next = old_waiter,
1782517833 .unpark_flag = unpark_flag_init,
......@@ -17947,9 +17955,36 @@ fn setUnparkFlag(f: *UnparkFlag) void {
1794717955/// but it seems that someone at Microsoft forgot how big their TIDs are supposed to be.
1794817956const UnparkTid = switch (native_os) {
1794917957 .windows => usize,
17958 else => ParkTid,
17959};
17960
17961const ParkTid = switch (native_os) {
17962 .haiku => std.c.sem_id,
1795017963 else => std.Thread.Id,
1795117964};
1795217965
17966threadlocal var park_sem: std.c.sem_id = -1;
17967
17968fn getParkTid() ParkTid {
17969 switch (native_os) {
17970 .haiku => {
17971 if (park_sem == -1) {
17972 park_sem = std.c._kern_create_sem(0, null);
17973 if (park_sem < 0) @panic("_kern_create_sem failed");
17974 _ = std.c.on_exit_thread(destroyParkSem, null);
17975 }
17976 return park_sem;
17977 },
17978 else => {
17979 return if (Thread.current) |thread| thread.id else std.Thread.getCurrentId();
17980 },
17981 }
17982}
17983
17984fn destroyParkSem(_: ?*anyopaque) callconv(.c) void {
17985 _ = std.c._kern_delete_sem(park_sem);
17986}
17987
1795317988fn park(
1795417989 timeout: Io.Timeout,
1795517990 /// This value has no semantic effect, but may allow the OS to optimize the operation.
......@@ -18015,6 +18050,27 @@ fn park(
1801518050 }
1801618051 },
1801718052 .illumos => @panic("TODO: illumos lwp_park"),
18053 .haiku => {
18054 const timeout_flags: u32, const timeout_us = switch (timeout) {
18055 .none => .{ 0, 0 },
18056 .deadline => |deadline| .{
18057 if (deadline.clock == .real) std.c.B_ABSOLUTE_TIMEOUT | std.c.B_TIMEOUT_REAL_TIME_BASE else std.c.B_ABSOLUTE_TIMEOUT,
18058 deadline.raw.toMicroseconds(),
18059 },
18060 .duration => |duration| .{
18061 if (duration.clock == .real) std.c.B_ABSOLUTE_TIMEOUT | std.c.B_TIMEOUT_REAL_TIME_BASE else std.c.B_ABSOLUTE_TIMEOUT,
18062 nowPosix(duration.clock).addDuration(duration.raw).toMicroseconds(),
18063 },
18064 };
18065 while (true) {
18066 switch (std.c._kern_acquire_sem_etc(park_sem, 1, timeout_flags, timeout_us)) {
18067 0 => return,
18068 std.c.E.B_TIMED_OUT => return error.Timeout,
18069 std.c.E.B_INTERRUPTED => {},
18070 else => unreachable,
18071 }
18072 }
18073 },
1801818074 else => comptime unreachable,
1801918075 }
1802018076}
......@@ -18057,6 +18113,14 @@ fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {
1805718113 }
1805818114 },
1805918115 .illumos => @panic("TODO: illumos lwp_unpark"),
18116 .haiku => {
18117 for (tids) |tid| {
18118 switch (std.c._kern_release_sem_etc(tid, 1, 0)) {
18119 0 => {},
18120 else => recoverableOsBugDetected(),
18121 }
18122 }
18123 },
1806018124 else => comptime unreachable,
1806118125 }
1806218126}
lib/std/c.zig+8
......@@ -2991,6 +2991,7 @@ pub const SIG = switch (native_os) {
29912991 pub const UNBLOCK = 2;
29922992 pub const SETMASK = 3;
29932993
2994 pub const IO: SIG = .POLL;
29942995 pub const IOT: SIG = .ABRT;
29952996
29962997 HUP = 1,
......@@ -11267,15 +11268,22 @@ pub const signalfd_siginfo = illumos.signalfd_siginfo;
1126711268pub const taskid_t = illumos.taskid_t;
1126811269pub const zoneid_t = illumos.zoneid_t;
1126911270
11271pub const B_ABSOLUTE_TIMEOUT = haiku.B_ABSOLUTE_TIMEOUT;
1127011272pub const B_OS_NAME_LENGTH = haiku.B_OS_NAME_LENGTH;
11273pub const B_TIMEOUT_REAL_TIME_BASE = haiku.B_TIMEOUT_REAL_TIME_BASE;
1127111274pub const DirEnt = haiku.DirEnt;
11275pub const _kern_acquire_sem_etc = haiku._kern_acquire_sem_etc;
11276pub const _kern_create_sem = haiku._kern_create_sem;
11277pub const _kern_delete_sem = haiku._kern_delete_sem;
1127211278pub const _kern_open_dir = haiku._kern_open_dir;
1127311279pub const _kern_read_dir = haiku._kern_read_dir;
1127411280pub const _kern_read_stat = haiku._kern_read_stat;
11281pub const _kern_release_sem_etc = haiku._kern_release_sem_etc;
1127511282pub const _kern_rewind_dir = haiku._kern_rewind_dir;
1127611283pub const area_id = haiku.area_id;
1127711284pub const find_thread = haiku.find_thread;
1127811285pub const get_system_info = haiku.get_system_info;
11286pub const on_exit_thread = haiku.on_exit_thread;
1127911287pub const port_id = haiku.port_id;
1128011288pub const readv_pos = haiku.readv_pos;
1128111289pub const sem_id = haiku.sem_id;
lib/std/c/haiku.zig+7
......@@ -11,12 +11,19 @@ comptime {
1111}
1212
1313pub const B_OS_NAME_LENGTH = 32;
14pub const B_ABSOLUTE_TIMEOUT = 0x10;
15pub const B_TIMEOUT_REAL_TIME_BASE = 0x40;
1416
17pub extern "root" fn _kern_create_sem(count: c_int, name: ?[*:0]const u8) sem_id;
18pub extern "root" fn _kern_delete_sem(id: sem_id) status_t;
19pub extern "root" fn _kern_acquire_sem_etc(id: sem_id, count: u32, flags: u32, timeout: i64) status_t;
20pub extern "root" fn _kern_release_sem_etc(id: sem_id, count: u32, flags: u32) status_t;
1521pub extern "root" fn _kern_open_dir(fd: fd_t, path: [*:0]const u8) fd_t;
1622pub extern "root" fn _kern_read_dir(fd: fd_t, buffer: [*]u8, bufferSize: usize, maxCount: u32) isize;
1723pub extern "root" fn _kern_rewind_dir(fd: fd_t) status_t;
1824pub extern "root" fn _kern_read_stat(fd: fd_t, path: [*:0]const u8, traverseLink: bool, stat: *std.c.Stat, statSize: usize) status_t;
1925
26pub extern "root" fn on_exit_thread(callback: *const fn (?*anyopaque) callconv(.c) void, data: ?*anyopaque) status_t;
2027pub extern "root" fn find_thread(name: ?[*:0]const u8) thread_id;
2128pub extern "root" fn get_system_info(info: *system_info) status_t;
2229