| ... | @@ -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, |
| 832 | | 833 | |
| 833 | csprng: Csprng, | 834 | csprng: Csprng, |
| 834 | | 835 | |
| ... | @@ -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 | }, |
| 1226 | | 1227 | |
| ... | @@ -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 anyway | 17432 | .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 mechanism | 17434 | .illumos => true, // Illumos has no futex mechanism |
| | 17435 | .haiku => true, // Haiku has no futex mechanism |
| 17433 | else => false, | 17436 | else => false, |
| 17434 | }; | 17437 | }; |
| 17435 | const use_parking_sleep = switch (native_os) { | 17438 | const 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 thread | 17482 | /// `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 { |
| 17516 | | 17519 | |
| 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(); |
| 17520 | | 17523 | |
| 17521 | var waiter: Waiter = .{ | 17524 | var waiter: Waiter = .{ |
| 17522 | .node = undefined, // populated by list append | 17525 | .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 unpark | 17778 | 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 case | 17817 | 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 { |
| 17819 | | 17827 | |
| 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. |
| 17948 | const UnparkTid = switch (native_os) { | 17956 | const UnparkTid = switch (native_os) { |
| 17949 | .windows => usize, | 17957 | .windows => usize, |
| | 17958 | else => ParkTid, |
| | 17959 | }; |
| | 17960 | |
| | 17961 | const ParkTid = switch (native_os) { |
| | 17962 | .haiku => std.c.sem_id, |
| 17950 | else => std.Thread.Id, | 17963 | else => std.Thread.Id, |
| 17951 | }; | 17964 | }; |
| 17952 | | 17965 | |
| | 17966 | threadlocal var park_sem: std.c.sem_id = -1; |
| | 17967 | |
| | 17968 | fn 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 | |
| | 17984 | fn destroyParkSem(_: ?*anyopaque) callconv(.c) void { |
| | 17985 | _ = std.c._kern_delete_sem(park_sem); |
| | 17986 | } |
| | 17987 | |
| 17953 | fn park( | 17988 | fn 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 | } |