feature. See also
. The project being documented here (as the example) is the Zig library itself.
SafeAllocator.fuzzMultiThreaded
fn fuzzMultiThreaded(ctx: FuzzMultiThreadedContext, smith: *Smith) !void
File
Code
fn fuzzMultiThreaded(ctx: FuzzMultiThreadedContext, smith: *Smith) !void {
@disableInstrumentation();
var gpa_instance: std.heap.FixedBufferAllocator = .init(ctx.testing_buf);
const gpa = gpa_instance.allocator();
var op_count: u32 = 0;
while (!smith.eosWeighted(fuzz_probs.eos)) op_count += 1;
const Op = FuzzMultiThreadedContext.ThreadOps.Op;
const ops = gpa.alloc(Op, op_count) catch return error.SkipZigTest;
const op_results = gpa.alloc(Op.MemoryDependency, op_count) catch return error.SkipZigTest;
@memset(op_results, .init);
const allocs = gpa.alloc(struct {
memory: *FuzzMultiThreadedContext.ThreadOps.Op.MemoryDependency,
alignment: Alignment,
splat: ?u8,
}, op_count) catch return error.SkipZigTest;
var allocs_n: u32 = 0;
var expected_remaps: usize = 0;
const options = fuzz_probs.generateOptions(smith);
for (ops, op_results) |*op, *result| switch (fuzz_probs.generateOp(smith, allocs_n != 0)) {
.alloc => {
const splat = fuzz_probs.generateSplat(smith);
const will_memset = options.check_write_after_free or splat != null;
op.* = .{ .alloc = .{
.len = fuzz_probs.generateLen(smith, will_memset),
.alignment = smith.valueWeighted(Alignment, fuzz_probs.alignment),
.splat = splat,
.result = result,
} };
allocs[allocs_n] = .{
.memory = result,
.alignment = op.alloc.alignment,
.splat = splat,
};
allocs_n += 1;
},
.free => {
const i = smith.valueRangeLessThan(u32, 0, allocs_n);
op.* = .{ .free = .{
.memory = allocs[i].memory,
.alignment = allocs[i].alignment,
.splat = allocs[i].splat,
} };
allocs_n -= 1;
allocs[i] = allocs[allocs_n];
},
.resize, .remap => |kind| {
op.* = switch (kind) {
.remap => .{ .remap = undefined },
.resize => .{ .resize = undefined },
else => unreachable,
};
const realloc = switch (kind) {
.remap => &op.remap,
.resize => &op.resize,
else => unreachable,
};
expected_remaps += @intFromBool(kind == .remap);
const i = smith.valueRangeLessThan(u32, 0, allocs_n);
realloc.* = .{
.memory = allocs[i].memory,
.alignment = allocs[i].alignment,
.new_len = fuzz_probs.generateLen(smith, options.check_write_after_free),
.splat = allocs[i].splat,
.result = result,
};
allocs[i].memory = result;
},
};
const fails: []bool = gpa.alloc(bool, ops.len * 2 + smith.value(u8)) catch &.{};
const fixed_remaps: []bool = gpa.alloc(bool, expected_remaps + smith.value(u8)) catch &.{};
for (fails) |*f| f.* = smith.boolWeighted(31, 1);
for (fixed_remaps) |*f| f.* = smith.value(bool);
var backing_gpa_instance: FuzzMultiThreadedAllocator = .{
.gpa = gpa,
.fill = 0,
.active_allocs = 0,
.fail_i = 0,
.fixed_remap_i = 0,
.buf = ctx.backing_buf,
.fails = fails,
.fixed_remaps = fixed_remaps,
};
const backing_gpa = backing_gpa_instance.allocator();
ctx.ops.instance = .init(backing_gpa, options);
ctx.ops.i = 0;
ctx.ops.items = ops;
ctx.ops.running = FuzzMultiThreadedContext.n_threads;
@atomicStore(FuzzMultiThreadedContext.ThreadOps.Run, &ctx.ops.run, ctx.ops.run.next(), .release);
ctx.io.futexWake(FuzzMultiThreadedContext.ThreadOps.Run, &ctx.ops.run, math.maxInt(u32));
while (true) {
const prev_running = @atomicLoad(u32, &ctx.ops.running, .acquire);
if (prev_running == 0) break;
ctx.io.futexWaitUncancelable(u32, &ctx.ops.running, prev_running);
}
var expected_allocs = allocs_n;
for (allocs[0..allocs_n]) |a| {
expected_allocs -= @intFromBool(a.memory.memory == null);
}
try std.testing.expectEqual(expected_allocs, ctx.ops.instance.deinitLog(false));
try std.testing.expectEqual(0, backing_gpa_instance.active_allocs);
}