authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-01 17:49:29-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-01 18:05:54-05:00
log9b8e23934bc87f1fd6a42cdfdd551212994b6e58
tree0ac63357b6504f7bf44e66002b0ccca49dc64b45
parent8bedb10939b511e39787b2693249d2d3e8102854
signaturelock-open Commit is signed but in an unrecognized format.

introduce --single-threaded build option

closes #1764 This adds another boolean to the test matrix; hopefully it does not inflate the time too much. std.event.Loop does not work with this option yet. See #1908

18 files changed, 246 insertions(+), 83 deletions(-)

doc/langref.html.in+19
......@@ -6714,6 +6714,25 @@ pub fn build(b: *Builder) void {
67146714 {#header_close#}
67156715 {#see_also|Compile Variables|Zig Build System|Undefined Behavior#}
67166716 {#header_close#}
6717
6718 {#header_open|Single Threaded Builds#}
6719 <p>Zig has a compile option <code>--single-threaded</code> which has the following effects:
6720 <ul>
6721 <li>{#link|@atomicLoad#} is emitted as a normal load.</li>
6722 <li>{#link|@atomicRmw#} is emitted as a normal memory load, modify, store.</li>
6723 <li>{#link|@fence#} becomes a no-op.</li>
6724 <li>Variables which have Thread Local Storage instead become globals. TODO thread local variables
6725 are not implemented yet.</li>
6726 <li>The overhead of {#link|Coroutines#} becomes equivalent to function call overhead.
6727 TODO: please note this will not be implemented until the upcoming Coroutine Rewrite</li>
6728 <li>The {#syntax#}@import("builtin").single_threaded{#endsyntax#} becomes {#syntax#}true{#endsyntax#}
6729 and therefore various userland APIs which read this variable become more efficient.
6730 For example {#syntax#}std.Mutex{#endsyntax#} becomes
6731 an empty data structure and all of its functions become no-ops.</li>
6732 </ul>
6733 </p>
6734 {#header_close#}
6735
67176736 {#header_open|Undefined Behavior#}
67186737 <p>
67196738 Zig has many instances of undefined behavior. If undefined behavior is
src/all_types.hpp+1
......@@ -1808,6 +1808,7 @@ struct CodeGen {
18081808 bool is_static;
18091809 bool strip_debug_symbols;
18101810 bool is_test_build;
1811 bool is_single_threaded;
18111812 bool is_native_target;
18121813 bool linker_rdynamic;
18131814 bool no_rosegment_workaround;
src/codegen.cpp+4
......@@ -118,6 +118,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
118118 g->string_literals_table.init(16);
119119 g->type_info_cache.init(32);
120120 g->is_test_build = false;
121 g->is_single_threaded = false;
121122 buf_resize(&g->global_asm, 0);
122123
123124 for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) {
......@@ -7377,6 +7378,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
73777378 buf_appendf(contents, "pub const endian = %s;\n", endian_str);
73787379 }
73797380 buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build));
7381 buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded));
73807382 buf_appendf(contents, "pub const os = Os.%s;\n", cur_os);
73817383 buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch);
73827384 buf_appendf(contents, "pub const environ = Environ.%s;\n", cur_environ);
......@@ -7411,6 +7413,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
74117413 cache_buf(&cache_hash, compiler_id);
74127414 cache_int(&cache_hash, g->build_mode);
74137415 cache_bool(&cache_hash, g->is_test_build);
7416 cache_bool(&cache_hash, g->is_single_threaded);
74147417 cache_int(&cache_hash, g->zig_target.arch.arch);
74157418 cache_int(&cache_hash, g->zig_target.arch.sub_arch);
74167419 cache_int(&cache_hash, g->zig_target.vendor);
......@@ -8329,6 +8332,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
83298332 cache_bool(ch, g->is_static);
83308333 cache_bool(ch, g->strip_debug_symbols);
83318334 cache_bool(ch, g->is_test_build);
8335 cache_bool(ch, g->is_single_threaded);
83328336 cache_bool(ch, g->is_native_target);
83338337 cache_bool(ch, g->linker_rdynamic);
83348338 cache_bool(ch, g->no_rosegment_workaround);
src/main.cpp+6
......@@ -59,6 +59,7 @@ static int print_full_usage(const char *arg0) {
5959 " --release-fast build with optimizations on and safety off\n"
6060 " --release-safe build with optimizations on and safety on\n"
6161 " --release-small build with size optimizations on and safety off\n"
62 " --single-threaded source may assume it is only used single-threaded\n"
6263 " --static output will be statically linked\n"
6364 " --strip exclude debug symbols\n"
6465 " --target-arch [name] specify target architecture\n"
......@@ -393,6 +394,7 @@ int main(int argc, char **argv) {
393394 bool no_rosegment_workaround = false;
394395 bool system_linker_hack = false;
395396 TargetSubsystem subsystem = TargetSubsystemAuto;
397 bool is_single_threaded = false;
396398
397399 if (argc >= 2 && strcmp(argv[1], "build") == 0) {
398400 Buf zig_exe_path_buf = BUF_INIT;
......@@ -550,6 +552,8 @@ int main(int argc, char **argv) {
550552 disable_pic = true;
551553 } else if (strcmp(arg, "--system-linker-hack") == 0) {
552554 system_linker_hack = true;
555 } else if (strcmp(arg, "--single-threaded") == 0) {
556 is_single_threaded = true;
553557 } else if (strcmp(arg, "--test-cmd-bin") == 0) {
554558 test_exec_args.append(nullptr);
555559 } else if (arg[1] == 'L' && arg[2] != 0) {
......@@ -816,6 +820,7 @@ int main(int argc, char **argv) {
816820 switch (cmd) {
817821 case CmdBuiltin: {
818822 CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir());
823 g->is_single_threaded = is_single_threaded;
819824 Buf *builtin_source = codegen_generate_builtin_source(g);
820825 if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
821826 fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));
......@@ -889,6 +894,7 @@ int main(int argc, char **argv) {
889894 codegen_set_out_name(g, buf_out_name);
890895 codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
891896 codegen_set_is_test(g, cmd == CmdTest);
897 g->is_single_threaded = is_single_threaded;
892898 codegen_set_linker_script(g, linker_script);
893899 if (each_lib_rpath)
894900 codegen_set_each_lib_rpath(g, each_lib_rpath);
std/atomic/queue.zig+29-13
......@@ -170,20 +170,36 @@ test "std.atomic.Queue" {
170170 .get_count = 0,
171171 };
172172
173 var putters: [put_thread_count]*std.os.Thread = undefined;
174 for (putters) |*t| {
175 t.* = try std.os.spawnThread(&context, startPuts);
176 }
177 var getters: [put_thread_count]*std.os.Thread = undefined;
178 for (getters) |*t| {
179 t.* = try std.os.spawnThread(&context, startGets);
180 }
173 if (builtin.single_threaded) {
174 {
175 var i: usize = 0;
176 while (i < put_thread_count) : (i += 1) {
177 std.debug.assertOrPanic(startPuts(&context) == 0);
178 }
179 }
180 context.puts_done = 1;
181 {
182 var i: usize = 0;
183 while (i < put_thread_count) : (i += 1) {
184 std.debug.assertOrPanic(startGets(&context) == 0);
185 }
186 }
187 } else {
188 var putters: [put_thread_count]*std.os.Thread = undefined;
189 for (putters) |*t| {
190 t.* = try std.os.spawnThread(&context, startPuts);
191 }
192 var getters: [put_thread_count]*std.os.Thread = undefined;
193 for (getters) |*t| {
194 t.* = try std.os.spawnThread(&context, startGets);
195 }
181196
182 for (putters) |t|
183 t.wait();
184 _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
185 for (getters) |t|
186 t.wait();
197 for (putters) |t|
198 t.wait();
199 _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
200 for (getters) |t|
201 t.wait();
202 }
187203
188204 if (context.put_sum != context.get_sum) {
189205 std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
std/atomic/stack.zig+56-26
......@@ -4,10 +4,13 @@ const AtomicOrder = builtin.AtomicOrder;
44
55/// Many reader, many writer, non-allocating, thread-safe
66/// Uses a spinlock to protect push() and pop()
7/// When building in single threaded mode, this is a simple linked list.
78pub fn Stack(comptime T: type) type {
89 return struct {
910 root: ?*Node,
10 lock: u8,
11 lock: @typeOf(lock_init),
12
13 const lock_init = if (builtin.single_threaded) {} else u8(0);
1114
1215 pub const Self = @This();
1316
......@@ -19,7 +22,7 @@ pub fn Stack(comptime T: type) type {
1922 pub fn init() Self {
2023 return Self{
2124 .root = null,
22 .lock = 0,
25 .lock = lock_init,
2326 };
2427 }
2528
......@@ -31,20 +34,31 @@ pub fn Stack(comptime T: type) type {
3134 }
3235
3336 pub fn push(self: *Self, node: *Node) void {
34 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
35 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
36
37 node.next = self.root;
38 self.root = node;
37 if (builtin.single_threaded) {
38 node.next = self.root;
39 self.root = node;
40 } else {
41 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
42 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
43
44 node.next = self.root;
45 self.root = node;
46 }
3947 }
4048
4149 pub fn pop(self: *Self) ?*Node {
42 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
43 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
44
45 const root = self.root orelse return null;
46 self.root = root.next;
47 return root;
50 if (builtin.single_threaded) {
51 const root = self.root orelse return null;
52 self.root = root.next;
53 return root;
54 } else {
55 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
56 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
57
58 const root = self.root orelse return null;
59 self.root = root.next;
60 return root;
61 }
4862 }
4963
5064 pub fn isEmpty(self: *Self) bool {
......@@ -90,20 +104,36 @@ test "std.atomic.stack" {
90104 .get_count = 0,
91105 };
92106
93 var putters: [put_thread_count]*std.os.Thread = undefined;
94 for (putters) |*t| {
95 t.* = try std.os.spawnThread(&context, startPuts);
96 }
97 var getters: [put_thread_count]*std.os.Thread = undefined;
98 for (getters) |*t| {
99 t.* = try std.os.spawnThread(&context, startGets);
100 }
107 if (builtin.single_threaded) {
108 {
109 var i: usize = 0;
110 while (i < put_thread_count) : (i += 1) {
111 std.debug.assertOrPanic(startPuts(&context) == 0);
112 }
113 }
114 context.puts_done = 1;
115 {
116 var i: usize = 0;
117 while (i < put_thread_count) : (i += 1) {
118 std.debug.assertOrPanic(startGets(&context) == 0);
119 }
120 }
121 } else {
122 var putters: [put_thread_count]*std.os.Thread = undefined;
123 for (putters) |*t| {
124 t.* = try std.os.spawnThread(&context, startPuts);
125 }
126 var getters: [put_thread_count]*std.os.Thread = undefined;
127 for (getters) |*t| {
128 t.* = try std.os.spawnThread(&context, startGets);
129 }
101130
102 for (putters) |t|
103 t.wait();
104 _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
105 for (getters) |t|
106 t.wait();
131 for (putters) |t|
132 t.wait();
133 _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
134 for (getters) |t|
135 t.wait();
136 }
107137
108138 if (context.put_sum != context.get_sum) {
109139 std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
std/event/channel.zig+3
......@@ -319,6 +319,9 @@ pub fn Channel(comptime T: type) type {
319319}
320320
321321test "std.event.Channel" {
322 // https://github.com/ziglang/zig/issues/1908
323 if (builtin.single_threaded) return error.SkipZigTest;
324
322325 var da = std.heap.DirectAllocator.init();
323326 defer da.deinit();
324327
std/event/future.zig+3
......@@ -84,6 +84,9 @@ pub fn Future(comptime T: type) type {
8484}
8585
8686test "std.event.Future" {
87 // https://github.com/ziglang/zig/issues/1908
88 if (builtin.single_threaded) return error.SkipZigTest;
89
8790 var da = std.heap.DirectAllocator.init();
8891 defer da.deinit();
8992
std/event/group.zig+3
......@@ -121,6 +121,9 @@ pub fn Group(comptime ReturnType: type) type {
121121}
122122
123123test "std.event.Group" {
124 // https://github.com/ziglang/zig/issues/1908
125 if (builtin.single_threaded) return error.SkipZigTest;
126
124127 var da = std.heap.DirectAllocator.init();
125128 defer da.deinit();
126129
std/event/lock.zig+3
......@@ -122,6 +122,9 @@ pub const Lock = struct {
122122};
123123
124124test "std.event.Lock" {
125 // https://github.com/ziglang/zig/issues/1908
126 if (builtin.single_threaded) return error.SkipZigTest;
127
125128 var da = std.heap.DirectAllocator.init();
126129 defer da.deinit();
127130
std/event/loop.zig+22
......@@ -97,6 +97,7 @@ pub const Loop = struct {
9797 /// TODO copy elision / named return values so that the threads referencing *Loop
9898 /// have the correct pointer value.
9999 pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
100 if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode");
100101 const core_count = try os.cpuCount(allocator);
101102 return self.initInternal(allocator, core_count);
102103 }
......@@ -201,6 +202,11 @@ pub const Loop = struct {
201202 self.os_data.fs_thread.wait();
202203 }
203204
205 if (builtin.single_threaded) {
206 assert(extra_thread_count == 0);
207 return;
208 }
209
204210 var extra_thread_index: usize = 0;
205211 errdefer {
206212 // writing 8 bytes to an eventfd cannot fail
......@@ -301,6 +307,11 @@ pub const Loop = struct {
301307 self.os_data.fs_thread.wait();
302308 }
303309
310 if (builtin.single_threaded) {
311 assert(extra_thread_count == 0);
312 return;
313 }
314
304315 var extra_thread_index: usize = 0;
305316 errdefer {
306317 _ = os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null) catch unreachable;
......@@ -338,6 +349,11 @@ pub const Loop = struct {
338349 self.available_eventfd_resume_nodes.push(eventfd_node);
339350 }
340351
352 if (builtin.single_threaded) {
353 assert(extra_thread_count == 0);
354 return;
355 }
356
341357 var extra_thread_index: usize = 0;
342358 errdefer {
343359 var i: usize = 0;
......@@ -845,6 +861,9 @@ pub const Loop = struct {
845861};
846862
847863test "std.event.Loop - basic" {
864 // https://github.com/ziglang/zig/issues/1908
865 if (builtin.single_threaded) return error.SkipZigTest;
866
848867 var da = std.heap.DirectAllocator.init();
849868 defer da.deinit();
850869
......@@ -858,6 +877,9 @@ test "std.event.Loop - basic" {
858877}
859878
860879test "std.event.Loop - call" {
880 // https://github.com/ziglang/zig/issues/1908
881 if (builtin.single_threaded) return error.SkipZigTest;
882
861883 var da = std.heap.DirectAllocator.init();
862884 defer da.deinit();
863885
std/event/net.zig+3
......@@ -269,6 +269,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !os.File {
269269}
270270
271271test "listen on a port, send bytes, receive bytes" {
272 // https://github.com/ziglang/zig/issues/1908
273 if (builtin.single_threaded) return error.SkipZigTest;
274
272275 if (builtin.os != builtin.Os.linux) {
273276 // TODO build abstractions for other operating systems
274277 return error.SkipZigTest;
std/event/rwlock.zig+3
......@@ -211,6 +211,9 @@ pub const RwLock = struct {
211211};
212212
213213test "std.event.RwLock" {
214 // https://github.com/ziglang/zig/issues/1908
215 if (builtin.single_threaded) return error.SkipZigTest;
216
214217 var da = std.heap.DirectAllocator.init();
215218 defer da.deinit();
216219
std/mutex.zig+48-18
......@@ -14,7 +14,36 @@ const windows = std.os.windows;
1414/// If you need static initialization, use std.StaticallyInitializedMutex.
1515/// The Linux implementation is based on mutex3 from
1616/// https://www.akkadia.org/drepper/futex.pdf
17pub const Mutex = switch(builtin.os) {
17/// When an application is built in single threaded release mode, all the functions are
18/// no-ops. In single threaded debug mode, there is deadlock detection.
19pub const Mutex = if (builtin.single_threaded)
20 struct {
21 lock: @typeOf(lock_init),
22
23 const lock_init = if (std.debug.runtime_safety) false else {};
24
25 pub const Held = struct {
26 mutex: *Mutex,
27
28 pub fn release(self: Held) void {
29 if (std.debug.runtime_safety) {
30 self.mutex.lock = false;
31 }
32 }
33 };
34 pub fn init() Mutex {
35 return Mutex{ .lock = lock_init };
36 }
37 pub fn deinit(self: *Mutex) void {}
38
39 pub fn acquire(self: *Mutex) Held {
40 if (std.debug.runtime_safety and self.lock) {
41 @panic("deadlock detected");
42 }
43 return Held{ .mutex = self };
44 }
45 }
46else switch (builtin.os) {
1847 builtin.Os.linux => struct {
1948 /// 0: unlocked
2049 /// 1: locked, no waiters
......@@ -39,9 +68,7 @@ pub const Mutex = switch(builtin.os) {
3968 };
4069
4170 pub fn init() Mutex {
42 return Mutex {
43 .lock = 0,
44 };
71 return Mutex{ .lock = 0 };
4572 }
4673
4774 pub fn deinit(self: *Mutex) void {}
......@@ -60,7 +87,7 @@ pub const Mutex = switch(builtin.os) {
6087 }
6188 c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
6289 }
63 return Held { .mutex = self };
90 return Held{ .mutex = self };
6491 }
6592 },
6693 // TODO once https://github.com/ziglang/zig/issues/287 (copy elision) is solved, we can make a
......@@ -78,21 +105,19 @@ pub const Mutex = switch(builtin.os) {
78105 mutex: *Mutex,
79106
80107 pub fn release(self: Held) void {
81 SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock });
108 SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.lock });
82109 }
83110 };
84111
85112 pub fn init() Mutex {
86 return Mutex {
87 .lock = SpinLock.init(),
88 };
113 return Mutex{ .lock = SpinLock.init() };
89114 }
90115
91116 pub fn deinit(self: *Mutex) void {}
92117
93118 pub fn acquire(self: *Mutex) Held {
94119 _ = self.lock.acquire();
95 return Held { .mutex = self };
120 return Held{ .mutex = self };
96121 }
97122 },
98123};
......@@ -122,15 +147,20 @@ test "std.Mutex" {
122147 .data = 0,
123148 };
124149
125 const thread_count = 10;
126 var threads: [thread_count]*std.os.Thread = undefined;
127 for (threads) |*t| {
128 t.* = try std.os.spawnThread(&context, worker);
129 }
130 for (threads) |t|
131 t.wait();
150 if (builtin.single_threaded) {
151 worker(&context);
152 std.debug.assertOrPanic(context.data == TestContext.incr_count);
153 } else {
154 const thread_count = 10;
155 var threads: [thread_count]*std.os.Thread = undefined;
156 for (threads) |*t| {
157 t.* = try std.os.spawnThread(&context, worker);
158 }
159 for (threads) |t|
160 t.wait();
132161
133 std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
162 std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
163 }
134164}
135165
136166fn worker(ctx: *TestContext) void {
std/os/index.zig+1
......@@ -3013,6 +3013,7 @@ pub const SpawnThreadError = error{
30133013/// where T is u8, noreturn, void, or !void
30143014/// caller must call wait on the returned thread
30153015pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread {
3016 if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode");
30163017 // TODO compile-time call graph analysis to determine stack upper bound
30173018 // https://github.com/ziglang/zig/issues/157
30183019 const default_stack_size = 8 * 1024 * 1024;
std/os/test.zig+4
......@@ -40,6 +40,8 @@ fn testThreadIdFn(thread_id: *os.Thread.Id) void {
4040}
4141
4242test "std.os.Thread.getCurrentId" {
43 if (builtin.single_threaded) return error.SkipZigTest;
44
4345 var thread_current_id: os.Thread.Id = undefined;
4446 const thread = try os.spawnThread(&thread_current_id, testThreadIdFn);
4547 const thread_id = thread.handle();
......@@ -53,6 +55,8 @@ test "std.os.Thread.getCurrentId" {
5355}
5456
5557test "spawn threads" {
58 if (builtin.single_threaded) return error.SkipZigTest;
59
5660 var shared_ctx: i32 = 1;
5761
5862 const thread1 = try std.os.spawnThread({}, start1);
std/statically_initialized_mutex.zig+13-8
......@@ -93,13 +93,18 @@ test "std.StaticallyInitializedMutex" {
9393 .data = 0,
9494 };
9595
96 const thread_count = 10;
97 var threads: [thread_count]*std.os.Thread = undefined;
98 for (threads) |*t| {
99 t.* = try std.os.spawnThread(&context, TestContext.worker);
100 }
101 for (threads) |t|
102 t.wait();
96 if (builtin.single_threaded) {
97 TestContext.worker(&context);
98 std.debug.assertOrPanic(context.data == TestContext.incr_count);
99 } else {
100 const thread_count = 10;
101 var threads: [thread_count]*std.os.Thread = undefined;
102 for (threads) |*t| {
103 t.* = try std.os.spawnThread(&context, TestContext.worker);
104 }
105 for (threads) |t|
106 t.wait();
103107
104 std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
108 std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
109 }
105110}
test/tests.zig+25-18
......@@ -163,25 +163,32 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons
163163 for (test_targets) |test_target| {
164164 const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch);
165165 for (modes) |mode| {
166 for ([]bool{
167 false,
168 true,
169 }) |link_libc| {
170 if (link_libc and !is_native) {
171 // don't assume we have a cross-compiling libc set up
172 continue;
173 }
174 const these_tests = b.addTest(root_src);
175 these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", name, @tagName(test_target.os), @tagName(test_target.arch), @tagName(mode), if (link_libc) "c" else "bare"));
176 these_tests.setFilter(test_filter);
177 these_tests.setBuildMode(mode);
178 if (!is_native) {
179 these_tests.setTarget(test_target.arch, test_target.os, test_target.environ);
180 }
181 if (link_libc) {
182 these_tests.linkSystemLibrary("c");
166 for ([]bool{ false, true }) |link_libc| {
167 for ([]bool{ false, true }) |single_threaded| {
168 if (link_libc and !is_native) {
169 // don't assume we have a cross-compiling libc set up
170 continue;
171 }
172 const these_tests = b.addTest(root_src);
173 these_tests.setNamePrefix(b.fmt(
174 "{}-{}-{}-{}-{}-{} ",
175 name,
176 @tagName(test_target.os),
177 @tagName(test_target.arch),
178 @tagName(mode),
179 if (link_libc) "c" else "bare",
180 if (single_threaded) "single" else "multi",
181 ));
182 these_tests.setFilter(test_filter);
183 these_tests.setBuildMode(mode);
184 if (!is_native) {
185 these_tests.setTarget(test_target.arch, test_target.os, test_target.environ);
186 }
187 if (link_libc) {
188 these_tests.linkSystemLibrary("c");
189 }
190 step.dependOn(&these_tests.step);
183191 }
184 step.dependOn(&these_tests.step);
185192 }
186193 }
187194 }