rtl: add racy_read

This commit is contained in:
Loïc Grégoire
2026-10-24 22:20:21 -04:00
parent 0ffaa8df5b
commit 8958854a28
7 changed files with 16 additions and 21 deletions
+5
View File
@@ -30,3 +30,8 @@ pub fn read_struct(
return result; return result;
} }
/// Racily read a pointer on purpose.
pub fn racy_read(comptime T: type, ptr: *T) T {
return @atomicLoad(T, ptr, .unordered);
}
+4 -4
View File
@@ -24,8 +24,8 @@ pub fn SeqLockType(comptime T: type) type {
continue; continue;
} }
// Relaxed load memcpy is needed here since data races are UB. // Unordered load memcpy is needed here since data races are UB.
rtl.barrier.atomic_load_memcpy(&ret, &self.raw, .monotonic); rtl.barrier.atomic_load_memcpy(&ret, &self.raw, .unordered);
// Ensure the sequence read happens *after* the data is fully // Ensure the sequence read happens *after* the data is fully
// loaded. // loaded.
@@ -49,8 +49,8 @@ pub fn SeqLockType(comptime T: type) type {
// is incremented. // is incremented.
rtl.barrier.fence(.release); rtl.barrier.fence(.release);
// Relaxed store memcpy is needed here since data races are UB. // Unordered store memcpy is needed here since data races are UB.
rtl.barrier.atomic_store_memcpy(&self.raw, &data, .monotonic); rtl.barrier.atomic_store_memcpy(&self.raw, &data, .unordered);
self.sequence.store(seq + 2, .release); self.sequence.store(seq + 2, .release);
} }
+2 -3
View File
@@ -144,13 +144,12 @@ const Pool = struct {
/// Return whether or not a new worker thread should be added to this pool. /// Return whether or not a new worker thread should be added to this pool.
fn should_grow(self: *Pool) bool { fn should_grow(self: *Pool) bool {
// NOTE: lockless racy check whether or not the queue is empty // Lockless racy check whether or not the queue is empty
// and its active count. // and its active count.
// This is fine as this is best effort anyway and is a heuristic. // This is fine as this is best effort anyway and is a heuristic.
const is_empty = @atomicLoad( const is_empty = rtl.mem.racy_read(
*rtl.List.Entry, *rtl.List.Entry,
&self.queue.items.head.next, &self.queue.items.head.next,
.monotonic,
) == &self.queue.items.head; ) == &self.queue.items.head;
const active = self.queue.active.load(.monotonic); const active = self.queue.active.load(.monotonic);
+1 -5
View File
@@ -293,11 +293,7 @@ pub fn RingBufferType(data_size_bits: usize, avg_msg_bits: usize) type {
// Loading the ID here is purposely racey because // Loading the ID here is purposely racey because
// we check if the descriptor is valid anyway. // we check if the descriptor is valid anyway.
const id: Id = @truncate( const id: Id = @truncate(
@atomicLoad( rtl.mem.racy_read(usize, @ptrCast(&blk.id)),
usize,
@as(*usize, @ptrCast(&blk.id)),
.monotonic,
),
); );
const res = self.read_desc(id); const res = self.read_desc(id);
desc = res.desc; desc = res.desc;
+1 -2
View File
@@ -175,10 +175,9 @@ pub const Mutex = struct {
var gas: usize = 0; var gas: usize = 0;
while (@atomicLoad( while (rtl.mem.racy_read(
?*ke.Thread, ?*ke.Thread,
cpu_curtd, cpu_curtd,
.unordered,
) == owner and gas < optimistic_spins) : (gas += 1) { ) == owner and gas < optimistic_spins) : (gas += 1) {
if (m.state.load(.monotonic) == State.uncontended) { if (m.state.load(.monotonic) == State.uncontended) {
// Got released, retry acquiring it. // Got released, retry acquiring it.
+1 -5
View File
@@ -1779,11 +1779,7 @@ pub fn idle(_: ?*anyopaque) noreturn {
while (true) { while (true) {
// Racy but only a hint anyway // Racy but only a hint anyway
const next = @atomicLoad( const next = rtl.mem.racy_read(?*ke.Thread, &cpu.next_thread);
?*ke.Thread,
&cpu.next_thread,
.monotonic,
);
if (cpu.load.load(.monotonic) != 0 or next != null) { if (cpu.load.load(.monotonic) != 0 or next != null) {
std.atomic.spinLoopHint(); std.atomic.spinLoopHint();
+2 -2
View File
@@ -736,7 +736,7 @@ pub const Zone = struct {
// Local depot is empty, rebalance with the zone depot. // Local depot is empty, rebalance with the zone depot.
// Purposefully race against cpu_depot_size because locking is // Purposefully race against cpu_depot_size because locking is
// tricky here, we may get a stale value but it is harmless. // tricky here, we may get a stale value but it is harmless.
const n = @atomicLoad(usize, &self.cpu_depot_size, .monotonic); const n = rtl.mem.racy_read(usize, &self.cpu_depot_size);
self.alloc_depot_rebalance(n, cpu); self.alloc_depot_rebalance(n, cpu);
@@ -924,7 +924,7 @@ pub const Zone = struct {
// Local depot is empty, rebalance with the zone depot. // Local depot is empty, rebalance with the zone depot.
// Race explained in alloc. // Race explained in alloc.
const n = @atomicLoad(usize, &self.cpu_depot_size, .monotonic); const n = rtl.mem.racy_read(usize, &self.cpu_depot_size);
self.free_depot_rebalance(n, cpu); self.free_depot_rebalance(n, cpu);