added threading
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 23 Nov 2021 04:25:48 +0000 (22:25 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 23 Nov 2021 04:25:48 +0000 (22:25 -0600)
Makefile
include/heartbreak.h
misc/onyx/heartbreak.onyx
misc/onyx/heartbreak_thread.onyx [new file with mode: 0644]
misc/onyx/heartbreak_thread_runtime.onyx [new file with mode: 0644]
src/heartbreak.c
src/heartbreak_thread.c [new file with mode: 0644]
tests/simp.onyx

index 4208375ce85c0501233ef79c6ca398ba7ab9849d..710797847a4cceb66126e68a13e322aa4f31e31f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,16 +3,16 @@ WARNINGS=-Wimplicit -Wmisleading-indentation -Wparentheses -Wsequence-point -Wre
 FLAGS=$(WARNINGS) -g3 -O2
 
 INCLUDES=-I./include -I./lib/linux_x86_64/include -I./lib/common/include
-LIBS=-L./lib/linux_x86_64/lib -lwasmer -Wl,-rpath=./lib/linux_x86_64/lib -lglfw -lGL -lm
+LIBS=-L./lib/linux_x86_64/lib -lwasmer -Wl,-rpath=./lib/linux_x86_64/lib -lglfw -lGL -lm -lpthread
 TARGET=./bin/heartbreak
 
-OBJECT_FILES=./build/heartbreak.o ./build/utils.o ./build/gfx.o ./build/heartbreak_system.o ./build/heartbreak_graphics.o ./build/heartbreak_input.o ./build/heartbreak_window.o
+OBJECT_FILES=./build/heartbreak.o ./build/utils.o ./build/gfx.o ./build/heartbreak_system.o ./build/heartbreak_graphics.o ./build/heartbreak_input.o ./build/heartbreak_window.o ./build/heartbreak_thread.o
 
 build/%.o: src/%.c
        $(CC) $(FLAGS) $(INCLUDES) -o $@ $(LIBS) -c $<
 
 tests/%.wasm: tests/%.onyx misc/onyx/*
-       onyx compile -V $< -o $@
+       onyx compile -V $< -o $@ -r wasi --multi-threaded
 
 clean:
        rm -f $(OBJECT_FILES) 2>/dev/null
@@ -20,6 +20,6 @@ clean:
 $(TARGET): $(OBJECT_FILES)
        $(CC) $(FLAGS) -o $@ $^ $(LIBS)
 
-all: $(TARGET) tests/minesweeper.wasm
+all: $(TARGET) tests/minesweeper.wasm tests/simp.wasm
 run: all
-       ./bin/heartbreak tests/minesweeper.wasm
+       ./bin/heartbreak tests/simp.wasm
index e26d93ad548187525ff7ff53f09fb07d1406b6fa..5de3d3f7d7f37322a63cea42a5fcef89a8c8c0c9 100644 (file)
 
 //
 // WebAssembly State
-extern wasm_memory_t*   wasm_memory;
-extern wasm_table_t*    wasm_func_table;
-extern wasm_module_t*   wasm_module;
-extern wasm_instance_t* wasm_instance;
+extern wasm_engine_t*     wasm_engine;
+extern wasm_store_t*      wasm_store;
+extern wasm_memory_t*     wasm_memory;
+extern wasm_table_t*      wasm_func_table;
+extern wasm_extern_vec_t  wasm_imports;
+extern wasm_module_t*     wasm_module;
+extern wasm_instance_t*   wasm_instance;
 
 //
 // Global OpenGL / GLFW things
@@ -72,6 +75,7 @@ typedef struct WasmFuncDefinition {
 extern WasmFuncDefinition* __heartbreak_module_system[];
 extern WasmFuncDefinition* __heartbreak_module_graphics[];
 extern WasmFuncDefinition* __heartbreak_module_input[];
+extern WasmFuncDefinition* __heartbreak_module_thread[];
 extern WasmFuncDefinition* __heartbreak_module_window[];
 
 #endif
index 36b70f999e20849457ff63d80389535f578c4cdd..eabaf958b998c176f827ef99861734e03249da90 100644 (file)
@@ -3,6 +3,8 @@ package heartbreak
 #load "./heartbreak_graphics"
 #load "./heartbreak_system"
 #load "./heartbreak_input"
+#load "./heartbreak_thread_runtime"
+#load "./heartbreak_thread"
 #load "./heartbreak_window"
 
 use package core {println}
diff --git a/misc/onyx/heartbreak_thread.onyx b/misc/onyx/heartbreak_thread.onyx
new file mode 100644 (file)
index 0000000..c35e42d
--- /dev/null
@@ -0,0 +1,4 @@
+package heartbreak.thread
+
+// This function exists because WASI's pole_oneoff has quirky bugs when combined with pthreads
+sleep :: (ms: i64) -> void #foreign "heartbreak" "thread_sleep" ---
diff --git a/misc/onyx/heartbreak_thread_runtime.onyx b/misc/onyx/heartbreak_thread_runtime.onyx
new file mode 100644 (file)
index 0000000..1bae83f
--- /dev/null
@@ -0,0 +1,9 @@
+package runtime
+
+#if Multi_Threading_Enabled {
+    __spawn_thread :: (id: i32, tls_base: rawptr, func: (data: rawptr) -> void, data: rawptr) -> bool #foreign "heartbreak" "thread_spawn" ---
+    __kill_thread  :: (id: i32) -> i32 #foreign "heartbreak" "thread_kill" ---
+
+    #export "_thread_start" _thread_start
+    #export "_thread_exit"  _thread_exit
+}
index 2c8cf5d0262633549693dc63d66ba15d15199a8e..fee838609e3c0728d36fdbb5405b3f9ca1021d03 100644 (file)
 
 GLFWwindow* glfw_window = NULL;
 ImmediateRenderer renderer;
-wasm_module_t*   wasm_module;
-wasm_instance_t* wasm_instance;
-wasm_memory_t*   wasm_memory;
-wasm_table_t*    wasm_func_table;
+wasm_engine_t*     wasm_engine;
+wasm_store_t*      wasm_store;
+wasm_module_t*     wasm_module;
+wasm_instance_t*   wasm_instance;
+wasm_memory_t*     wasm_memory;
+wasm_table_t*      wasm_func_table;
+wasm_extern_vec_t  wasm_imports;
 
 void build_heartbreak_imports(WasmFuncDefinition*** out_wfds, i32* out_count) {
     static WasmFuncDefinition** modules[] = {
         __heartbreak_module_system,
         __heartbreak_module_graphics,
         __heartbreak_module_input,
+        __heartbreak_module_thread,
         __heartbreak_module_window,
     };
 
@@ -75,9 +79,6 @@ void run_wasm_file(bh_buffer wasm_bytes) {
     wasi_config_t*   wasi_config = NULL;
     wasi_env_t*      wasi_env = NULL;
 
-    wasm_engine_t*   engine = NULL;
-    wasm_store_t*    store = NULL;
-
     config = wasm_config_new();
     if (!config) goto error_handling;
 
@@ -98,21 +99,21 @@ void run_wasm_file(bh_buffer wasm_bytes) {
     wasi_env  = wasi_env_new(wasi_config);
     if (!wasi_env) goto error_handling;
 
-    engine = wasm_engine_new_with_config(config);
-    if (!engine) goto error_handling;
+    wasm_engine = wasm_engine_new_with_config(config);
+    if (!wasm_engine) goto error_handling;
 
-    store  = wasm_store_new(engine);
-    if (!store) goto error_handling;
+    wasm_store  = wasm_store_new(wasm_engine);
+    if (!wasm_store) goto error_handling;
 
     wasm_byte_vec_t wasm_data;
     wasm_data.size = wasm_bytes.length;
     wasm_data.data = wasm_bytes.data;
 
-    wasm_module = wasm_module_new(store, &wasm_data);
+    wasm_module = wasm_module_new(wasm_store, &wasm_data);
     if (!wasm_module) goto error_handling;
 
     wasmer_named_extern_vec_t wasi_imports;
-    wasi_get_unordered_imports(store, wasm_module, wasi_env, &wasi_imports);
+    wasi_get_unordered_imports(wasm_store, wasm_module, wasi_env, &wasi_imports);
 
     WasmFuncDefinition** defs;
     i32 defs_count;
@@ -124,8 +125,7 @@ void run_wasm_file(bh_buffer wasm_bytes) {
     wasm_importtype_vec_t module_imports;    // @Free
     wasm_module_imports(wasm_module, &module_imports);
 
-    wasm_extern_vec_t imports;
-    wasm_extern_vec_new_uninitialized(&imports, module_imports.size); // @Free
+    wasm_extern_vec_new_uninitialized(&wasm_imports, module_imports.size); // @Free
 
     fori (i, 0, (i32) module_imports.size) {
         const wasm_name_t* module_name = wasm_importtype_module(module_imports.data[i]);
@@ -143,6 +143,18 @@ void run_wasm_file(bh_buffer wasm_bytes) {
             }
         }
 
+        // Then look for special onyx.memory
+        if (wasm_name_equals_string(module_name, "onyx") && wasm_name_equals_string(import_name, "memory")) {
+            if (wasm_memory == NULL) {
+                wasm_limits_t limits = { 1024, 65536 };
+                wasm_memorytype_t* memory_type = wasm_memorytype_new(&limits);
+                wasm_memory = wasm_memory_new(wasm_store, memory_type);
+            }
+
+            import = wasm_memory_as_extern(wasm_memory);
+            goto import_found;
+        }
+
         // Otherwise, try heartbreak
         if (!wasm_name_equals(&heartbreak_name, module_name)) goto bad_import;
 
@@ -158,7 +170,7 @@ void run_wasm_file(bh_buffer wasm_bytes) {
 
                 wasm_functype_t* wasm_functype = wasm_functype_new(&wasm_params, &wasm_results);
 
-                wasm_func_t* wasm_func = wasm_func_new(store, wasm_functype, defs[j]->func);
+                wasm_func_t* wasm_func = wasm_func_new(wasm_store, wasm_functype, defs[j]->func);
                 import = wasm_func_as_extern(wasm_func);
                 goto import_found;
             }
@@ -168,7 +180,7 @@ void run_wasm_file(bh_buffer wasm_bytes) {
 
     import_found:
         // bh_printf("Found import %b.%b.\n", module_name->data, module_name->size, import_name->data, import_name->size);
-        imports.data[i] = import;
+        wasm_imports.data[i] = import;
         continue;
 
     bad_import:
@@ -178,7 +190,7 @@ void run_wasm_file(bh_buffer wasm_bytes) {
 
     wasm_trap_t* traps = NULL;
 
-    wasm_instance = wasm_instance_new(store, wasm_module, &imports, &traps);
+    wasm_instance = wasm_instance_new(wasm_store, wasm_module, &wasm_imports, &traps);
     if (!wasm_instance) goto error_handling;
 
     wasm_extern_t* start_extern = wasm_extern_lookup_by_name(wasm_module, wasm_instance, HEARTBREAK_WASM_START_FUNCTION_NAME);
@@ -220,8 +232,8 @@ error_handling:
 cleanup:
     if (wasm_instance) wasm_instance_delete(wasm_instance);
     if (wasm_module) wasm_module_delete(wasm_module);
-    if (store)       wasm_store_delete(store);
-    if (engine)      wasm_engine_delete(engine);
+    if (wasm_store)  wasm_store_delete(wasm_store);
+    if (wasm_engine) wasm_engine_delete(wasm_engine);
     return;
 }
 
diff --git a/src/heartbreak_thread.c b/src/heartbreak_thread.c
new file mode 100644 (file)
index 0000000..59c65e9
--- /dev/null
@@ -0,0 +1,143 @@
+#include "heartbreak.h"
+
+#ifdef _BH_LINUX
+    #include <pthread.h>
+    #include <signal.h>
+    #include <unistd.h>
+#endif
+
+#define HEARTBREAK_MODULE_NAME thread
+
+typedef struct Thread {
+    i32 id;
+    i32 tls_base;
+    i32 funcidx;
+    i32 dataptr;
+    wasm_instance_t* instance;
+
+    #ifdef _BH_LINUX
+        pthread_t thread;
+    #endif
+
+    #ifdef _BH_WINDOWS
+        HANDLE thread_handle;
+        i32    thread_id;
+    #endif
+} Thread;
+
+static bh_arr(Thread) threads = NULL;
+
+#ifdef _BH_LINUX
+static void *thread_run(void *data) {
+#endif
+#ifdef _BH_WINDOWS
+static i32 thread_run(void *data) {
+#endif
+    Thread *thread = (Thread *) data;
+
+    wasm_trap_t* traps = NULL;
+    thread->instance = wasm_instance_new(wasm_store, wasm_module, &wasm_imports, &traps);
+
+    wasm_extern_t* start_extern = wasm_extern_lookup_by_name(wasm_module, thread->instance, "_thread_start");
+    wasm_func_t*   start_func   = wasm_extern_as_func(start_extern);
+
+    wasm_extern_t* exit_extern = wasm_extern_lookup_by_name(wasm_module, thread->instance, "_thread_exit");
+    wasm_func_t*   exit_func   = wasm_extern_as_func(exit_extern);
+
+    wasm_trap_t* trap=NULL;
+
+    { // Call the _thread_start procedure
+        wasm_val_t args[]    = { WASM_I32_VAL(thread->id), WASM_I32_VAL(thread->tls_base), WASM_I32_VAL (thread->funcidx), WASM_I32_VAL(thread->dataptr) };
+        wasm_val_vec_t results;
+        wasm_val_vec_t args_array = WASM_ARRAY_VEC(args);
+
+        trap = wasm_func_call(start_func, &args_array, &results);
+        if (trap != NULL) {
+            wasm_message_t msg;
+            wasm_trap_message(trap, &msg);
+            bh_printf("TRAP: %b\n", msg.data, msg.size);
+
+            wasm_frame_t *origin = wasm_trap_origin(trap);
+            bh_printf("HERE: func[%d] at %p.\n", wasm_frame_func_index(origin), wasm_frame_module_offset(origin));
+        }
+    }
+
+    { // Call the _thread_exit procedure
+        wasm_val_t args[]    = { WASM_I32_VAL(thread->id) };
+        wasm_val_vec_t results;
+        wasm_val_vec_t args_array = WASM_ARRAY_VEC(args);
+
+        trap = wasm_func_call(exit_func, &args_array, &results);
+    }
+
+    return 0;
+}
+
+HEARTBREAK_DEF(spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    if (threads == NULL) bh_arr_new(bh_heap_allocator(), threads, 128);
+    bh_arr_insert_end(threads, 1);
+    Thread *thread = &bh_arr_last(threads);
+
+    thread->id       = params->data[0].of.i32;
+    thread->tls_base = params->data[1].of.i32;
+    thread->funcidx  = params->data[2].of.i32;
+    thread->dataptr  = params->data[3].of.i32;
+
+    #ifdef _BH_LINUX
+        pthread_create(&thread->thread, NULL, thread_run, thread);
+    #endif
+
+    #ifdef _BH_WINDOWS
+        thread->thread_handle = CreateThread(NULL, 0, thread_run, thread, 0, &thread->thread_id);
+    #endif
+
+    results->data[0] = WASM_I32_VAL(1);
+    return NULL;
+}
+
+HEARTBREAK_DEF(kill, (WASM_I32), (WASM_I32)) {
+    i32 thread_id = params->data[0].of.i32;
+
+    i32 i = 0;
+    bh_arr_each(Thread, thread, threads) {
+        if (thread->id == thread_id) {
+            #ifdef _BH_LINUX
+            pthread_kill(thread->thread, SIGKILL);
+            #endif
+
+            #ifdef _BH_WINDOWS
+            TerminateThread(thread->thread_handle, 0);
+            #endif
+
+            bh_arr_deleten(threads, i, 1);
+            results->data[0] = WASM_I32_VAL(1);
+            return NULL;
+        }
+
+        i++;
+    }
+
+    results->data[0] = WASM_I32_VAL(0);
+    return NULL;
+}
+
+HEARTBREAK_DEF(sleep, (WASM_I64), ()) {
+    i32 t = params->data[0].of.i64;
+
+    #ifdef _BH_LINUX
+    usleep(t * 1000);
+    #endif
+
+    #ifdef _BH_WINDOWS
+    Sleep(t);
+    #endif
+    return NULL;
+}
+
+HEARTBREAK_MODULE {
+    HEARTBREAK_FUNC(spawn)
+    HEARTBREAK_FUNC(kill)
+    HEARTBREAK_FUNC(sleep)
+    
+    NULL
+};
index 674a246890883acb48d2cdb23ceec454b5e05c47..0462e7eff20b646448afeb0e260dec06bf9df966 100644 (file)
@@ -26,12 +26,20 @@ load :: () {
 
     printf("Width is: {}\n", hb.graphics.getTextWidth("Hello, World!"));
 
-    better_font := hb.graphics.newFont("C:\\Windows\\Fonts\\calibri.ttf", 32);
+    better_font := hb.graphics.newFont("/usr/share/fonts/TTF/InputSerif-Regular.ttf", 32);
     assert(better_font != 0, "Failed to load font!");
     hb.graphics.setFont(better_font);
     // hb.graphics.setFont();
 
     // hb.window.setDimensions(1200, 900);
+    
+    #persist dummy_thread: thread.Thread;
+    thread.spawn(^dummy_thread, null, (_) => {
+        while true {
+            println("Loop");
+            hb.thread.sleep(1000);
+        }
+    });
 }
 
 keydown :: (key: hb.input.KeyConstant, mods: i32) {