authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-28 17:53:06-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-28 17:53:06-04:00
log96ecb402590df7a02526009f1630f27e14a0e77c
tree7c9816d48aec59f73228d474c67b682dfde9038b
parent4ac36d094c06b04c1c71d972d3f3e1187bccea95

add fuzz tests for std.atomic.Stack


3 files changed, 143 insertions(+), 13 deletions(-)

src/ir.cpp+5
......@@ -18184,6 +18184,11 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr
1818418184 } else {
1818518185 if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering))
1818618186 return ira->codegen->builtin_types.entry_invalid;
18187 if (ordering == AtomicOrderUnordered) {
18188 ir_add_error(ira, instruction->ordering,
18189 buf_sprintf("@atomicRmw atomic ordering must not be Unordered"));
18190 return ira->codegen->builtin_types.entry_invalid;
18191 }
1818718192 }
1818818193
1818918194 if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
std/atomic/stack.zig+82-5
......@@ -1,3 +1,6 @@
1const builtin = @import("builtin");
2const AtomicOrder = builtin.AtomicOrder;
3
14/// Many reader, many writer, non-allocating, thread-safe, lock-free
25pub fn Stack(comptime T: type) type {
36 return struct {
......@@ -20,26 +23,100 @@ pub fn Stack(comptime T: type) type {
2023 /// being the first item in the stack, returns the other item that was there.
2124 pub fn pushFirst(self: &Self, node: &Node) ?&Node {
2225 node.next = null;
23 return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.AcqRel, AtomicOrder.AcqRel);
26 return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst);
2427 }
2528
2629 pub fn push(self: &Self, node: &Node) void {
27 var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire);
30 var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst);
2831 while (true) {
2932 node.next = root;
30 root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? break;
33 root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break;
3134 }
3235 }
3336
3437 pub fn pop(self: &Self) ?&Node {
3538 var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire);
3639 while (true) {
37 root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.Release, AtomicOrder.Acquire) ?? return root;
40 root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root;
3841 }
3942 }
4043
4144 pub fn isEmpty(self: &Self) bool {
42 return @atomicLoad(?&Node, &self.root, AtomicOrder.Relaxed) == null;
45 return @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst) == null;
4346 }
4447 };
4548}
49
50const std = @import("std");
51const Context = struct {
52 allocator: &std.mem.Allocator,
53 stack: &Stack(i32),
54 put_sum: isize,
55 get_sum: isize,
56 puts_done: u8, // TODO make this a bool
57};
58const puts_per_thread = 1000;
59const put_thread_count = 3;
60
61test "std.atomic.stack" {
62 var direct_allocator = std.heap.DirectAllocator.init();
63 defer direct_allocator.deinit();
64
65 var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024);
66 defer direct_allocator.allocator.free(plenty_of_memory);
67
68 var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
69 var a = &fixed_buffer_allocator.allocator;
70
71 var stack = Stack(i32).init();
72 var context = Context {
73 .allocator = a,
74 .stack = &stack,
75 .put_sum = 0,
76 .get_sum = 0,
77 .puts_done = 0,
78 };
79
80 var putters: [put_thread_count]&std.os.Thread = undefined;
81 for (putters) |*t| {
82 *t = try std.os.spawnThreadAllocator(a, &context, startPuts);
83 }
84 var getters: [put_thread_count]&std.os.Thread = undefined;
85 for (getters) |*t| {
86 *t = try std.os.spawnThreadAllocator(a, &context, startGets);
87 }
88
89 for (putters) |t| t.wait();
90 _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
91 for (getters) |t| t.wait();
92
93 std.debug.assert(context.put_sum == context.get_sum);
94}
95
96fn startPuts(ctx: &Context) u8 {
97 var put_count: usize = puts_per_thread;
98 var r = std.rand.DefaultPrng.init(0xdeadbeef);
99 while (put_count != 0) : (put_count -= 1) {
100 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
101 const x = @bitCast(i32, r.random.scalar(u32));
102 const node = ctx.allocator.create(Stack(i32).Node) catch unreachable;
103 node.data = x;
104 ctx.stack.push(node);
105 _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
106 }
107 return 0;
108}
109
110fn startGets(ctx: &Context) u8 {
111 while (true) {
112 while (ctx.stack.pop()) |node| {
113 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
114 _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
115 }
116
117 if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {
118 break;
119 }
120 }
121 return 0;
122}
std/heap.zig+56-8
......@@ -47,13 +47,6 @@ pub const DirectAllocator = struct {
4747
4848 const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void;
4949
50 //pub const canary_bytes = []u8 {48, 239, 128, 46, 18, 49, 147, 9, 195, 59, 203, 3, 245, 54, 9, 122};
51 //pub const want_safety = switch (builtin.mode) {
52 // builtin.Mode.Debug => true,
53 // builtin.Mode.ReleaseSafe => true,
54 // else => false,
55 //};
56
5750 pub fn init() DirectAllocator {
5851 return DirectAllocator {
5952 .allocator = Allocator {
......@@ -298,7 +291,7 @@ pub const FixedBufferAllocator = struct {
298291
299292 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
300293 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
301 const addr = @ptrToInt(&self.buffer[self.end_index]);
294 const addr = @ptrToInt(self.buffer.ptr) + self.end_index;
302295 const rem = @rem(addr, alignment);
303296 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
304297 const adjusted_index = self.end_index + march_forward_bytes;
......@@ -325,6 +318,54 @@ pub const FixedBufferAllocator = struct {
325318 fn free(allocator: &Allocator, bytes: []u8) void { }
326319};
327320
321/// lock free
322pub const ThreadSafeFixedBufferAllocator = struct {
323 allocator: Allocator,
324 end_index: usize,
325 buffer: []u8,
326
327 pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator {
328 return ThreadSafeFixedBufferAllocator {
329 .allocator = Allocator {
330 .allocFn = alloc,
331 .reallocFn = realloc,
332 .freeFn = free,
333 },
334 .buffer = buffer,
335 .end_index = 0,
336 };
337 }
338
339 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
340 const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);
341 var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);
342 while (true) {
343 const addr = @ptrToInt(self.buffer.ptr) + end_index;
344 const rem = @rem(addr, alignment);
345 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
346 const adjusted_index = end_index + march_forward_bytes;
347 const new_end_index = adjusted_index + n;
348 if (new_end_index > self.buffer.len) {
349 return error.OutOfMemory;
350 }
351 end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index,
352 builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index .. new_end_index];
353 }
354 }
355
356 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
357 if (new_size <= old_mem.len) {
358 return old_mem[0..new_size];
359 } else {
360 const result = try alloc(allocator, new_size, alignment);
361 mem.copy(u8, result, old_mem);
362 return result;
363 }
364 }
365
366 fn free(allocator: &Allocator, bytes: []u8) void { }
367};
368
328369
329370
330371test "c_allocator" {
......@@ -363,6 +404,13 @@ test "FixedBufferAllocator" {
363404 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
364405}
365406
407test "ThreadSafeFixedBufferAllocator" {
408 var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
409
410 try testAllocator(&fixed_buffer_allocator.allocator);
411 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
412}
413
366414fn testAllocator(allocator: &mem.Allocator) !void {
367415 var slice = try allocator.alloc(&i32, 100);
368416