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
$(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
//
// 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
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
#load "./heartbreak_graphics"
#load "./heartbreak_system"
#load "./heartbreak_input"
+#load "./heartbreak_thread_runtime"
+#load "./heartbreak_thread"
#load "./heartbreak_window"
use package core {println}
--- /dev/null
+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" ---
--- /dev/null
+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
+}
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,
};
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;
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;
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]);
}
}
+ // 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;
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;
}
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:
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);
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;
}
--- /dev/null
+#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
+};
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) {