authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-06-22 19:53:07+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-06-26 20:00:57+02:00
log622b7c47468bc4508f4cfe840e0f8c51b54949dd
tree987bad278d24c50313ec104e60a0fc6b00d20d50
parent834609038c83e0122b506fadfa38b9acb7ee9068
signaturelock-open Commit is signed but in an unrecognized format.

free allocated memory upon call `join`

When `join` detects a thread has completed, it will free the allocated memory of the thread. For this we must first copy the allocator. This is required as the allocated memory holds a reference to the original allocator. If we free the memory, we would end up with UB as the allocator would free itself.

1 files changed, 22 insertions(+), 20 deletions(-)

lib/std/Thread.zig+22-20
......@@ -762,13 +762,13 @@ const WasiThreadImpl = struct {
762762 /// A meta-data structure used to bootstrap a thread
763763 const Instance = struct {
764764 thread: WasiThread,
765 /// Address of this `Instance`
766 base: usize,
767 /// Contains the pointer of the new __tls_base.
768 tls_base: usize,
769 /// Contains the pointer to the stack for the newly spawned thread.
770 stack_pointer: usize,
771 /// Contains the pointer to the wrapper which holds all arguments
765 /// Contains the offset to the new __tls_base.
766 /// The offset starting from the memory's base.
767 tls_offset: usize,
768 /// Contains the offset to the stack for the newly spawned thread.
769 /// The offset is calculated starting from the memory's base.
770 stack_offset: usize,
771 /// Contains the raw pointer value to the wrapper which holds all arguments
772772 /// for the callback.
773773 raw_ptr: usize,
774774 /// Function pointer to a wrapping function which will call the user's
......@@ -790,9 +790,12 @@ const WasiThreadImpl = struct {
790790 }
791791
792792 fn join(self: Impl) void {
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);
793 defer {
794 // Create a copy of the allocator so we do not free the reference to the
795 // original allocator while freeing the memory.
796 var allocator = self.thread.allocator;
797 allocator.free(self.thread.memory);
798 }
796799
797800 var spin: u8 = 10;
798801 while (true) {
......@@ -808,11 +811,11 @@ const WasiThreadImpl = struct {
808811 }
809812
810813 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]
814 \\ local.get %[ptr]
815 \\ local.get %[expected]
816 \\ i64.const -1 # infinite
817 \\ memory.atomic.wait32 0
818 \\ local.set %[ret]
816819 : [ret] "=r" (-> u32),
817820 : [ptr] "r" (&self.thread.tid.value),
818821 [expected] "r" (tid),
......@@ -883,9 +886,8 @@ const WasiThreadImpl = struct {
883886 const instance = @ptrCast(*Instance, @alignCast(@alignOf(Instance), &allocated_memory[instance_offset]));
884887 instance.* = .{
885888 .thread = .{ .memory = allocated_memory, .allocator = config.allocator.? },
886 .base = @ptrToInt(allocated_memory.ptr),
887 .tls_base = tls_offset,
888 .stack_pointer = stack_offset,
889 .tls_offset = tls_offset,
890 .stack_offset = stack_offset,
889891 .raw_ptr = @ptrToInt(wrapper),
890892 .call_back = &Wrapper.entry,
891893 };
......@@ -903,8 +905,8 @@ const WasiThreadImpl = struct {
903905
904906 /// Bootstrap procedure, called by the HOST environment after thread creation.
905907 export fn wasi_thread_start(tid: i32, arg: *Instance) void {
906 __set_stack_pointer(arg.thread.memory.ptr + arg.stack_pointer);
907 __wasm_init_tls(arg.thread.memory.ptr + arg.tls_base);
908 __set_stack_pointer(arg.thread.memory.ptr + arg.stack_offset);
909 __wasm_init_tls(arg.thread.memory.ptr + arg.tls_offset);
908910 WasiThreadImpl.tls_thread_id = @intCast(u32, tid);
909911
910912 // Finished bootstrapping, call user's procedure.