Skip to content

Commit 321a739

Browse files
committed
Add a Futex2 Bitset type for futex2 wake and wait syscalls
Cleanup linux/test.zig type futex/2_* uaddr as *const u32 consider changing to *const atomic.Value(u32) Use At Flags in fstatat Use EpollOp in epoll_ctl syscall Signed-off-by: Bernard Assan <mega.alpha100@gmail.com>
1 parent a1714dc commit 321a739

File tree

3 files changed

+139
-62
lines changed

3 files changed

+139
-62
lines changed

lib/std/Thread.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1545,7 +1545,7 @@ const LinuxThreadImpl = struct {
15451545
if (tid == 0) break;
15461546

15471547
switch (linux.E.init(linux.futex_4arg(
1548-
&self.thread.child_tid.raw,
1548+
@ptrCast(&self.thread.child_tid.raw),
15491549
.{ .cmd = .WAIT, .private = false },
15501550
@bitCast(tid),
15511551
null,

lib/std/os/linux.zig

Lines changed: 99 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,14 @@ pub const futex_param4 = extern union {
690690
///
691691
/// The futex_op parameter is a sub-command and flags. The sub-command
692692
/// defines which of the subsequent paramters are relevant.
693-
pub fn futex(uaddr: *const anyopaque, futex_op: FUTEX_OP, val: u32, val2timeout: futex_param4, uaddr2: ?*const anyopaque, val3: u32) usize {
693+
pub fn futex(
694+
uaddr: *const u32,
695+
futex_op: FUTEX_OP,
696+
val: u32,
697+
val2timeout: futex_param4,
698+
uaddr2: ?*const anyopaque,
699+
val3: u32,
700+
) usize {
694701
return syscall6(
695702
if (@hasField(SYS, "futex") and native_arch != .hexagon) .futex else .futex_time64,
696703
@intFromPtr(uaddr),
@@ -704,7 +711,7 @@ pub fn futex(uaddr: *const anyopaque, futex_op: FUTEX_OP, val: u32, val2timeout:
704711

705712
/// Three-argument variation of the v1 futex call. Only suitable for a
706713
/// futex_op that ignores the remaining arguments (e.g., FUTUX_OP.WAKE).
707-
pub fn futex_3arg(uaddr: *const anyopaque, futex_op: FUTEX_OP, val: u32) usize {
714+
pub fn futex_3arg(uaddr: *const u32, futex_op: FUTEX_OP, val: u32) usize {
708715
return syscall3(
709716
if (@hasField(SYS, "futex") and native_arch != .hexagon) .futex else .futex_time64,
710717
@intFromPtr(uaddr),
@@ -715,7 +722,7 @@ pub fn futex_3arg(uaddr: *const anyopaque, futex_op: FUTEX_OP, val: u32) usize {
715722

716723
/// Four-argument variation on the v1 futex call. Only suitable for
717724
/// futex_op that ignores the remaining arguments (e.g., FUTEX_OP.WAIT).
718-
pub fn futex_4arg(uaddr: *const anyopaque, futex_op: FUTEX_OP, val: u32, timeout: ?*const timespec) usize {
725+
pub fn futex_4arg(uaddr: *const u32, futex_op: FUTEX_OP, val: u32, timeout: ?*const timespec) usize {
719726
return syscall4(
720727
if (@hasField(SYS, "futex") and native_arch != .hexagon) .futex else .futex_time64,
721728
@intFromPtr(uaddr),
@@ -754,7 +761,7 @@ pub fn futex2_waitv(
754761
assert(futexes.len <= Futex2.waitone_max);
755762
return syscall5(
756763
.futex_waitv,
757-
@intFromPtr(futexes),
764+
@intFromPtr(futexes.ptr),
758765
@intCast(futexes.len),
759766
@as(u32, @bitCast(flags)),
760767
@intFromPtr(timeout),
@@ -769,7 +776,7 @@ pub fn futex2_waitv(
769776
/// Requires at least kernel v6.7.
770777
pub fn futex2_wait(
771778
/// Address of the futex to wait on.
772-
uaddr: *const anyopaque,
779+
uaddr: *const u32,
773780
/// Value of `uaddr`.
774781
val: usize,
775782
/// Bitmask to match against incoming wakeup masks. Must not be zero.
@@ -784,7 +791,7 @@ pub fn futex2_wait(
784791
.futex_wait,
785792
@intFromPtr(uaddr),
786793
val,
787-
@intFromEnum(mask),
794+
@intCast(mask.toInt()),
788795
@as(u32, @bitCast(flags)),
789796
@intFromPtr(timeout),
790797
@intFromEnum(clockid),
@@ -798,7 +805,7 @@ pub fn futex2_wait(
798805
/// Requires at least kernel v6.7.
799806
pub fn futex2_wake(
800807
/// Futex to wake
801-
uaddr: *const anyopaque,
808+
uaddr: *const u32,
802809
/// Bitmask to match against waiters.
803810
mask: Futex2.Bitset,
804811
/// Maximum number of waiters on the futex to wake.
@@ -808,7 +815,7 @@ pub fn futex2_wake(
808815
return syscall4(
809816
.futex_wake,
810817
@intFromPtr(uaddr),
811-
@intFromEnum(mask),
818+
@intCast(mask.toInt()),
812819
@intCast(nr_wake),
813820
@as(u32, @bitCast(flags)),
814821
);
@@ -2280,16 +2287,27 @@ pub fn lstat(pathname: [*:0]const u8, statbuf: *Stat) usize {
22802287
}
22812288
}
22822289

2283-
// TODO: flags is At Flags
2284-
pub fn fstatat(dirfd: i32, path: [*:0]const u8, stat_buf: *Stat, flags: u32) usize {
2290+
pub fn fstatat(dirfd: i32, path: [*:0]const u8, stat_buf: *Stat, flags: At) usize {
22852291
if (native_arch == .riscv32 or native_arch.isLoongArch()) {
22862292
// riscv32 and loongarch have made the interesting decision to not implement some of
22872293
// the older stat syscalls, including this one.
22882294
@compileError("No fstatat syscall on this architecture.");
22892295
} else if (@hasField(SYS, "fstatat64")) {
2290-
return syscall4(.fstatat64, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), @intFromPtr(stat_buf), flags);
2296+
return syscall4(
2297+
.fstatat64,
2298+
@as(usize, @bitCast(@as(isize, dirfd))),
2299+
@intFromPtr(path),
2300+
@intFromPtr(stat_buf),
2301+
@intCast(@as(u32, @bitCast(flags))),
2302+
);
22912303
} else {
2292-
return syscall4(.fstatat, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), @intFromPtr(stat_buf), flags);
2304+
return syscall4(
2305+
.fstatat,
2306+
@as(usize, @bitCast(@as(isize, dirfd))),
2307+
@intFromPtr(path),
2308+
@intFromPtr(stat_buf),
2309+
@bitCast(flags),
2310+
);
22932311
}
22942312
}
22952313

@@ -2459,8 +2477,14 @@ pub fn epoll_create1(flags: usize) usize {
24592477
return syscall1(.epoll_create1, flags);
24602478
}
24612479

2462-
pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: ?*epoll_event) usize {
2463-
return syscall4(.epoll_ctl, @as(usize, @bitCast(@as(isize, epoll_fd))), @as(usize, @intCast(op)), @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(ev));
2480+
pub fn epoll_ctl(epoll_fd: i32, op: EpollOp, fd: i32, ev: ?*epoll_event) usize {
2481+
return syscall4(
2482+
.epoll_ctl,
2483+
@as(usize, @bitCast(@as(isize, epoll_fd))),
2484+
@as(usize, @intFromEnum(op)),
2485+
@as(usize, @bitCast(@as(isize, fd))),
2486+
@intFromPtr(ev),
2487+
);
24642488
}
24652489

24662490
pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize {
@@ -3733,14 +3757,67 @@ pub const Futex2 = struct {
37333757
__reserved: u32 = 0,
37343758
};
37353759

3736-
pub const Bitset = enum(u64) {
3737-
/// matches FUTEX_WAIT_BITSET
3738-
wait = 9,
3739-
/// matches FUTEX_WAKE_BITSET
3740-
wake = 10,
3741-
/// bitset with all bits set for the FUTEX_xxx_BITSET OPs to request a
3742-
/// match of any bit.
3743-
match_any = 0xffffffff,
3760+
/// `Bitset` for `futex2_wait`, `futex2_wake`, `IoUring.futex_wait` and
3761+
/// `IoUring.futex_wake` operations
3762+
/// At least one bit must be set before performing supported operations
3763+
/// The bitset is stored in the kernel-internal state of a waiter. During a
3764+
/// wake operation, the same mask previously set during the wait call can
3765+
/// be used to select which waiters to woke up
3766+
/// See https://man7.org/linux/man-pages/man2/futex_wake_bitset.2const.html
3767+
/// `IoUring` supports a u64 `Bitset` while the raw syscalls uses only u32
3768+
/// bits of `Bitset`
3769+
pub const Bitset = packed struct(u64) {
3770+
waiter1: bool = false,
3771+
waiter2: bool = false,
3772+
waiter3: bool = false,
3773+
waiter4: bool = false,
3774+
waiter5: bool = false,
3775+
waiter6: bool = false,
3776+
waiter7: bool = false,
3777+
waiter8: bool = false,
3778+
waiter9: bool = false,
3779+
waiter10: bool = false,
3780+
waiter11: bool = false,
3781+
waiter12: bool = false,
3782+
waiter13: bool = false,
3783+
waiter14: bool = false,
3784+
waiter15: bool = false,
3785+
waiter16: bool = false,
3786+
waiter17: bool = false,
3787+
waiter18: bool = false,
3788+
waiter19: bool = false,
3789+
waiter20: bool = false,
3790+
waiter21: bool = false,
3791+
waiter22: bool = false,
3792+
waiter23: bool = false,
3793+
waiter24: bool = false,
3794+
waiter25: bool = false,
3795+
waiter26: bool = false,
3796+
waiter27: bool = false,
3797+
waiter28: bool = false,
3798+
waiter29: bool = false,
3799+
waiter30: bool = false,
3800+
waiter31: bool = false,
3801+
waiter32: bool = false,
3802+
io_uring_extra: u32 = 0,
3803+
3804+
/// `Bitset` with all bits set for the FUTEX_xxx_BITSET OPs to request a
3805+
/// match of any bit. matches FUTEX_BITSET_MATCH_ANY
3806+
pub const match_any: Bitset = @bitCast(@as(u64, 0x00000000ffffffff));
3807+
/// Bitset must not be empty, this is only useful in test
3808+
pub const empty: Bitset = .{};
3809+
3810+
/// Create from raw u64 value
3811+
pub fn fromInt(value: u64) Bitset {
3812+
const bitset: Bitset = @bitCast(value);
3813+
assert(bitset != empty);
3814+
return bitset;
3815+
}
3816+
3817+
/// Convert to raw u64 for syscall
3818+
pub fn toInt(self: Bitset) u64 {
3819+
return @bitCast(self);
3820+
}
37443821
};
37453822
};
37463823

0 commit comments

Comments
 (0)