mirror of
https://github.com/ziglang/zig.git
synced 2024-12-15 07:40:13 +00:00
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
This commit is contained in:
parent
8bedb10939
commit
9b8e23934b
@ -6714,6 +6714,25 @@ pub fn build(b: *Builder) void {
|
||||
{#header_close#}
|
||||
{#see_also|Compile Variables|Zig Build System|Undefined Behavior#}
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|Single Threaded Builds#}
|
||||
<p>Zig has a compile option <code>--single-threaded</code> which has the following effects:
|
||||
<ul>
|
||||
<li>{#link|@atomicLoad#} is emitted as a normal load.</li>
|
||||
<li>{#link|@atomicRmw#} is emitted as a normal memory load, modify, store.</li>
|
||||
<li>{#link|@fence#} becomes a no-op.</li>
|
||||
<li>Variables which have Thread Local Storage instead become globals. TODO thread local variables
|
||||
are not implemented yet.</li>
|
||||
<li>The overhead of {#link|Coroutines#} becomes equivalent to function call overhead.
|
||||
TODO: please note this will not be implemented until the upcoming Coroutine Rewrite</li>
|
||||
<li>The {#syntax#}@import("builtin").single_threaded{#endsyntax#} becomes {#syntax#}true{#endsyntax#}
|
||||
and therefore various userland APIs which read this variable become more efficient.
|
||||
For example {#syntax#}std.Mutex{#endsyntax#} becomes
|
||||
an empty data structure and all of its functions become no-ops.</li>
|
||||
</ul>
|
||||
</p>
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|Undefined Behavior#}
|
||||
<p>
|
||||
Zig has many instances of undefined behavior. If undefined behavior is
|
||||
|
@ -1808,6 +1808,7 @@ struct CodeGen {
|
||||
bool is_static;
|
||||
bool strip_debug_symbols;
|
||||
bool is_test_build;
|
||||
bool is_single_threaded;
|
||||
bool is_native_target;
|
||||
bool linker_rdynamic;
|
||||
bool no_rosegment_workaround;
|
||||
|
@ -118,6 +118,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
|
||||
g->string_literals_table.init(16);
|
||||
g->type_info_cache.init(32);
|
||||
g->is_test_build = false;
|
||||
g->is_single_threaded = false;
|
||||
buf_resize(&g->global_asm, 0);
|
||||
|
||||
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) {
|
||||
buf_appendf(contents, "pub const endian = %s;\n", endian_str);
|
||||
}
|
||||
buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build));
|
||||
buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded));
|
||||
buf_appendf(contents, "pub const os = Os.%s;\n", cur_os);
|
||||
buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch);
|
||||
buf_appendf(contents, "pub const environ = Environ.%s;\n", cur_environ);
|
||||
@ -7411,6 +7413,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
|
||||
cache_buf(&cache_hash, compiler_id);
|
||||
cache_int(&cache_hash, g->build_mode);
|
||||
cache_bool(&cache_hash, g->is_test_build);
|
||||
cache_bool(&cache_hash, g->is_single_threaded);
|
||||
cache_int(&cache_hash, g->zig_target.arch.arch);
|
||||
cache_int(&cache_hash, g->zig_target.arch.sub_arch);
|
||||
cache_int(&cache_hash, g->zig_target.vendor);
|
||||
@ -8329,6 +8332,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
|
||||
cache_bool(ch, g->is_static);
|
||||
cache_bool(ch, g->strip_debug_symbols);
|
||||
cache_bool(ch, g->is_test_build);
|
||||
cache_bool(ch, g->is_single_threaded);
|
||||
cache_bool(ch, g->is_native_target);
|
||||
cache_bool(ch, g->linker_rdynamic);
|
||||
cache_bool(ch, g->no_rosegment_workaround);
|
||||
|
@ -59,6 +59,7 @@ static int print_full_usage(const char *arg0) {
|
||||
" --release-fast build with optimizations on and safety off\n"
|
||||
" --release-safe build with optimizations on and safety on\n"
|
||||
" --release-small build with size optimizations on and safety off\n"
|
||||
" --single-threaded source may assume it is only used single-threaded\n"
|
||||
" --static output will be statically linked\n"
|
||||
" --strip exclude debug symbols\n"
|
||||
" --target-arch [name] specify target architecture\n"
|
||||
@ -393,6 +394,7 @@ int main(int argc, char **argv) {
|
||||
bool no_rosegment_workaround = false;
|
||||
bool system_linker_hack = false;
|
||||
TargetSubsystem subsystem = TargetSubsystemAuto;
|
||||
bool is_single_threaded = false;
|
||||
|
||||
if (argc >= 2 && strcmp(argv[1], "build") == 0) {
|
||||
Buf zig_exe_path_buf = BUF_INIT;
|
||||
@ -550,6 +552,8 @@ int main(int argc, char **argv) {
|
||||
disable_pic = true;
|
||||
} else if (strcmp(arg, "--system-linker-hack") == 0) {
|
||||
system_linker_hack = true;
|
||||
} else if (strcmp(arg, "--single-threaded") == 0) {
|
||||
is_single_threaded = true;
|
||||
} else if (strcmp(arg, "--test-cmd-bin") == 0) {
|
||||
test_exec_args.append(nullptr);
|
||||
} else if (arg[1] == 'L' && arg[2] != 0) {
|
||||
@ -816,6 +820,7 @@ int main(int argc, char **argv) {
|
||||
switch (cmd) {
|
||||
case CmdBuiltin: {
|
||||
CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir());
|
||||
g->is_single_threaded = is_single_threaded;
|
||||
Buf *builtin_source = codegen_generate_builtin_source(g);
|
||||
if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
|
||||
fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));
|
||||
@ -889,6 +894,7 @@ int main(int argc, char **argv) {
|
||||
codegen_set_out_name(g, buf_out_name);
|
||||
codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
|
||||
codegen_set_is_test(g, cmd == CmdTest);
|
||||
g->is_single_threaded = is_single_threaded;
|
||||
codegen_set_linker_script(g, linker_script);
|
||||
if (each_lib_rpath)
|
||||
codegen_set_each_lib_rpath(g, each_lib_rpath);
|
||||
|
@ -170,20 +170,36 @@ test "std.atomic.Queue" {
|
||||
.get_count = 0,
|
||||
};
|
||||
|
||||
var putters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (putters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startPuts);
|
||||
}
|
||||
var getters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (getters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startGets);
|
||||
}
|
||||
if (builtin.single_threaded) {
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < put_thread_count) : (i += 1) {
|
||||
std.debug.assertOrPanic(startPuts(&context) == 0);
|
||||
}
|
||||
}
|
||||
context.puts_done = 1;
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < put_thread_count) : (i += 1) {
|
||||
std.debug.assertOrPanic(startGets(&context) == 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var putters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (putters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startPuts);
|
||||
}
|
||||
var getters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (getters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startGets);
|
||||
}
|
||||
|
||||
for (putters) |t|
|
||||
t.wait();
|
||||
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||
for (getters) |t|
|
||||
t.wait();
|
||||
for (putters) |t|
|
||||
t.wait();
|
||||
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||
for (getters) |t|
|
||||
t.wait();
|
||||
}
|
||||
|
||||
if (context.put_sum != context.get_sum) {
|
||||
std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
|
||||
|
@ -4,10 +4,13 @@ const AtomicOrder = builtin.AtomicOrder;
|
||||
|
||||
/// Many reader, many writer, non-allocating, thread-safe
|
||||
/// Uses a spinlock to protect push() and pop()
|
||||
/// When building in single threaded mode, this is a simple linked list.
|
||||
pub fn Stack(comptime T: type) type {
|
||||
return struct {
|
||||
root: ?*Node,
|
||||
lock: u8,
|
||||
lock: @typeOf(lock_init),
|
||||
|
||||
const lock_init = if (builtin.single_threaded) {} else u8(0);
|
||||
|
||||
pub const Self = @This();
|
||||
|
||||
@ -19,7 +22,7 @@ pub fn Stack(comptime T: type) type {
|
||||
pub fn init() Self {
|
||||
return Self{
|
||||
.root = null,
|
||||
.lock = 0,
|
||||
.lock = lock_init,
|
||||
};
|
||||
}
|
||||
|
||||
@ -31,20 +34,31 @@ pub fn Stack(comptime T: type) type {
|
||||
}
|
||||
|
||||
pub fn push(self: *Self, node: *Node) void {
|
||||
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
|
||||
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
|
||||
if (builtin.single_threaded) {
|
||||
node.next = self.root;
|
||||
self.root = node;
|
||||
} else {
|
||||
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
|
||||
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
|
||||
|
||||
node.next = self.root;
|
||||
self.root = node;
|
||||
node.next = self.root;
|
||||
self.root = node;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop(self: *Self) ?*Node {
|
||||
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
|
||||
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
|
||||
if (builtin.single_threaded) {
|
||||
const root = self.root orelse return null;
|
||||
self.root = root.next;
|
||||
return root;
|
||||
} else {
|
||||
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
|
||||
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
|
||||
|
||||
const root = self.root orelse return null;
|
||||
self.root = root.next;
|
||||
return root;
|
||||
const root = self.root orelse return null;
|
||||
self.root = root.next;
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isEmpty(self: *Self) bool {
|
||||
@ -90,20 +104,36 @@ test "std.atomic.stack" {
|
||||
.get_count = 0,
|
||||
};
|
||||
|
||||
var putters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (putters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startPuts);
|
||||
}
|
||||
var getters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (getters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startGets);
|
||||
}
|
||||
if (builtin.single_threaded) {
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < put_thread_count) : (i += 1) {
|
||||
std.debug.assertOrPanic(startPuts(&context) == 0);
|
||||
}
|
||||
}
|
||||
context.puts_done = 1;
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < put_thread_count) : (i += 1) {
|
||||
std.debug.assertOrPanic(startGets(&context) == 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var putters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (putters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startPuts);
|
||||
}
|
||||
var getters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (getters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startGets);
|
||||
}
|
||||
|
||||
for (putters) |t|
|
||||
t.wait();
|
||||
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||
for (getters) |t|
|
||||
t.wait();
|
||||
for (putters) |t|
|
||||
t.wait();
|
||||
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||
for (getters) |t|
|
||||
t.wait();
|
||||
}
|
||||
|
||||
if (context.put_sum != context.get_sum) {
|
||||
std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
|
||||
|
@ -319,6 +319,9 @@ pub fn Channel(comptime T: type) type {
|
||||
}
|
||||
|
||||
test "std.event.Channel" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
|
@ -84,6 +84,9 @@ pub fn Future(comptime T: type) type {
|
||||
}
|
||||
|
||||
test "std.event.Future" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
|
@ -121,6 +121,9 @@ pub fn Group(comptime ReturnType: type) type {
|
||||
}
|
||||
|
||||
test "std.event.Group" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
|
@ -122,6 +122,9 @@ pub const Lock = struct {
|
||||
};
|
||||
|
||||
test "std.event.Lock" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
|
@ -97,6 +97,7 @@ pub const Loop = struct {
|
||||
/// TODO copy elision / named return values so that the threads referencing *Loop
|
||||
/// have the correct pointer value.
|
||||
pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
|
||||
if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode");
|
||||
const core_count = try os.cpuCount(allocator);
|
||||
return self.initInternal(allocator, core_count);
|
||||
}
|
||||
@ -201,6 +202,11 @@ pub const Loop = struct {
|
||||
self.os_data.fs_thread.wait();
|
||||
}
|
||||
|
||||
if (builtin.single_threaded) {
|
||||
assert(extra_thread_count == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
var extra_thread_index: usize = 0;
|
||||
errdefer {
|
||||
// writing 8 bytes to an eventfd cannot fail
|
||||
@ -301,6 +307,11 @@ pub const Loop = struct {
|
||||
self.os_data.fs_thread.wait();
|
||||
}
|
||||
|
||||
if (builtin.single_threaded) {
|
||||
assert(extra_thread_count == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
var extra_thread_index: usize = 0;
|
||||
errdefer {
|
||||
_ = os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null) catch unreachable;
|
||||
@ -338,6 +349,11 @@ pub const Loop = struct {
|
||||
self.available_eventfd_resume_nodes.push(eventfd_node);
|
||||
}
|
||||
|
||||
if (builtin.single_threaded) {
|
||||
assert(extra_thread_count == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
var extra_thread_index: usize = 0;
|
||||
errdefer {
|
||||
var i: usize = 0;
|
||||
@ -845,6 +861,9 @@ pub const Loop = struct {
|
||||
};
|
||||
|
||||
test "std.event.Loop - basic" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
@ -858,6 +877,9 @@ test "std.event.Loop - basic" {
|
||||
}
|
||||
|
||||
test "std.event.Loop - call" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
|
@ -269,6 +269,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !os.File {
|
||||
}
|
||||
|
||||
test "listen on a port, send bytes, receive bytes" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
if (builtin.os != builtin.Os.linux) {
|
||||
// TODO build abstractions for other operating systems
|
||||
return error.SkipZigTest;
|
||||
|
@ -211,6 +211,9 @@ pub const RwLock = struct {
|
||||
};
|
||||
|
||||
test "std.event.RwLock" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
|
@ -14,7 +14,36 @@ const windows = std.os.windows;
|
||||
/// If you need static initialization, use std.StaticallyInitializedMutex.
|
||||
/// The Linux implementation is based on mutex3 from
|
||||
/// https://www.akkadia.org/drepper/futex.pdf
|
||||
pub const Mutex = switch(builtin.os) {
|
||||
/// When an application is built in single threaded release mode, all the functions are
|
||||
/// no-ops. In single threaded debug mode, there is deadlock detection.
|
||||
pub const Mutex = if (builtin.single_threaded)
|
||||
struct {
|
||||
lock: @typeOf(lock_init),
|
||||
|
||||
const lock_init = if (std.debug.runtime_safety) false else {};
|
||||
|
||||
pub const Held = struct {
|
||||
mutex: *Mutex,
|
||||
|
||||
pub fn release(self: Held) void {
|
||||
if (std.debug.runtime_safety) {
|
||||
self.mutex.lock = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
pub fn init() Mutex {
|
||||
return Mutex{ .lock = lock_init };
|
||||
}
|
||||
pub fn deinit(self: *Mutex) void {}
|
||||
|
||||
pub fn acquire(self: *Mutex) Held {
|
||||
if (std.debug.runtime_safety and self.lock) {
|
||||
@panic("deadlock detected");
|
||||
}
|
||||
return Held{ .mutex = self };
|
||||
}
|
||||
}
|
||||
else switch (builtin.os) {
|
||||
builtin.Os.linux => struct {
|
||||
/// 0: unlocked
|
||||
/// 1: locked, no waiters
|
||||
@ -39,9 +68,7 @@ pub const Mutex = switch(builtin.os) {
|
||||
};
|
||||
|
||||
pub fn init() Mutex {
|
||||
return Mutex {
|
||||
.lock = 0,
|
||||
};
|
||||
return Mutex{ .lock = 0 };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Mutex) void {}
|
||||
@ -60,7 +87,7 @@ pub const Mutex = switch(builtin.os) {
|
||||
}
|
||||
c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
|
||||
}
|
||||
return Held { .mutex = self };
|
||||
return Held{ .mutex = self };
|
||||
}
|
||||
},
|
||||
// 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) {
|
||||
mutex: *Mutex,
|
||||
|
||||
pub fn release(self: Held) void {
|
||||
SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock });
|
||||
SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.lock });
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init() Mutex {
|
||||
return Mutex {
|
||||
.lock = SpinLock.init(),
|
||||
};
|
||||
return Mutex{ .lock = SpinLock.init() };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Mutex) void {}
|
||||
|
||||
pub fn acquire(self: *Mutex) Held {
|
||||
_ = self.lock.acquire();
|
||||
return Held { .mutex = self };
|
||||
return Held{ .mutex = self };
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -122,15 +147,20 @@ test "std.Mutex" {
|
||||
.data = 0,
|
||||
};
|
||||
|
||||
const thread_count = 10;
|
||||
var threads: [thread_count]*std.os.Thread = undefined;
|
||||
for (threads) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, worker);
|
||||
}
|
||||
for (threads) |t|
|
||||
t.wait();
|
||||
if (builtin.single_threaded) {
|
||||
worker(&context);
|
||||
std.debug.assertOrPanic(context.data == TestContext.incr_count);
|
||||
} else {
|
||||
const thread_count = 10;
|
||||
var threads: [thread_count]*std.os.Thread = undefined;
|
||||
for (threads) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, worker);
|
||||
}
|
||||
for (threads) |t|
|
||||
t.wait();
|
||||
|
||||
std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
|
||||
std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
|
||||
}
|
||||
}
|
||||
|
||||
fn worker(ctx: *TestContext) void {
|
||||
|
@ -3013,6 +3013,7 @@ pub const SpawnThreadError = error{
|
||||
/// where T is u8, noreturn, void, or !void
|
||||
/// caller must call wait on the returned thread
|
||||
pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread {
|
||||
if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode");
|
||||
// TODO compile-time call graph analysis to determine stack upper bound
|
||||
// https://github.com/ziglang/zig/issues/157
|
||||
const default_stack_size = 8 * 1024 * 1024;
|
||||
|
@ -40,6 +40,8 @@ fn testThreadIdFn(thread_id: *os.Thread.Id) void {
|
||||
}
|
||||
|
||||
test "std.os.Thread.getCurrentId" {
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var thread_current_id: os.Thread.Id = undefined;
|
||||
const thread = try os.spawnThread(&thread_current_id, testThreadIdFn);
|
||||
const thread_id = thread.handle();
|
||||
@ -53,6 +55,8 @@ test "std.os.Thread.getCurrentId" {
|
||||
}
|
||||
|
||||
test "spawn threads" {
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var shared_ctx: i32 = 1;
|
||||
|
||||
const thread1 = try std.os.spawnThread({}, start1);
|
||||
|
@ -93,13 +93,18 @@ test "std.StaticallyInitializedMutex" {
|
||||
.data = 0,
|
||||
};
|
||||
|
||||
const thread_count = 10;
|
||||
var threads: [thread_count]*std.os.Thread = undefined;
|
||||
for (threads) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, TestContext.worker);
|
||||
}
|
||||
for (threads) |t|
|
||||
t.wait();
|
||||
if (builtin.single_threaded) {
|
||||
TestContext.worker(&context);
|
||||
std.debug.assertOrPanic(context.data == TestContext.incr_count);
|
||||
} else {
|
||||
const thread_count = 10;
|
||||
var threads: [thread_count]*std.os.Thread = undefined;
|
||||
for (threads) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, TestContext.worker);
|
||||
}
|
||||
for (threads) |t|
|
||||
t.wait();
|
||||
|
||||
std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
|
||||
std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
|
||||
}
|
||||
}
|
||||
|
@ -163,25 +163,32 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons
|
||||
for (test_targets) |test_target| {
|
||||
const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch);
|
||||
for (modes) |mode| {
|
||||
for ([]bool{
|
||||
false,
|
||||
true,
|
||||
}) |link_libc| {
|
||||
if (link_libc and !is_native) {
|
||||
// don't assume we have a cross-compiling libc set up
|
||||
continue;
|
||||
for ([]bool{ false, true }) |link_libc| {
|
||||
for ([]bool{ false, true }) |single_threaded| {
|
||||
if (link_libc and !is_native) {
|
||||
// don't assume we have a cross-compiling libc set up
|
||||
continue;
|
||||
}
|
||||
const these_tests = b.addTest(root_src);
|
||||
these_tests.setNamePrefix(b.fmt(
|
||||
"{}-{}-{}-{}-{}-{} ",
|
||||
name,
|
||||
@tagName(test_target.os),
|
||||
@tagName(test_target.arch),
|
||||
@tagName(mode),
|
||||
if (link_libc) "c" else "bare",
|
||||
if (single_threaded) "single" else "multi",
|
||||
));
|
||||
these_tests.setFilter(test_filter);
|
||||
these_tests.setBuildMode(mode);
|
||||
if (!is_native) {
|
||||
these_tests.setTarget(test_target.arch, test_target.os, test_target.environ);
|
||||
}
|
||||
if (link_libc) {
|
||||
these_tests.linkSystemLibrary("c");
|
||||
}
|
||||
step.dependOn(&these_tests.step);
|
||||
}
|
||||
const these_tests = b.addTest(root_src);
|
||||
these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", name, @tagName(test_target.os), @tagName(test_target.arch), @tagName(mode), if (link_libc) "c" else "bare"));
|
||||
these_tests.setFilter(test_filter);
|
||||
these_tests.setBuildMode(mode);
|
||||
if (!is_native) {
|
||||
these_tests.setTarget(test_target.arch, test_target.os, test_target.environ);
|
||||
}
|
||||
if (link_libc) {
|
||||
these_tests.linkSystemLibrary("c");
|
||||
}
|
||||
step.dependOn(&these_tests.step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user