authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-06-13 22:05:03+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-06-13 22:05:03+01:00
log55b7187429fca74c60be80618fccec7253add6b9
treec9818cc45ea741e5991b75ac22c9bc23e64f6a4c
parent121d62044399da0957fe3bf80c87ef23d7cdb1a3
signaturelock-open Commit is signed but in an unrecognized format.

link: fix obvious race condition

Did you know that allocators reuse addresses? If not, then don't feel bad, because apparently I don't either! This dumb mistake was probably responsible for the CI failures on `master` yesterday.

2 files changed, 7 insertions(+), 6 deletions(-)

src/Zcu/PerThread.zig+1-1
...@@ -4395,7 +4395,7 @@ pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air, ou...@@ -4395,7 +4395,7 @@ pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air, ou
4395 };4395 };
4396 // release `out.value` with this store; synchronizes with acquire loads in `link`4396 // release `out.value` with this store; synchronizes with acquire loads in `link`
4397 out.status.store(if (success) .ready else .failed, .release);4397 out.status.store(if (success) .ready else .failed, .release);
4398 zcu.comp.link_task_queue.mirReady(zcu.comp, out);4398 zcu.comp.link_task_queue.mirReady(zcu.comp, func_index, out);
4399 if (zcu.pending_codegen_jobs.rmw(.Sub, 1, .monotonic) == 1) {4399 if (zcu.pending_codegen_jobs.rmw(.Sub, 1, .monotonic) == 1) {
4400 // Decremented to 0, so all done.4400 // Decremented to 0, so all done.
4401 zcu.codegen_prog_node.end();4401 zcu.codegen_prog_node.end();
src/link/Queue.zig+6-5
...@@ -64,7 +64,7 @@ state: union(enum) {...@@ -64,7 +64,7 @@ state: union(enum) {
64 finished,64 finished,
65 /// The link thread is not running or queued, because it is waiting for this MIR to be populated.65 /// The link thread is not running or queued, because it is waiting for this MIR to be populated.
66 /// Once codegen completes, it must call `mirReady` which will restart the link thread.66 /// Once codegen completes, it must call `mirReady` which will restart the link thread.
67 wait_for_mir: *ZcuTask.LinkFunc.SharedMir,67 wait_for_mir: InternPool.Index,
68},68},
6969
70/// In the worst observed case, MIR is around 50 times as large as AIR. More typically, the ratio is70/// In the worst observed case, MIR is around 50 times as large as AIR. More typically, the ratio is
...@@ -113,7 +113,7 @@ pub fn start(q: *Queue, comp: *Compilation) void {...@@ -113,7 +113,7 @@ pub fn start(q: *Queue, comp: *Compilation) void {
113113
114/// Called by codegen workers after they have populated a `ZcuTask.LinkFunc.SharedMir`. If the link114/// Called by codegen workers after they have populated a `ZcuTask.LinkFunc.SharedMir`. If the link
115/// thread was waiting for this MIR, it can resume.115/// thread was waiting for this MIR, it can resume.
116pub fn mirReady(q: *Queue, comp: *Compilation, mir: *ZcuTask.LinkFunc.SharedMir) void {116pub fn mirReady(q: *Queue, comp: *Compilation, func_index: InternPool.Index, mir: *ZcuTask.LinkFunc.SharedMir) void {
117 // We would like to assert that `mir` is not pending, but that would race with a worker thread117 // We would like to assert that `mir` is not pending, but that would race with a worker thread
118 // potentially freeing it.118 // potentially freeing it.
119 {119 {
...@@ -121,7 +121,7 @@ pub fn mirReady(q: *Queue, comp: *Compilation, mir: *ZcuTask.LinkFunc.SharedMir)...@@ -121,7 +121,7 @@ pub fn mirReady(q: *Queue, comp: *Compilation, mir: *ZcuTask.LinkFunc.SharedMir)
121 defer q.mutex.unlock();121 defer q.mutex.unlock();
122 switch (q.state) {122 switch (q.state) {
123 .finished, .running => return,123 .finished, .running => return,
124 .wait_for_mir => |wait_for| if (wait_for != mir) return,124 .wait_for_mir => |wait_for| if (wait_for != func_index) return,
125 }125 }
126 // We were waiting for `mir`, so we will restart the linker thread.126 // We were waiting for `mir`, so we will restart the linker thread.
127 q.state = .running;127 q.state = .running;
...@@ -171,7 +171,7 @@ pub fn enqueueZcu(q: *Queue, comp: *Compilation, task: ZcuTask) Allocator.Error!...@@ -171,7 +171,7 @@ pub fn enqueueZcu(q: *Queue, comp: *Compilation, task: ZcuTask) Allocator.Error!
171 }171 }
172 // Restart the linker thread, unless it would immediately be blocked172 // Restart the linker thread, unless it would immediately be blocked
173 if (task == .link_func and task.link_func.mir.status.load(.acquire) == .pending) {173 if (task == .link_func and task.link_func.mir.status.load(.acquire) == .pending) {
174 q.state = .{ .wait_for_mir = task.link_func.mir };174 q.state = .{ .wait_for_mir = task.link_func.func };
175 return;175 return;
176 }176 }
177 q.state = .running;177 q.state = .running;
...@@ -248,7 +248,7 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void {...@@ -248,7 +248,7 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void {
248 defer q.mutex.unlock();248 defer q.mutex.unlock();
249 if (status_ptr.load(.acquire) != .pending) break :pending;249 if (status_ptr.load(.acquire) != .pending) break :pending;
250 // We will stop for now, and get restarted once this MIR is ready.250 // We will stop for now, and get restarted once this MIR is ready.
251 q.state = .{ .wait_for_mir = task.link_func.mir };251 q.state = .{ .wait_for_mir = task.link_func.func };
252 q.flush_safety.unlock();252 q.flush_safety.unlock();
253 return;253 return;
254 }254 }
...@@ -273,6 +273,7 @@ const std = @import("std");...@@ -273,6 +273,7 @@ const std = @import("std");
273const assert = std.debug.assert;273const assert = std.debug.assert;
274const Allocator = std.mem.Allocator;274const Allocator = std.mem.Allocator;
275const Compilation = @import("../Compilation.zig");275const Compilation = @import("../Compilation.zig");
276const InternPool = @import("../InternPool.zig");
276const link = @import("../link.zig");277const link = @import("../link.zig");
277const PrelinkTask = link.PrelinkTask;278const PrelinkTask = link.PrelinkTask;
278const ZcuTask = link.ZcuTask;279const ZcuTask = link.ZcuTask;