From: Brendan Hansen Date: Tue, 23 Nov 2021 04:25:48 +0000 (-0600) Subject: added threading X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=43ecd63b2cba98b906270b00dfcbb426eb3f2a36;p=heartbreak.git added threading --- diff --git a/Makefile b/Makefile index 4208375..7107978 100644 --- 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 diff --git a/include/heartbreak.h b/include/heartbreak.h index e26d93a..5de3d3f 100644 --- a/include/heartbreak.h +++ b/include/heartbreak.h @@ -15,10 +15,13 @@ // // 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 diff --git a/misc/onyx/heartbreak.onyx b/misc/onyx/heartbreak.onyx index 36b70f9..eabaf95 100644 --- a/misc/onyx/heartbreak.onyx +++ b/misc/onyx/heartbreak.onyx @@ -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 index 0000000..c35e42d --- /dev/null +++ b/misc/onyx/heartbreak_thread.onyx @@ -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 index 0000000..1bae83f --- /dev/null +++ b/misc/onyx/heartbreak_thread_runtime.onyx @@ -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 +} diff --git a/src/heartbreak.c b/src/heartbreak.c index 2c8cf5d..fee8386 100644 --- a/src/heartbreak.c +++ b/src/heartbreak.c @@ -29,16 +29,20 @@ 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 index 0000000..59c65e9 --- /dev/null +++ b/src/heartbreak_thread.c @@ -0,0 +1,143 @@ +#include "heartbreak.h" + +#ifdef _BH_LINUX + #include + #include + #include +#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 +}; diff --git a/tests/simp.onyx b/tests/simp.onyx index 674a246..0462e7e 100644 --- a/tests/simp.onyx +++ b/tests/simp.onyx @@ -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) {