diff --git a/doc/langref.html.in b/doc/langref.html.in index e49609fdbf3cb3c29715f6e2108401032b5e2c3a..a7163360153545a11f909c8393fc1d5825931abb 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -933,8 +933,8 @@ const assert = std.debug.assert; threadlocal var x: i32 = 1234; test "thread local storage" { - const thread1 = try std.Thread.spawn({}, testTls); - const thread2 = try std.Thread.spawn({}, testTls); + const thread1 = try std.Thread.spawn(testTls, {}); + const thread2 = try std.Thread.spawn(testTls, {}); testTls({}); thread1.wait(); thread2.wait(); diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 77277bd1541c72ae2333d7b21416ebe562028a49..b9a69d151fa1093a04f58ad6b3efc61921b5e374 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -165,18 +165,32 @@ pub const SpawnError = error{ Unexpected, }; -/// caller must call wait on the returned thread -/// fn startFn(@TypeOf(context)) T -/// where T is u8, noreturn, void, or !void -/// caller must call wait on the returned thread -pub fn spawn(context: anytype, comptime startFn: anytype) SpawnError!*Thread { +// Given `T`, the type of the thread startFn, extract the expected type for the +// context parameter. +fn SpawnContextType(comptime T: type) type { + const TI = @typeInfo(T); + if (TI != .Fn) + @compileError("expected function type, found " ++ @typeName(T)); + + if (TI.Fn.args.len != 1) + @compileError("expected function with single argument, found " ++ @typeName(T)); + + return TI.Fn.args[0].arg_type orelse + @compileError("cannot use a generic function as thread startFn"); +} + +/// Spawns a new thread executing startFn, returning an handle for it. +/// Caller must call wait on the returned thread. +/// The `startFn` function must take a single argument of type T and return a +/// value of type u8, noreturn, void or !void. +/// The `context` parameter is of type T and is passed to the spawned thread. +pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startFn))) SpawnError!*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 = 16 * 1024 * 1024; const Context = @TypeOf(context); - comptime assert(@typeInfo(@TypeOf(startFn)).Fn.args[0].arg_type.? == Context); if (std.Target.current.os.tag == .windows) { const WinThread = struct { diff --git a/lib/std/Thread/AutoResetEvent.zig b/lib/std/Thread/AutoResetEvent.zig index 8b8b5658bf03eb19c49820e58881502e88366971..0726dc794aeefec4be0f87dc69af524fb8126647 100644 --- a/lib/std/Thread/AutoResetEvent.zig +++ b/lib/std/Thread/AutoResetEvent.zig @@ -220,8 +220,8 @@ test "basic usage" { }; var context = Context{}; - const send_thread = try std.Thread.spawn(&context, Context.sender); - const recv_thread = try std.Thread.spawn(&context, Context.receiver); + const send_thread = try std.Thread.spawn(Context.sender, &context); + const recv_thread = try std.Thread.spawn(Context.receiver, &context); send_thread.wait(); recv_thread.wait(); diff --git a/lib/std/Thread/Mutex.zig b/lib/std/Thread/Mutex.zig index 94711bcda0d0a20820fa83917d83f0d28e44e7f3..e7d11954bf0ea01a54f207921dc8e630014a66e6 100644 --- a/lib/std/Thread/Mutex.zig +++ b/lib/std/Thread/Mutex.zig @@ -299,7 +299,7 @@ test "basic usage" { const thread_count = 10; var threads: [thread_count]*std.Thread = undefined; for (threads) |*t| { - t.* = try std.Thread.spawn(&context, worker); + t.* = try std.Thread.spawn(worker, &context); } for (threads) |t| t.wait(); diff --git a/lib/std/Thread/ResetEvent.zig b/lib/std/Thread/ResetEvent.zig index 622f9be98eacae52e3c9769342ba82b46fb241ed..439fb0db4054464d2640b53b90db6638ce45cc49 100644 --- a/lib/std/Thread/ResetEvent.zig +++ b/lib/std/Thread/ResetEvent.zig @@ -281,7 +281,7 @@ test "basic usage" { var context: Context = undefined; try context.init(); defer context.deinit(); - const receiver = try std.Thread.spawn(&context, Context.receiver); + const receiver = try std.Thread.spawn(Context.receiver, &context); defer receiver.wait(); context.sender(); @@ -290,7 +290,7 @@ test "basic usage" { // https://github.com/ziglang/zig/issues/7009 var timed = Context.init(); defer timed.deinit(); - const sleeper = try std.Thread.spawn(&timed, Context.sleeper); + const sleeper = try std.Thread.spawn(Context.sleeper, &timed); defer sleeper.wait(); try timed.timedWaiter(); } diff --git a/lib/std/Thread/StaticResetEvent.zig b/lib/std/Thread/StaticResetEvent.zig index 6d90d7cf9a193439de595f320cc38b60153256ec..07a8e50c1696918309e98fb9d5dab5b5a00644d7 100644 --- a/lib/std/Thread/StaticResetEvent.zig +++ b/lib/std/Thread/StaticResetEvent.zig @@ -379,7 +379,7 @@ test "basic usage" { }; var context = Context{}; - const receiver = try std.Thread.spawn(&context, Context.receiver); + const receiver = try std.Thread.spawn(Context.receiver, &context); defer receiver.wait(); context.sender(); @@ -388,7 +388,7 @@ test "basic usage" { // https://github.com/ziglang/zig/issues/7009 var timed = Context.init(); defer timed.deinit(); - const sleeper = try std.Thread.spawn(&timed, Context.sleeper); + const sleeper = try std.Thread.spawn(Context.sleeper, &timed); defer sleeper.wait(); try timed.timedWaiter(); } diff --git a/lib/std/atomic/queue.zig b/lib/std/atomic/queue.zig index f5f63944ab8706c57d3130483d063a619674dd04..4e427a1669815cd97630c0f5d89e7261220fb286 100644 --- a/lib/std/atomic/queue.zig +++ b/lib/std/atomic/queue.zig @@ -216,11 +216,11 @@ test "std.atomic.Queue" { var putters: [put_thread_count]*std.Thread = undefined; for (putters) |*t| { - t.* = try std.Thread.spawn(&context, startPuts); + t.* = try std.Thread.spawn(startPuts, &context); } var getters: [put_thread_count]*std.Thread = undefined; for (getters) |*t| { - t.* = try std.Thread.spawn(&context, startGets); + t.* = try std.Thread.spawn(startGets, &context); } for (putters) |t| diff --git a/lib/std/atomic/stack.zig b/lib/std/atomic/stack.zig index d55a8f81a3d6a14c70e01153b38fe8233eb7abc6..4096c27354ee9e388dbedc71a7c900438ad09c4d 100644 --- a/lib/std/atomic/stack.zig +++ b/lib/std/atomic/stack.zig @@ -123,11 +123,11 @@ test "std.atomic.stack" { } else { var putters: [put_thread_count]*std.Thread = undefined; for (putters) |*t| { - t.* = try std.Thread.spawn(&context, startPuts); + t.* = try std.Thread.spawn(startPuts, &context); } var getters: [put_thread_count]*std.Thread = undefined; for (getters) |*t| { - t.* = try std.Thread.spawn(&context, startGets); + t.* = try std.Thread.spawn(startGets, &context); } for (putters) |t| diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig index 912f99e961c1f6dfcc369525ac3767a9d74790de..878cea4aa655f22d475027568f8653644a6494c9 100644 --- a/lib/std/event/loop.zig +++ b/lib/std/event/loop.zig @@ -185,7 +185,7 @@ pub const Loop = struct { errdefer self.deinitOsData(); if (!builtin.single_threaded) { - self.fs_thread = try Thread.spawn(self, posixFsRun); + self.fs_thread = try Thread.spawn(posixFsRun, self); } errdefer if (!builtin.single_threaded) { self.posixFsRequest(&self.fs_end_request); @@ -264,7 +264,7 @@ pub const Loop = struct { } } while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { - self.extra_threads[extra_thread_index] = try Thread.spawn(self, workerRun); + self.extra_threads[extra_thread_index] = try Thread.spawn(workerRun, self); } }, .macos, .freebsd, .netbsd, .dragonfly, .openbsd => { @@ -329,7 +329,7 @@ pub const Loop = struct { } } while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { - self.extra_threads[extra_thread_index] = try Thread.spawn(self, workerRun); + self.extra_threads[extra_thread_index] = try Thread.spawn(workerRun, self); } }, .windows => { @@ -378,7 +378,7 @@ pub const Loop = struct { } } while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { - self.extra_threads[extra_thread_index] = try Thread.spawn(self, workerRun); + self.extra_threads[extra_thread_index] = try Thread.spawn(workerRun, self); } }, else => {}, @@ -798,7 +798,7 @@ pub const Loop = struct { .event = std.Thread.AutoResetEvent{}, .is_running = true, // Must be last so that it can read the other state, such as `is_running`. - .thread = try std.Thread.spawn(self, DelayQueue.run), + .thread = try std.Thread.spawn(DelayQueue.run, self), }; } diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index e9f28a0b600f8d89c77b58bc94b1f2ad28c6687b..0e2d296bb715df6ab0f9b3a5b9715c8029c473d5 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -762,7 +762,7 @@ test "open file with exclusive lock twice, make sure it waits" { try evt.init(); defer evt.deinit(); - const t = try std.Thread.spawn(S.C{ .dir = &tmp.dir, .evt = &evt }, S.checkFn); + const t = try std.Thread.spawn(S.checkFn, S.C{ .dir = &tmp.dir, .evt = &evt }); defer t.wait(); const SLEEP_TIMEOUT_NS = 10 * std.time.ns_per_ms; diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 10a9c4e18b6afe5483534b3c207c5feb1db29fbc..4470d17aeb2d5f9c3f3f1f683ba75d6e85c2b9ee 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -161,7 +161,7 @@ test "listen on a port, send bytes, receive bytes" { } }; - const t = try std.Thread.spawn(server.listen_address, S.clientFn); + const t = try std.Thread.spawn(S.clientFn, server.listen_address); defer t.wait(); var client = try server.accept(); @@ -285,7 +285,7 @@ test "listen on a unix socket, send bytes, receive bytes" { } }; - const t = try std.Thread.spawn({}, S.clientFn); + const t = try std.Thread.spawn(S.clientFn, {}); defer t.wait(); var client = try server.accept(); diff --git a/lib/std/once.zig b/lib/std/once.zig index efa99060d3a6bd3c39609158402feceb1afc7d9d..79d273b86a9f742327625413e006ca9ea59b74be 100644 --- a/lib/std/once.zig +++ b/lib/std/once.zig @@ -59,11 +59,11 @@ test "Once executes its function just once" { defer for (threads) |handle| handle.wait(); for (threads) |*handle| { - handle.* = try std.Thread.spawn(@as(u8, 0), struct { + handle.* = try std.Thread.spawn(struct { fn thread_fn(x: u8) void { global_once.call(); } - }.thread_fn); + }.thread_fn, 0); } } diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index f08d4d58fad9273ee782a7909e3a71c0a74df881..c8cef38d5d875d827a28b0aeb80fc5db8a68a696 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -317,7 +317,7 @@ test "std.Thread.getCurrentId" { if (builtin.single_threaded) return error.SkipZigTest; var thread_current_id: Thread.Id = undefined; - const thread = try Thread.spawn(&thread_current_id, testThreadIdFn); + const thread = try Thread.spawn(testThreadIdFn, &thread_current_id); const thread_id = thread.handle(); thread.wait(); if (Thread.use_pthreads) { @@ -336,10 +336,10 @@ test "spawn threads" { var shared_ctx: i32 = 1; - const thread1 = try Thread.spawn({}, start1); - const thread2 = try Thread.spawn(&shared_ctx, start2); - const thread3 = try Thread.spawn(&shared_ctx, start2); - const thread4 = try Thread.spawn(&shared_ctx, start2); + const thread1 = try Thread.spawn(start1, {}); + const thread2 = try Thread.spawn(start2, &shared_ctx); + const thread3 = try Thread.spawn(start2, &shared_ctx); + const thread4 = try Thread.spawn(start2, &shared_ctx); thread1.wait(); thread2.wait(); @@ -367,8 +367,8 @@ test "cpu count" { test "thread local storage" { if (builtin.single_threaded) return error.SkipZigTest; - const thread1 = try Thread.spawn({}, testTls); - const thread2 = try Thread.spawn({}, testTls); + const thread1 = try Thread.spawn(testTls, {}); + const thread2 = try Thread.spawn(testTls, {}); testTls({}); thread1.wait(); thread2.wait(); diff --git a/src/ThreadPool.zig b/src/ThreadPool.zig index ec580210e9a99af76a13f983db98da85e44d714b..e66742b49e03c51d54180c18e0a7aad3388432b6 100644 --- a/src/ThreadPool.zig +++ b/src/ThreadPool.zig @@ -74,7 +74,7 @@ pub fn init(self: *ThreadPool, allocator: *std.mem.Allocator) !void { try worker.idle_node.data.init(); errdefer worker.idle_node.data.deinit(); - worker.thread = try std.Thread.spawn(worker, Worker.run); + worker.thread = try std.Thread.spawn(Worker.run, worker); } }