| ... | @@ -790,7 +790,40 @@ const WasiThreadImpl = struct { | ... | @@ -790,7 +790,40 @@ const WasiThreadImpl = struct { |
| 790 | } | 790 | } |
| 791 | | 791 | |
| 792 | fn join(self: Impl) void { | 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 | fn spawn(config: std.Thread.SpawnConfig, comptime f: anytype, args: anytype) !WasiThreadImpl { | 829 | fn spawn(config: std.Thread.SpawnConfig, comptime f: anytype, args: anytype) !WasiThreadImpl { |
| ... | @@ -868,25 +901,43 @@ const WasiThreadImpl = struct { | ... | @@ -868,25 +901,43 @@ const WasiThreadImpl = struct { |
| 868 | return .{ .thread = &instance.thread }; | 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 | __set_stack_pointer(arg.thread.memory.ptr + arg.stack_pointer); | 906 | __set_stack_pointer(arg.thread.memory.ptr + arg.stack_pointer); |
| 873 | __wasm_init_tls(arg.thread.memory.ptr + arg.tls_base); | 907 | __wasm_init_tls(arg.thread.memory.ptr + arg.tls_base); |
| 874 | WasiThreadImpl.tls_thread_id = @intCast(u32, tid); | 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 | arg.call_back(arg.raw_ptr); | 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. | 932 | /// 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 | 933 | /// Newly created thread will call `wasi_tread_start` with the thread ID as well |
| 882 | // as the input `arg` that was provided to `spawnWasiThread` | 934 | /// as the input `arg` that was provided to `spawnWasiThread` |
| 883 | const spawnWasiThread = @"thread-spawn"; | 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 | /// Initializes the TLS data segment starting at `memory`. | 938 | /// Initializes the TLS data segment starting at `memory`. |
| 887 | /// This is a synthetic function, generated by the linker. | 939 | /// This is a synthetic function, generated by the linker. |
| 888 | extern fn __wasm_init_tls(memory: [*]u8) void; | 940 | extern fn __wasm_init_tls(memory: [*]u8) void; |
| 889 | extern fn __set_stack_pointer(ptr: [*]u8) void; | | |
| 890 | | 941 | |
| 891 | /// Returns a pointer to the base of the TLS data segment for the current thread | 942 | /// Returns a pointer to the base of the TLS data segment for the current thread |
| 892 | inline fn __tls_base() [*]u8 { | 943 | inline fn __tls_base() [*]u8 { |