const FuzzMultiThreadedContext = struct
const FuzzMultiThreadedContext = struct {
testing_buf: []u8,
backing_buf: []u8,
io: std.Io,
ops: *ThreadOps,
const n_threads = 4;
const ThreadOps = struct {
/// Switches between two values for each time a run starts.
run: Run,
/// While this can be calculated as `n_threads - (i -| ops.items.len)`,
/// this also serves as `.release` synchronization for each thread.
running: u32,
instance: SafeAllocator,
i: usize,
items: []Op,
const Run = packed struct(u32) {
n: bool,
pad: u31 = 0,
fn wait(ptr: *Run, val: Run, io: std.Io) error{Canceled}!void {
assert(val.pad == 0);
while (true) {
// This cannot load a previous value since this thread previously loaded the
// latest value.
const prev = @atomicLoad(Run, ptr, .acquire);
assert(prev.pad == 0);
if (prev == val) break;
try io.futexWait(Run, ptr, prev);
}
}
fn next(r: Run) Run {
assert(r.pad == 0);
return .{ .n = !r.n };
}
};
const Op = union(fuzz_probs.Op) {
alloc: struct {
len: usize,
alignment: Alignment,
splat: ?u8,
/// Not embeded directly in the struct as a workaround for tsan since a
/// switch directly on `Op` loads the entire value non-atomically.
result: *MemoryDependency,
},
free: struct {
memory: *MemoryDependency,
alignment: Alignment,
splat: ?u8,
},
resize: Realloc,
remap: Realloc,
const Realloc = struct {
memory: *MemoryDependency,
alignment: Alignment,
new_len: usize,
splat: ?u8,
/// Not embeded directly in the struct as a workaround for tsan since a
/// switch directly on `Op` loads the entire value non-atomically.
result: *MemoryDependency,
};
const MemoryDependency = struct {
ready: std.Io.Event,
/// Null if the memory failed to be allocated
memory: ?[]u8,
const init: MemoryDependency = .{
.ready = .unset,
.memory = undefined,
};
fn get(dep: *MemoryDependency, io: std.Io) ?[]u8 {
dep.ready.waitUncancelable(io);
return dep.memory;
}
};
};
};
}