store allocator & remove global assembly

We now store the original allocator that was used to allocate the
memory required for the thread. This allocator can then be used
in any cleanup functionality to ensure the memory is freed correctly.

Secondly, we now use a function to set the stack pointer instead of
generating a function using global assembly. This is a lot cleaner
and more readable.
This commit is contained in:
Luuk de Gram 2023-06-21 21:43:11 +02:00
parent a97dbdfa0b
commit 10bf58b2db
No known key found for this signature in database
GPG Key ID: A8CFE58E4DC7D664

View File

@ -739,32 +739,24 @@ const PosixThreadImpl = struct {
};
const WasiThreadImpl = struct {
comptime {
// Sets the stack pointer, which is needed after creating a new thread
// to ensure the stack of the main thread isn't being poluted.
asm (
\\ .text
\\ .export_name __set_stack_pointer, __set_stack_pointer
\\ .globaltype __stack_pointer, i32
\\ .hidden wasi_thread_start
\\ .globl wasi_thread_start
\\ .type __set_stack_pointer, @function
\\
\\ __set_stack_pointer:
\\ .functype __set_stack_pointer (i32) -> ()
\\ local.get 0 # The raw pointer which replaces the stack pointer
\\ global.set __stack_pointer
\\ end_function
);
}
thread: *WasiThread,
pub const ThreadHandle = i32;
threadlocal var tls_thread_id: Id = 0;
const WasiThread = struct {
/// Thread ID
tid: Atomic(i32) = Atomic(i32).init(0),
/// Contains all memory which was allocated to bootstrap this thread, including:
/// - Guard page
/// - Stack
/// - TLS segment
/// - `Instance`
/// All memory is freed upon call to `join`
memory: []u8,
/// The allocator used to allocate the thread's memory,
/// which is also used during `join` to ensure clean-up.
allocator: std.mem.Allocator,
};
/// A meta-data structure used to bootstrap a thread
@ -790,7 +782,7 @@ const WasiThreadImpl = struct {
}
fn getHandle(self: Impl) ThreadHandle {
return self.thread.tid;
return self.thread.tid.load(.SeqCst);
}
fn detach(self: Impl) void {
@ -813,7 +805,6 @@ const WasiThreadImpl = struct {
}
};
var guard_offset: usize = undefined;
var stack_offset: usize = undefined;
var tls_offset: usize = undefined;
var wrapper_offset: usize = undefined;
@ -824,8 +815,11 @@ const WasiThreadImpl = struct {
// - The TLS segment
// - `Instance` - containing information about how to call the user's function.
const map_bytes = blk: {
// start with atleast a single page, which is used as a guard to prevent
// other threads clobbering our new thread.
// Unfortunately, WebAssembly has no notion of read-only segments, so this
// is only a temporary measure until the entire page is "run over".
var bytes: usize = std.wasm.page_size;
guard_offset = bytes;
bytes = std.mem.alignForward(usize, bytes, 16); // align stack to 16 bytes
stack_offset = bytes;
@ -855,7 +849,7 @@ const WasiThreadImpl = struct {
const instance = @ptrCast(*Instance, @alignCast(@alignOf(Instance), &allocated_memory[instance_offset]));
instance.* = .{
.thread = .{ .memory = allocated_memory },
.thread = .{ .memory = allocated_memory, .allocator = config.allocator.? },
.base = @ptrToInt(allocated_memory.ptr),
.tls_base = tls_offset,
.stack_pointer = stack_offset,
@ -923,6 +917,16 @@ const WasiThreadImpl = struct {
: [ret] "=r" (-> u32),
);
}
/// Allows for setting the stack pointer in the WebAssembly module.
inline fn __set_stack_pointer(addr: [*]u8) void {
asm volatile (
\\ local.get %[ptr]
\\ global.set __stack_pointer
:
: [ptr] "r" (addr),
);
}
};
const LinuxThreadImpl = struct {