process.exit(status);
},
- spawn_thread(funcidx, dataptr) {
+ spawn_thread(id, funcidx, dataptr) {
try {
const worker = new Worker(__filename, {
workerData: {
+ thread_id: id,
memory: wasm_memory,
wasm_bytes: wasm_bytes,
funcidx: funcidx,
},
});
- /*worker.on("exit", (code) => {
- console.log("THREAD STOPPED");
- });
- */
-
return 1;
} catch (e) {
});
} else {
- let { memory, wasm_bytes, funcidx, dataptr } = workerData;
+ let { thread_id, memory, wasm_bytes, funcidx, dataptr } = workerData;
ENV.onyx.memory = memory;
wasm_memory = memory;
const lib = res.instance.exports;
lib._thread_start(funcidx, dataptr);
+ lib._thread_exit(thread_id);
});
}
#load "core/intrinsics/wasm"
+#if runtime.Multi_Threading_Enabled {
+ heap_mutex: sync.Mutex;
+}
+
init :: () {
heap_state.free_list = null;
heap_state.next_alloc = cast(rawptr) (cast(uintptr) __heap_start + 8);
use package core.alloc { heap_allocator }
heap_allocator.data = ^heap_state;
heap_allocator.func = heap_alloc_proc;
+
+ #if runtime.Multi_Threading_Enabled {
+ sync.mutex_init(^heap_mutex);
+ }
}
get_watermark :: () => cast(u32) heap_state.next_alloc;
memory_copy, memory_fill,
}
- memory :: package core.memory
- math :: package core.math
+ memory :: package core.memory
+ math :: package core.math
+ runtime :: package runtime
+ sync :: package core.sync
uintptr :: #type u32
heap_alloc :: (size_: u32, align: u32) -> rawptr {
if size_ == 0 do return null;
+ #if runtime.Multi_Threading_Enabled {
+ sync.scoped_mutex(^heap_mutex);
+ }
+
size := size_ + sizeof heap_block;
size = math.max(size, sizeof heap_freed_block);
memory.align(~~^size, ~~align);
heap_free :: (ptr: rawptr) {
#if Enable_Debug do assert(ptr != null, "Trying to free a null pointer.");
+ #if runtime.Multi_Threading_Enabled {
+ sync.scoped_mutex(^heap_mutex);
+ }
hb_ptr := cast(^heap_freed_block) (cast(uintptr) ptr - sizeof heap_allocated_block);
#if Enable_Debug do assert(hb_ptr.size & Allocated_Flag == Allocated_Flag, "Corrupted heap on free. This could be due to a double free, or using memory past were you allocated it.");
heap_resize :: (ptr: rawptr, new_size_: u32, align: u32) -> rawptr {
if ptr == null do return null;
+
+ #if runtime.Multi_Threading_Enabled {
+ sync.scoped_mutex(^heap_mutex);
+ }
new_size := new_size_ + sizeof heap_block;
new_size = math.max(new_size, sizeof heap_freed_block);
context.assert_handler = __assert_handler;
__stdio_init();
+
+ #if Multi_Threading_Enabled {
+ thread.__initialize();
+ }
}
}
#if Multi_Threading_Enabled {
- __spawn_thread :: (func: (data: rawptr) -> void, data: rawptr) -> bool #foreign "host" "spawn_thread" ---
+ __spawn_thread :: (id: i32, func: (data: rawptr) -> void, data: rawptr) -> bool #foreign "host" "spawn_thread" ---
#export "_thread_start" (func: (data: rawptr) -> void, data: rawptr) {
- // Do thread initialization stuff...
- // Just has to setup a stack frame for itself
__stack_top = raw_alloc(context.allocator, 1 << 20);
+ // Need to initialize thread-local variables here
+
func(data);
}
+
+ #export "_thread_exit" (id: i32) {
+ thread.__exited(id);
+ }
}
\ No newline at end of file
#load "./stdio"
}
-#if #defined(runtime.Allow_Multi_Threading) {
+#if runtime.Multi_Threading_Enabled {
#load "./intrinsics/atomics"
+ #load "./sync/mutex"
+ #load "./threads/thread"
}
\ No newline at end of file
--- /dev/null
+package core.sync
+
+use package core.intrinsics.atomics
+
+Mutex :: struct {
+ lock : i32;
+ // owner : Thread_Id;
+}
+
+mutex_init :: (m: ^Mutex) {
+ m.lock = 0;
+}
+
+mutex_destroy :: (m: ^Mutex) {
+ m.lock = -1;
+}
+
+mutex_lock :: (m: ^Mutex) {
+ while __atomic_cmpxchg(^m.lock, 0, 1) == 1 {
+ __atomic_wait(^m.lock, 0);
+ }
+}
+
+mutex_unlock :: (m: ^Mutex) {
+ __atomic_store(^m.lock, 0);
+ __atomic_notify(^m.lock, maximum = 1);
+}
+
+scoped_mutex :: macro (m: ^Mutex) {
+ ml :: mutex_lock
+ mu :: mutex_unlock
+
+ ml(m);
+ defer mu(m);
+}
\ No newline at end of file
--- /dev/null
+package core.thread
+
+use package core
+
+#private {
+ thread_mutex : sync.Mutex;
+ next_thread_id := 1;
+ thread_map : Map(Thread_ID, ^Thread);
+}
+
+Thread_ID :: #type i32
+
+Thread :: struct {
+ id : Thread_ID;
+ alive : bool;
+}
+
+spawn :: (t: ^Thread, func: (rawptr) -> void, data: rawptr) {
+ sync.scoped_mutex(^thread_mutex);
+
+ t.id = next_thread_id;
+ t.alive = true;
+ next_thread_id += 1;
+
+ thread_map->put(t.id, t);
+
+ runtime.__spawn_thread(t.id, func, data);
+}
+
+join :: (t: ^Thread) {
+ while t.alive ---;
+}
+
+__initialize :: () {
+ thread_map->init();
+}
+
+__exited :: (id: i32) {
+ sync.scoped_mutex(^thread_mutex);
+
+ thread := thread_map->get(id);
+ if thread != null {
+ thread.alive = false;
+ thread_map->delete(id);
+ }
+}
+
+#private_file runtime :: package runtime
+