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 {...@@ -829,6 +829,7 @@ const Thread = struct {
829 /// Always released when `Status.cancelation` is set to `.parked`.829 /// Always released when `Status.cancelation` is set to `.parked`.
830 futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,830 futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,
831 unpark_flag: UnparkFlag,831 unpark_flag: UnparkFlag,
832 park_tid: if (ParkTid == std.Thread.Id) void else ParkTid,
832833
833 csprng: Csprng,834 csprng: Csprng,
834835
...@@ -1220,7 +1221,7 @@ const Thread = struct {...@@ -1220,7 +1221,7 @@ const Thread = struct {
1220 parking_futex.removeCanceledWaiter(futex_waiter);1221 parking_futex.removeCanceledWaiter(futex_waiter);
1221 }1222 }
1222 if (need_unpark_flag) setUnparkFlag(&thread.unpark_flag);1223 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);
1224 return false;1225 return false;
1225 },1226 },
12261227
...@@ -1749,6 +1750,7 @@ fn worker(t: *Threaded) void {...@@ -1749,6 +1750,7 @@ fn worker(t: *Threaded) void {
1749 .cancel_protection = .unblocked,1750 .cancel_protection = .unblocked,
1750 .futex_waiter = undefined,1751 .futex_waiter = undefined,
1751 .unpark_flag = unpark_flag_init,1752 .unpark_flag = unpark_flag_init,
1753 .park_tid = if (ParkTid == std.Thread.Id) {} else getParkTid(),
1752 .csprng = .uninitialized,1754 .csprng = .uninitialized,
1753 };1755 };
1754 Thread.current = &thread;1756 Thread.current = &thread;
...@@ -17430,6 +17432,7 @@ const use_parking_futex = switch (native_os) {...@@ -17430,6 +17432,7 @@ const use_parking_futex = switch (native_os) {
17430 .windows => true, // RtlWaitOnAddress is a userland implementation anyway17432 .windows => true, // RtlWaitOnAddress is a userland implementation anyway
17431 .netbsd => true, // NetBSD has `futex(2)`, but it's historically been quite buggy. TODO: evaluate whether it's okay to use now.17433 .netbsd => true, // NetBSD has `futex(2)`, but it's historically been quite buggy. TODO: evaluate whether it's okay to use now.
17432 .illumos => true, // Illumos has no futex mechanism17434 .illumos => true, // Illumos has no futex mechanism
17435 .haiku => true, // Haiku has no futex mechanism
17433 else => false,17436 else => false,
17434};17437};
17435const use_parking_sleep = switch (native_os) {17438const use_parking_sleep = switch (native_os) {
...@@ -17475,7 +17478,7 @@ const parking_futex = struct {...@@ -17475,7 +17478,7 @@ const parking_futex = struct {
17475 const Waiter = struct {17478 const Waiter = struct {
17476 node: std.DoublyLinkedList.Node,17479 node: std.DoublyLinkedList.Node,
17477 address: usize,17480 address: usize,
17478 tid: std.Thread.Id,17481 tid: ParkTid,
17479 /// `thread_status.cancelation` is `.parked` while the thread is waiting. The single thread17482 /// `thread_status.cancelation` is `.parked` while the thread is waiting. The single thread
17480 /// which atomically updates it (to `.none` or `.canceling`) is responsible for:17483 /// which atomically updates it (to `.none` or `.canceling`) is responsible for:
17481 ///17484 ///
...@@ -17516,7 +17519,7 @@ const parking_futex = struct {...@@ -17516,7 +17519,7 @@ const parking_futex = struct {
1751617519
17517 // Put the threadlocal access outside of the critical section.17520 // Put the threadlocal access outside of the critical section.
17518 const opt_thread = Thread.current;17521 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
17521 var waiter: Waiter = .{17524 var waiter: Waiter = .{
17522 .node = undefined, // populated by list append17525 .node = undefined, // populated by list append
...@@ -17764,7 +17767,12 @@ const parking_sleep = struct {...@@ -17764,7 +17767,12 @@ const parking_sleep = struct {
17764 },17767 },
17765 }17768 }
17766 }17769 }
17770
17767 // Uncancelable sleep; we expect not to be manually unparked.17771 // 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();
17768 var dummy_flag: UnparkFlag = unpark_flag_init;17776 var dummy_flag: UnparkFlag = unpark_flag_init;
17769 if (park(timeout, null, if (need_unpark_flag) &dummy_flag)) {17777 if (park(timeout, null, if (need_unpark_flag) &dummy_flag)) {
17770 unreachable; // unexpected unpark17778 unreachable; // unexpected unpark
...@@ -17803,7 +17811,7 @@ const ParkingMutex = struct {...@@ -17803,7 +17811,7 @@ const ParkingMutex = struct {
17803 /// Never modified once the `Waiter` is in the linked list.17811 /// Never modified once the `Waiter` is in the linked list.
17804 next: ?*Waiter,17812 next: ?*Waiter,
17805 /// Never modified once the `Waiter` is in the linked list.17813 /// Never modified once the `Waiter` is in the linked list.
17806 tid: std.Thread.Id,17814 tid: ParkTid,
17807 };17815 };
17808 fn lock(m: *ParkingMutex) void {17816 fn lock(m: *ParkingMutex) void {
17809 state: switch (State.unlocked) { // assume 'unlocked' to optimize for uncontended case17817 state: switch (State.unlocked) { // assume 'unlocked' to optimize for uncontended case
...@@ -17819,7 +17827,7 @@ const ParkingMutex = struct {...@@ -17819,7 +17827,7 @@ const ParkingMutex = struct {
1781917827
17820 .locked_once, _ => |last_state| {17828 .locked_once, _ => |last_state| {
17821 const old_waiter = last_state.waiter();17829 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();
17823 var waiter: Waiter = .{17831 var waiter: Waiter = .{
17824 .next = old_waiter,17832 .next = old_waiter,
17825 .unpark_flag = unpark_flag_init,17833 .unpark_flag = unpark_flag_init,
...@@ -17947,9 +17955,36 @@ fn setUnparkFlag(f: *UnparkFlag) void {...@@ -17947,9 +17955,36 @@ fn setUnparkFlag(f: *UnparkFlag) void {
17947/// but it seems that someone at Microsoft forgot how big their TIDs are supposed to be.17955/// but it seems that someone at Microsoft forgot how big their TIDs are supposed to be.
17948const UnparkTid = switch (native_os) {17956const UnparkTid = switch (native_os) {
17949 .windows => usize,17957 .windows => usize,
17958 else => ParkTid,
17959};
17960
17961const ParkTid = switch (native_os) {
17962 .haiku => std.c.sem_id,
17950 else => std.Thread.Id,17963 else => std.Thread.Id,
17951};17964};
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
17953fn park(17988fn park(
17954 timeout: Io.Timeout,17989 timeout: Io.Timeout,
17955 /// This value has no semantic effect, but may allow the OS to optimize the operation.17990 /// This value has no semantic effect, but may allow the OS to optimize the operation.
...@@ -18015,6 +18050,27 @@ fn park(...@@ -18015,6 +18050,27 @@ fn park(
18015 }18050 }
18016 },18051 },
18017 .illumos => @panic("TODO: illumos lwp_park"),18052 .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 },
18018 else => comptime unreachable,18074 else => comptime unreachable,
18019 }18075 }
18020}18076}
...@@ -18057,6 +18113,14 @@ fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {...@@ -18057,6 +18113,14 @@ fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {
18057 }18113 }
18058 },18114 },
18059 .illumos => @panic("TODO: illumos lwp_unpark"),18115 .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 },
18060 else => comptime unreachable,18124 else => comptime unreachable,
18061 }18125 }
18062}18126}
lib/std/c.zig+8
...@@ -2991,6 +2991,7 @@ pub const SIG = switch (native_os) {...@@ -2991,6 +2991,7 @@ pub const SIG = switch (native_os) {
2991 pub const UNBLOCK = 2;2991 pub const UNBLOCK = 2;
2992 pub const SETMASK = 3;2992 pub const SETMASK = 3;
29932993
2994 pub const IO: SIG = .POLL;
2994 pub const IOT: SIG = .ABRT;2995 pub const IOT: SIG = .ABRT;
29952996
2996 HUP = 1,2997 HUP = 1,
...@@ -11267,15 +11268,22 @@ pub const signalfd_siginfo = illumos.signalfd_siginfo;...@@ -11267,15 +11268,22 @@ pub const signalfd_siginfo = illumos.signalfd_siginfo;
11267pub const taskid_t = illumos.taskid_t;11268pub const taskid_t = illumos.taskid_t;
11268pub const zoneid_t = illumos.zoneid_t;11269pub const zoneid_t = illumos.zoneid_t;
1126911270
11271pub const B_ABSOLUTE_TIMEOUT = haiku.B_ABSOLUTE_TIMEOUT;
11270pub const B_OS_NAME_LENGTH = haiku.B_OS_NAME_LENGTH;11272pub const B_OS_NAME_LENGTH = haiku.B_OS_NAME_LENGTH;
11273pub const B_TIMEOUT_REAL_TIME_BASE = haiku.B_TIMEOUT_REAL_TIME_BASE;
11271pub const DirEnt = haiku.DirEnt;11274pub 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;
11272pub const _kern_open_dir = haiku._kern_open_dir;11278pub const _kern_open_dir = haiku._kern_open_dir;
11273pub const _kern_read_dir = haiku._kern_read_dir;11279pub const _kern_read_dir = haiku._kern_read_dir;
11274pub const _kern_read_stat = haiku._kern_read_stat;11280pub const _kern_read_stat = haiku._kern_read_stat;
11281pub const _kern_release_sem_etc = haiku._kern_release_sem_etc;
11275pub const _kern_rewind_dir = haiku._kern_rewind_dir;11282pub const _kern_rewind_dir = haiku._kern_rewind_dir;
11276pub const area_id = haiku.area_id;11283pub const area_id = haiku.area_id;
11277pub const find_thread = haiku.find_thread;11284pub const find_thread = haiku.find_thread;
11278pub const get_system_info = haiku.get_system_info;11285pub const get_system_info = haiku.get_system_info;
11286pub const on_exit_thread = haiku.on_exit_thread;
11279pub const port_id = haiku.port_id;11287pub const port_id = haiku.port_id;
11280pub const readv_pos = haiku.readv_pos;11288pub const readv_pos = haiku.readv_pos;
11281pub const sem_id = haiku.sem_id;11289pub const sem_id = haiku.sem_id;
lib/std/c/haiku.zig+7
...@@ -11,12 +11,19 @@ comptime {...@@ -11,12 +11,19 @@ comptime {
11}11}
1212
13pub const B_OS_NAME_LENGTH = 32;13pub 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;
15pub extern "root" fn _kern_open_dir(fd: fd_t, path: [*:0]const u8) fd_t;21pub extern "root" fn _kern_open_dir(fd: fd_t, path: [*:0]const u8) fd_t;
16pub extern "root" fn _kern_read_dir(fd: fd_t, buffer: [*]u8, bufferSize: usize, maxCount: u32) isize;22pub extern "root" fn _kern_read_dir(fd: fd_t, buffer: [*]u8, bufferSize: usize, maxCount: u32) isize;
17pub extern "root" fn _kern_rewind_dir(fd: fd_t) status_t;23pub extern "root" fn _kern_rewind_dir(fd: fd_t) status_t;
18pub extern "root" fn _kern_read_stat(fd: fd_t, path: [*:0]const u8, traverseLink: bool, stat: *std.c.Stat, statSize: usize) status_t;24pub 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;
20pub extern "root" fn find_thread(name: ?[*:0]const u8) thread_id;27pub extern "root" fn find_thread(name: ?[*:0]const u8) thread_id;
21pub extern "root" fn get_system_info(info: *system_info) status_t;28pub extern "root" fn get_system_info(info: *system_info) status_t;
2229