diff options
| author | Luuk de Gram <luuk@degram.dev> | 2023-06-21 21:44:53 +0200 |
|---|---|---|
| committer | Luuk de Gram <luuk@degram.dev> | 2023-06-26 20:00:57 +0200 |
| commit | 834609038c83e0122b506fadfa38b9acb7ee9068 (patch) | |
| tree | b738876952e39d484ddb642a28ce4763c4771098 /lib/std/Thread.zig | |
| parent | 10bf58b2db0e340137228f706ee2e8a67b46a892 (diff) | |
| download | zig-834609038c83e0122b506fadfa38b9acb7ee9068.tar.gz zig-834609038c83e0122b506fadfa38b9acb7ee9068.zip | |
std: implement `join` for WASI-threads
We now reset the Thread ID to 0 and wake up the main thread listening
for the thread to finish. We use inline assembly as we cannot use
the stack to set the thread ID as it could possibly clobber any
of the memory.
Currently, we leak the memory that was allocated for the thread.
We need to implement a way where we can clean up the memory without
using the stack (as the stack is stored inside this same memory).
Diffstat (limited to 'lib/std/Thread.zig')
| -rw-r--r-- | lib/std/Thread.zig | 67 |
1 files changed, 59 insertions, 8 deletions
diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 04531f97bc..29ba789ffc 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -790,7 +790,40 @@ const WasiThreadImpl = struct { } fn join(self: Impl) void { - _ = self; + // TODO cleanup memory + // The memory also contains the thread's stack, which is problematic while freeing the memory + // defer self.thread.allocator.free(self.thread.memory); + + var spin: u8 = 10; + while (true) { + const tid = self.thread.tid.load(.SeqCst); + if (tid == 0) { + break; + } + + if (spin > 0) { + spin -= 1; + std.atomic.spinLoopHint(); + continue; + } + + const result = asm ( + \\local.get %[ptr] + \\local.get %[expected] + \\i64.const -1 # infinite + \\memory.atomic.wait32 0 + \\local.set %[ret] + : [ret] "=r" (-> u32), + : [ptr] "r" (&self.thread.tid.value), + [expected] "r" (tid), + ); + switch (result) { + 0 => continue, // ok + 1 => continue, // expected =! loaded + 2 => unreachable, // timeout (infinite) + else => unreachable, + } + } } fn spawn(config: std.Thread.SpawnConfig, comptime f: anytype, args: anytype) !WasiThreadImpl { @@ -868,25 +901,43 @@ const WasiThreadImpl = struct { return .{ .thread = &instance.thread }; } - export fn wasi_thread_start(tid: i32, arg: *const Instance) void { + /// Bootstrap procedure, called by the HOST environment after thread creation. + export fn wasi_thread_start(tid: i32, arg: *Instance) void { __set_stack_pointer(arg.thread.memory.ptr + arg.stack_pointer); __wasm_init_tls(arg.thread.memory.ptr + arg.tls_base); WasiThreadImpl.tls_thread_id = @intCast(u32, tid); - // finished bootstrapping, call user's procedure. + // Finished bootstrapping, call user's procedure. arg.call_back(arg.raw_ptr); + + // Thread finished. Reset Thread ID and wake up the main thread if needed. + // We use inline assembly here as we must ensure not to use the stack. + asm volatile ( + \\ local.get %[ptr] + \\ i32.const 0 + \\ i32.atomic.store 0 + : + : [ptr] "r" (&arg.thread.tid.value), + ); + asm volatile ( + \\ local.get %[ptr] + \\ i32.const 1 # waiters + \\ memory.atomic.notify 0 + \\ drop # no need to know the waiters + : + : [ptr] "r" (&arg.thread.tid.value), + ); } - // Asks the host to create a new thread for us. - // Newly created thread wil lcall `wasi_tread_start` with the thread ID as well - // as the input `arg` that was provided to `spawnWasiThread` + /// Asks the host to create a new thread for us. + /// Newly created thread will call `wasi_tread_start` with the thread ID as well + /// as the input `arg` that was provided to `spawnWasiThread` const spawnWasiThread = @"thread-spawn"; - extern "wasi" fn @"thread-spawn"(arg: *const Instance) i32; + extern "wasi" fn @"thread-spawn"(arg: *Instance) i32; /// Initializes the TLS data segment starting at `memory`. /// This is a synthetic function, generated by the linker. extern fn __wasm_init_tls(memory: [*]u8) void; - extern fn __set_stack_pointer(ptr: [*]u8) void; /// Returns a pointer to the base of the TLS data segment for the current thread inline fn __tls_base() [*]u8 { |
