| ... | ... | @@ -790,7 +790,40 @@ const WasiThreadImpl = struct { |
| 790 | 790 | } |
| 791 | 791 | |
| 792 | 792 | fn join(self: Impl) void { |
| 793 | | _ = self; |
| 793 | // TODO cleanup memory |
| 794 | // The memory also contains the thread's stack, which is problematic while freeing the memory |
| 795 | // defer self.thread.allocator.free(self.thread.memory); |
| 796 | |
| 797 | var spin: u8 = 10; |
| 798 | while (true) { |
| 799 | const tid = self.thread.tid.load(.SeqCst); |
| 800 | if (tid == 0) { |
| 801 | break; |
| 802 | } |
| 803 | |
| 804 | if (spin > 0) { |
| 805 | spin -= 1; |
| 806 | std.atomic.spinLoopHint(); |
| 807 | continue; |
| 808 | } |
| 809 | |
| 810 | const result = asm ( |
| 811 | \\local.get %[ptr] |
| 812 | \\local.get %[expected] |
| 813 | \\i64.const -1 # infinite |
| 814 | \\memory.atomic.wait32 0 |
| 815 | \\local.set %[ret] |
| 816 | : [ret] "=r" (-> u32), |
| 817 | : [ptr] "r" (&self.thread.tid.value), |
| 818 | [expected] "r" (tid), |
| 819 | ); |
| 820 | switch (result) { |
| 821 | 0 => continue, // ok |
| 822 | 1 => continue, // expected =! loaded |
| 823 | 2 => unreachable, // timeout (infinite) |
| 824 | else => unreachable, |
| 825 | } |
| 826 | } |
| 794 | 827 | } |
| 795 | 828 | |
| 796 | 829 | fn spawn(config: std.Thread.SpawnConfig, comptime f: anytype, args: anytype) !WasiThreadImpl { |
| ... | ... | @@ -868,25 +901,43 @@ const WasiThreadImpl = struct { |
| 868 | 901 | return .{ .thread = &instance.thread }; |
| 869 | 902 | } |
| 870 | 903 | |
| 871 | | export fn wasi_thread_start(tid: i32, arg: *const Instance) void { |
| 904 | /// Bootstrap procedure, called by the HOST environment after thread creation. |
| 905 | export fn wasi_thread_start(tid: i32, arg: *Instance) void { |
| 872 | 906 | __set_stack_pointer(arg.thread.memory.ptr + arg.stack_pointer); |
| 873 | 907 | __wasm_init_tls(arg.thread.memory.ptr + arg.tls_base); |
| 874 | 908 | WasiThreadImpl.tls_thread_id = @intCast(u32, tid); |
| 875 | 909 | |
| 876 | | // finished bootstrapping, call user's procedure. |
| 910 | // Finished bootstrapping, call user's procedure. |
| 877 | 911 | arg.call_back(arg.raw_ptr); |
| 912 | |
| 913 | // Thread finished. Reset Thread ID and wake up the main thread if needed. |
| 914 | // We use inline assembly here as we must ensure not to use the stack. |
| 915 | asm volatile ( |
| 916 | \\ local.get %[ptr] |
| 917 | \\ i32.const 0 |
| 918 | \\ i32.atomic.store 0 |
| 919 | : |
| 920 | : [ptr] "r" (&arg.thread.tid.value), |
| 921 | ); |
| 922 | asm volatile ( |
| 923 | \\ local.get %[ptr] |
| 924 | \\ i32.const 1 # waiters |
| 925 | \\ memory.atomic.notify 0 |
| 926 | \\ drop # no need to know the waiters |
| 927 | : |
| 928 | : [ptr] "r" (&arg.thread.tid.value), |
| 929 | ); |
| 878 | 930 | } |
| 879 | 931 | |
| 880 | | // Asks the host to create a new thread for us. |
| 881 | | // Newly created thread wil lcall `wasi_tread_start` with the thread ID as well |
| 882 | | // as the input `arg` that was provided to `spawnWasiThread` |
| 932 | /// Asks the host to create a new thread for us. |
| 933 | /// Newly created thread will call `wasi_tread_start` with the thread ID as well |
| 934 | /// as the input `arg` that was provided to `spawnWasiThread` |
| 883 | 935 | const spawnWasiThread = @"thread-spawn"; |
| 884 | | extern "wasi" fn @"thread-spawn"(arg: *const Instance) i32; |
| 936 | extern "wasi" fn @"thread-spawn"(arg: *Instance) i32; |
| 885 | 937 | |
| 886 | 938 | /// Initializes the TLS data segment starting at `memory`. |
| 887 | 939 | /// This is a synthetic function, generated by the linker. |
| 888 | 940 | extern fn __wasm_init_tls(memory: [*]u8) void; |
| 889 | | extern fn __set_stack_pointer(ptr: [*]u8) void; |
| 890 | 941 | |
| 891 | 942 | /// Returns a pointer to the base of the TLS data segment for the current thread |
| 892 | 943 | inline fn __tls_base() [*]u8 { |