moved onyx_runtime out of the compiler and into a module
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 10 Dec 2021 19:09:24 +0000 (13:09 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 10 Dec 2021 19:09:24 +0000 (13:09 -0600)
build.bat
build.sh
core/runtime/onyx_run.onyx
include/onyx_library.h
modules/onyx_runtime/build.bat [new file with mode: 0644]
modules/onyx_runtime/build.sh [new file with mode: 0644]
modules/onyx_runtime/onyx_runtime.c [new file with mode: 0644]
src/wasm_runtime.c

index cf2dbe6eb6820d638f156f7687ed2d6cca5293cd..143fa8ecf5d30801485b9116d8ff8d60e7e7ef60 100644 (file)
--- a/build.bat
+++ b/build.bat
@@ -32,3 +32,5 @@ del *.pdb > NUL 2> NUL
 del *.ilk > NUL 2> NUL
 del *.obj > NUL 2> NUL
 del misc\icon_resource.res
+
+call modules\onyx_runtime\build
index 022579c2333929c9e414182e2ec895d4751e37c5..73da61e1e1301793e6d0a473b6f8abc54273c672 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -78,5 +78,8 @@ compile
 echo "Installing onyxrun executable"
 sudo cp ./bin/onyxrun "$BIN_DIR/onyxrun"
 
+./modules/onyx_runtime/build.sh
+sudo mv "./onyx_runtime.so" "$CORE_DIR/lib/onyx_runtime.so"
+
 # Otherwise the prompt ends on the same line
 printf "\n"
index 166f2ffd6ad4d7f6c1ea27c55ff6b74a5ab6ac3a..d034f230ce83291b96b6495a837299fc024a6862 100644 (file)
@@ -10,8 +10,8 @@ use package wasi
 #load "core/runtime/wasi"
 
 #if Multi_Threading_Enabled {
-    __spawn_thread :: (id: i32, tls_base: rawptr, func: (data: rawptr) -> void, data: rawptr) -> bool #foreign "env" "spawn_thread" ---
-    __kill_thread  :: (id: i32) -> i32 #foreign "env" "kill_thread" ---
+    __spawn_thread :: (id: i32, tls_base: rawptr, func: (data: rawptr) -> void, data: rawptr) -> bool #foreign "onyx_runtime" "__spawn_thread" ---
+    __kill_thread  :: (id: i32) -> i32 #foreign "onyx_runtime" "__kill_thread" ---
 
     #export "_thread_start" _thread_start
     #export "_thread_exit"  _thread_exit
@@ -26,7 +26,9 @@ use package wasi
     InternalErr :: 0x03;
 }
 
-#foreign "env" {
+#library "onyx_runtime"
+
+#foreign "onyx_runtime" {
     __process_spawn   :: (path: str, args: [] str, non_blocking_io: bool) -> os.Process.Handle ---
     __process_read    :: (handle: os.Process.Handle, buffer: [] u8) -> i32 ---
     __process_write   :: (handle: os.Process.Handle, buffer: [] u8) -> i32 ---
index c5bc9c22e6a556530adf94a739b767b1ab7d139c..8c68ed7670d63c853e60ebd0b70445bda85b774f 100644 (file)
@@ -15,6 +15,8 @@ typedef struct OnyxRuntime {
     wasm_instance_t* wasm_instance;
     wasm_module_t* wasm_module;
     wasm_memory_t* wasm_memory;
+    wasm_store_t*  wasm_store;
+    wasm_extern_vec_t wasm_imports;
 
     // HACK HACK HACK
     // There should need to be this much stuff in here, but because Wasmer doesn't ship a "wasmerdll.lib"
@@ -25,6 +27,7 @@ typedef struct OnyxRuntime {
     wasm_extern_t* (*wasm_extern_lookup_by_name)(wasm_module_t* module, wasm_instance_t* instance, const char* name);
     wasm_func_t* (*wasm_extern_as_func)(wasm_extern_t* ext);
     wasm_trap_t* (*wasm_func_call)(const wasm_func_t* wasm_func, const wasm_val_vec_t* args, wasm_val_vec_t* results);
+    wasm_instance_t* (*wasm_instance_new)(wasm_store_t* store, const wasm_module_t* module, const wasm_extern_vec_t* imports, wasm_trap_t** traps);
 } OnyxRuntime;
 
 OnyxRuntime* runtime;
@@ -76,6 +79,7 @@ typedef struct WasmFuncDefinition {
     struct WasmFuncDefinition *ONYX_MODULE_NAME_GEN(ONYX_LIBRARY_NAME)[] =
 
 // Shorter names
+#ifndef ONYX_NO_SHORT_NAMES
 #undef  BOOL
 #undef  INT
 #undef  LONG
@@ -87,5 +91,6 @@ typedef struct WasmFuncDefinition {
 #define FLOAT WASM_F32
 #define DOUBLE WASM_F64
 #define PTR WASM_I32
+#endif
 
 #define ONYX_PTR(p) (p != 0 ? (runtime->wasm_memory_data(runtime->wasm_memory) + p) : NULL)
\ No newline at end of file
diff --git a/modules/onyx_runtime/build.bat b/modules/onyx_runtime/build.bat
new file mode 100644 (file)
index 0000000..e9e919d
--- /dev/null
@@ -0,0 +1,9 @@
+@echo off
+
+call "tools\dev.bat"
+
+cl /MT /std:c17 /TC /I include /I lib/common/include /D_USRDLL /D_WINDLL modules\onyx_runtime\onyx_runtime.c /link /DLL /OUT:onyx_runtime.dll
+
+del onyx_runtime.obj
+del onyx_runtime.lib
+del onyx_runtime.exp
\ No newline at end of file
diff --git a/modules/onyx_runtime/build.sh b/modules/onyx_runtime/build.sh
new file mode 100644 (file)
index 0000000..f800c6f
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+gcc -shared -fpic -I include -I lib/common/include modules/onyx_runtime/onyx_runtime.c -o onyx_runtime.so -lpthread
\ No newline at end of file
diff --git a/modules/onyx_runtime/onyx_runtime.c b/modules/onyx_runtime/onyx_runtime.c
new file mode 100644 (file)
index 0000000..7128cef
--- /dev/null
@@ -0,0 +1,472 @@
+
+#define BH_DEFINE
+#include "bh.h"
+
+#define ONYX_LIBRARY_NAME onyx_runtime
+#define ONYX_NO_SHORT_NAMES
+#include "onyx_library.h"
+
+#ifdef _BH_LINUX
+    #include <pthread.h>
+    #include <signal.h>
+    #include <sys/wait.h>
+    #include <dlfcn.h>
+#endif
+
+#include "types.h"  // For POINTER_SIZE
+
+typedef struct OnyxThread {
+    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
+} OnyxThread;
+
+static bh_arr(OnyxThread) threads = NULL;
+
+#ifdef _BH_LINUX
+static void *onyx_run_thread(void *data) {
+#endif
+#ifdef _BH_WINDOWS
+static i32 onyx_run_thread(void *data) {
+#endif
+    OnyxThread *thread = (OnyxThread *) data;
+
+    wasm_trap_t* traps = NULL;
+    thread->instance = runtime->wasm_instance_new(runtime->wasm_store, runtime->wasm_module, &runtime->wasm_imports, &traps);
+
+    wasm_extern_t* start_extern = runtime->wasm_extern_lookup_by_name(runtime->wasm_module, thread->instance, "_thread_start");
+    wasm_func_t*   start_func   = runtime->wasm_extern_as_func(start_extern);
+
+    wasm_extern_t* exit_extern = runtime->wasm_extern_lookup_by_name(runtime->wasm_module, thread->instance, "_thread_exit");
+    wasm_func_t*   exit_func   = runtime->wasm_extern_as_func(exit_extern);
+
+    wasm_trap_t* trap=NULL;
+
+    // NOTE: This is cached in a local variable because there is a tiny chance that if a lot of threads are created
+    // then the backing array for the thread handles will move and thread* we have well not be valid. I'm betting on this
+    // not happening before now in the function; however, it *could* happen before we call thread_exit. This would be bad
+    // because of the normal reasons why accessing memory that you don't own any more is bad.
+    i32 thread_id = thread->id;
+
+    { // 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 = runtime->wasm_func_call(start_func, &args_array, &results);
+        if (trap != NULL) {
+            /*
+            When proper linking is available for Wasmer, re-enable this code. At the moment
+            I don't see too much value in passing all of these functions on the Runtime object
+            especially since it is a hack to pass functions there anyway.
+
+            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));
+            */
+            bh_printf("ERROR: WebAssembly trap in thread: %d\n", thread_id);
+        }
+    }
+
+    { // Call the _thread_exit procedure
+        wasm_val_t args[]    = { WASM_I32_VAL(thread_id) };
+        wasm_val_vec_t results = { 0, 0 };
+        wasm_val_vec_t args_array = WASM_ARRAY_VEC(args);
+
+        trap = runtime->wasm_func_call(exit_func, &args_array, &results);
+    }
+
+    return 0;
+}
+
+ONYX_DEF(__spawn_thread, (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);
+    OnyxThread *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, onyx_run_thread, thread);
+    #endif
+
+    #ifdef _BH_WINDOWS
+        // thread->thread_handle = CreateThread(NULL, 0, onyx_run_thread, thread, 0, &thread->thread_id);
+        thread->thread_handle = (HANDLE) _beginthreadex(NULL, 0, onyx_run_thread, thread, 0, &thread->thread_id);
+    #endif
+
+    results->data[0] = WASM_I32_VAL(1);
+    return NULL;
+}
+
+ONYX_DEF(__kill_thread, (WASM_I32), (WASM_I32)) {
+    i32 thread_id = params->data[0].of.i32;
+
+    i32 i = 0;
+    bh_arr_each(OnyxThread, thread, threads) {
+        if (thread->id == thread_id) {
+            #ifdef _BH_LINUX
+            pthread_kill(thread->thread, SIGKILL);
+            #endif
+
+            #ifdef _BH_WINDOWS
+            TerminateThread(thread->thread_handle, 0);
+            CloseHandle(thread->thread_handle);
+            #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;
+}
+
+#define ONYX_PROCESS_MAGIC_NUMBER 0xdeadfadebabecafe
+typedef struct OnyxProcess {
+    u64 magic_number;
+
+#ifdef _BH_LINUX
+    // Pipes
+    i32 proc_to_host[2];
+    i32 host_to_proc[2];
+
+    pid_t pid;
+#endif
+
+#ifdef _BH_WINDOWS
+    HANDLE proc_to_host_read;
+    HANDLE proc_to_host_write;
+    HANDLE host_to_proc_read;
+    HANDLE host_to_proc_write;
+
+    PROCESS_INFORMATION proc_info;
+#endif
+} OnyxProcess;
+
+ONYX_DEF(__process_spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I64)) {
+    char* process_str = ONYX_PTR(params->data[0].of.i32);
+    i32   process_len = params->data[1].of.i32;
+    i32   args_ptr    = params->data[2].of.i32;
+    i32   args_len    = params->data[3].of.i32;
+    b32   blocking_io = !params->data[4].of.i32;
+
+    char process_path[1024];
+    process_len = bh_min(1023, process_len);
+    memcpy(process_path, process_str, process_len);
+    process_path[process_len] = '\0';
+
+    OnyxProcess *process = malloc(sizeof(OnyxProcess));
+    memset(process, 0, sizeof(*process));
+    process->magic_number = ONYX_PROCESS_MAGIC_NUMBER;
+
+    #ifdef _BH_LINUX
+        char **process_args = alloca(sizeof(char *) * (args_len + 2));
+        byte_t* array_loc = ONYX_PTR(args_ptr);
+        fori (i, 0, args_len) {
+            char *arg_str = ONYX_PTR(*(i32 *) (array_loc + i * 2 * POINTER_SIZE));
+            i32   arg_len = *(i32 *) (array_loc + i * 2 * POINTER_SIZE + POINTER_SIZE);
+
+            char *arg = alloca(sizeof(char) * (arg_len + 1));
+            memcpy(arg, arg_str, arg_len);
+            arg[arg_len] = '\0';
+            process_args[i + 1] = arg;
+        }
+        process_args[0] = process_path;
+        process_args[args_len + 1] = NULL;
+
+        if (pipe(process->proc_to_host) || pipe(process->host_to_proc)) {
+            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run
+            return NULL;
+        }
+
+        pid_t pid;
+        switch (pid = fork()) {
+            case -1: // Bad fork
+                wasm_val_init_ptr(&results->data[0], NULL); // Failed to run
+
+                close(process->proc_to_host[0]);
+                close(process->proc_to_host[1]);
+                close(process->host_to_proc[0]);
+                close(process->host_to_proc[1]);
+                break;
+
+            case 0: // Child process
+                close(process->proc_to_host[0]);
+                close(process->host_to_proc[1]);
+                dup2(process->host_to_proc[0], 0); // Map the output to the pipe
+                dup2(process->proc_to_host[1], 1); // Map the output to the pipe
+                dup2(process->proc_to_host[1], 2); // Stderr to stdout
+
+                if (!blocking_io) {
+                    fcntl(0, F_SETFL, O_NONBLOCK);
+                    fcntl(1, F_SETFL, O_NONBLOCK);
+                }
+
+                execv(process_path, process_args);
+                exit(-1);
+                break;
+
+            default: {
+                process->pid = pid;
+                close(process->host_to_proc[0]);
+                close(process->proc_to_host[1]);
+
+                wasm_val_init_ptr(&results->data[0], process);
+                break;
+            }
+        }
+
+    #endif
+
+    #ifdef _BH_WINDOWS
+        // CLEANUP CLEANUP CLEANUP: This is so freaking bad...
+        char cmdLine[2048];
+        memset(cmdLine, 0, 2048);
+        strncat(cmdLine, process_path, 2047);
+
+        byte_t* array_loc = ONYX_PTR(args_ptr);
+        fori (i, 0, args_len) {
+            char *arg_str = ONYX_PTR(*(i32 *) (array_loc + i * 2 * POINTER_SIZE));
+            i32   arg_len = *(i32 *) (array_loc + i * 2 * POINTER_SIZE + 4);
+
+            strncat(cmdLine, " ", 2047);
+            strncat(cmdLine, arg_str, arg_len);
+        }
+
+        STARTUPINFOA startup;
+        memset(&startup, 0, sizeof startup);
+        startup.cb = sizeof(startup);
+
+        SECURITY_ATTRIBUTES saAttr;
+        saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+        saAttr.lpSecurityDescriptor = NULL;
+        saAttr.bInheritHandle = 1;
+
+        i32 success = 1;
+        success = success && CreatePipe(&process->host_to_proc_read, &process->host_to_proc_write, &saAttr, 4096);
+        success = success && CreatePipe(&process->proc_to_host_read, &process->proc_to_host_write, &saAttr, 4096);
+        if (!success) {
+            // printf("FAILED TO CREATE PIPES: %d\n", GetLastError());
+            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
+            return NULL;
+        }
+
+        success = SetHandleInformation(process->proc_to_host_read, 1 /* HANDLE_FLAG_INHERIT */, 0);
+        success = success && SetHandleInformation(process->host_to_proc_write, 1 /* HANDLE_FLAG_INHERIT */, 0);
+        if (!success) {
+            // printf("FAILED TO CONFIGURE PIPES: %d\n", GetLastError());
+            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
+            return NULL;
+        }
+
+        startup.hStdInput  = process->host_to_proc_read;
+        startup.hStdOutput = process->proc_to_host_write;
+        startup.hStdError = process->proc_to_host_write;
+
+        startup.dwFlags |= STARTF_USESTDHANDLES;
+
+        memset(&process->proc_info, 0, sizeof process->proc_info);
+
+        success = CreateProcessA(process_path, cmdLine, &saAttr, &saAttr, 1, 0, NULL, NULL, &startup, &process->proc_info);
+        if (!success) {
+            printf("FAILED TO CREATE PROCESS: %d\n", GetLastError());
+            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
+            return NULL;
+        }
+
+        CloseHandle(process->proc_to_host_write);
+        CloseHandle(process->host_to_proc_read);
+        wasm_val_init_ptr(&results->data[0], process);
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__process_read, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
+    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+        results->data[0] = WASM_I32_VAL(0);
+        return NULL;
+    }
+
+    i32 output_ptr = params->data[1].of.i32;
+    i32 output_len = params->data[2].of.i32;
+    u8 *buffer = ONYX_PTR(output_ptr);
+
+    i32 bytes_read;
+    #ifdef _BH_LINUX
+        bytes_read = read(process->proc_to_host[0], buffer, output_len);
+        bytes_read = bh_max(bytes_read, 0);  // Silently consume errors
+    #endif
+
+    #ifdef _BH_WINDOWS
+        i32 success = ReadFile(process->proc_to_host_read, buffer, output_len, &bytes_read, NULL);
+        if (!success) bytes_read = 0;
+    #endif
+
+    results->data[0] = WASM_I32_VAL(bytes_read);
+    return NULL;
+}
+
+ONYX_DEF(__process_write, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
+    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+        results->data[0] = WASM_I32_VAL(0);
+        return NULL;
+    }
+
+    i32 input_ptr = params->data[1].of.i32;
+    i32 input_len = params->data[2].of.i32;
+    u8 *buffer = ONYX_PTR(input_ptr);
+
+    i32 bytes_written;
+    #ifdef _BH_LINUX
+        bytes_written = write(process->host_to_proc[1], buffer, input_len);
+        bytes_written = bh_max(bytes_written, 0);  // Silently consume errors
+    #endif
+
+    #ifdef _BH_WINDOWS
+        i32 success = WriteFile(process->host_to_proc_write, buffer, input_len, &bytes_written, NULL);
+        if (!success) bytes_written = 0;
+    #endif
+
+    results->data[0] = WASM_I32_VAL(bytes_written);
+    return NULL;
+}
+
+ONYX_DEF(__process_kill, (WASM_I64), (WASM_I32)) {
+    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+        results->data[0] = WASM_I32_VAL(0);
+        return NULL;
+    }
+
+    #ifdef _BH_LINUX
+        i32 failed = kill(process->pid, SIGKILL);
+        results->data[0] = WASM_I32_VAL(!failed);
+    #endif
+
+    #ifdef _BH_WINDOWS
+        i32 success = TerminateProcess(process->proc_info.hProcess, 1);
+        results->data[0] = WASM_I32_VAL(success ? 1 : 0);
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__process_wait, (WASM_I64), (WASM_I32)) {
+    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+        results->data[0] = WASM_I32_VAL(1);
+        return NULL;
+    }
+
+    #ifdef _BH_LINUX
+        i32 status;
+        waitpid(process->pid, &status, 0);
+
+        i32 exit_code = WEXITSTATUS(status);
+        results->data[0] = WASM_I32_VAL(exit_code != 0 ? 2 : 0);
+    #endif
+
+    #ifdef _BH_WINDOWS
+        DWORD exitCode;
+        while (1) {
+            if (!WaitForSingleObject(process->proc_info.hProcess, INFINITE)) {
+                // HACK HACK HACK
+                DWORD error = GetLastError();
+                if (error != 109 && error != 6) {
+                    // printf("ERROR IN WAIT FOR SINGLE: %d\n", error);
+                    results->data[0] = WASM_I32_VAL(1);
+                    return NULL;
+                }
+            }
+
+            if (!GetExitCodeProcess(process->proc_info.hProcess, &exitCode)) {
+                // HACK HACK HACK
+                // Apparently, I'm doing something wrong (maybe?) where the process handle becomes
+                // invalid and causes error 6 "invalid handle". So I think I can safely assume that
+                // if that is the case, then the process exited? probably successfuly? hopefully?
+                // Honestly I don't know and I can't find any documentation describing when a process
+                // handle goes invalid, other than after you close it explicitly. But in the run_tests
+                // script, I'm not calling either process_kill or process_destroy, which are the only
+                // other functions that close the process handle. So I'm left in the dark as to why this
+                // is happening, but oh well. This works for now.
+                //                                                           - brendanfh 2021/12/03
+                if (GetLastError() == 6) {
+                    exitCode = 0;
+                    break;
+                }
+
+                results->data[0] = WASM_I32_VAL(3);
+                return NULL;
+            }
+
+            // 259 is STILL_ACTIVE (aka STATUS_PENDING), which means that the process has not yet exited
+            if (exitCode != 259) break;
+        }
+
+        results->data[0] = WASM_I32_VAL(exitCode != 0 ? 2 : 0);
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__process_destroy, (WASM_I64), ()) {
+    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+        return NULL;
+    }
+
+    #ifdef _BH_LINUX
+        close(process->proc_to_host[0]);
+        close(process->host_to_proc[1]);
+    #endif
+
+    #ifdef _BH_WINDOWS
+        if (!CloseHandle(process->proc_info.hThread)
+         || !CloseHandle(process->proc_info.hProcess)) {
+            // printf("ERROR CLOSING HANDLES: %d\n", GetLastError());
+         }
+    #endif
+
+    free(process);
+
+    return NULL;
+}
+
+ONYX_LIBRARY {
+    ONYX_FUNC(__spawn_thread)
+    ONYX_FUNC(__kill_thread)
+
+    ONYX_FUNC(__process_spawn)
+    ONYX_FUNC(__process_read)
+    ONYX_FUNC(__process_write)
+    ONYX_FUNC(__process_kill)
+    ONYX_FUNC(__process_wait)
+    ONYX_FUNC(__process_destroy)
+
+    NULL
+};
\ No newline at end of file
index e11332c8a56511ba56ce1d878306c9855f2d4860..ed54afe4d74be92267bd704909461545fd28b27c 100644 (file)
@@ -63,444 +63,8 @@ wasm_extern_t* wasm_extern_lookup_by_name(wasm_module_t* module, wasm_instance_t
     return exports.data[idx];
 }
 
-#define WASM_INTEROP(name) static wasm_trap_t * name (const wasm_val_vec_t *params, wasm_val_vec_t *results)
 
 
-typedef struct OnyxThread {
-    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
-} OnyxThread;
-
-static bh_arr(OnyxThread) threads = NULL;
-
-#ifdef _BH_LINUX
-static void *onyx_run_thread(void *data) {
-#endif
-#ifdef _BH_WINDOWS
-static i32 onyx_run_thread(void *data) {
-#endif
-    OnyxThread *thread = (OnyxThread *) 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;
-
-    // NOTE: This is cached in a local variable because there is a tiny chance that if a lot of threads are created
-    // then the backing array for the thread handles will move and thread* we have well not be valid. I'm betting on this
-    // not happening before now in the function; however, it *could* happen before we call thread_exit. This would be bad
-    // because of the normal reasons why accessing memory that you don't own any more is bad.
-    i32 thread_id = thread->id;
-
-    { // 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;
-}
-
-WASM_INTEROP(onyx_spawn_thread_impl) {
-    if (threads == NULL) bh_arr_new(bh_heap_allocator(), threads, 128);
-    bh_arr_insert_end(threads, 1);
-    OnyxThread *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, onyx_run_thread, thread);
-    #endif
-
-    #ifdef _BH_WINDOWS
-        // thread->thread_handle = CreateThread(NULL, 0, onyx_run_thread, thread, 0, &thread->thread_id);
-        thread->thread_handle = (HANDLE) _beginthreadex(NULL, 0, onyx_run_thread, thread, 0, &thread->thread_id);
-    #endif
-
-    results->data[0] = WASM_I32_VAL(1);
-    return NULL;
-}
-
-WASM_INTEROP(onyx_kill_thread_impl) {
-    i32 thread_id = params->data[0].of.i32;
-
-    i32 i = 0;
-    bh_arr_each(OnyxThread, thread, threads) {
-        if (thread->id == thread_id) {
-            #ifdef _BH_LINUX
-            pthread_kill(thread->thread, SIGKILL);
-            #endif
-
-            #ifdef _BH_WINDOWS
-            TerminateThread(thread->thread_handle, 0);
-            CloseHandle(thread->thread_handle);
-            #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;
-}
-
-#define ONYX_PROCESS_MAGIC_NUMBER 0xdeadfadebabecafe
-typedef struct OnyxProcess {
-    u64 magic_number;
-
-#ifdef _BH_LINUX
-    // Pipes
-    i32 proc_to_host[2];
-    i32 host_to_proc[2];
-
-    pid_t pid;
-#endif
-
-#ifdef _BH_WINDOWS
-    HANDLE proc_to_host_read;
-    HANDLE proc_to_host_write;
-    HANDLE host_to_proc_read;
-    HANDLE host_to_proc_write;
-
-    PROCESS_INFORMATION proc_info;
-#endif
-} OnyxProcess;
-
-WASM_INTEROP(onyx_process_spawn_impl) {
-    char* process_str = ONYX_PTR(params->data[0].of.i32);
-    i32   process_len = params->data[1].of.i32;
-    i32   args_ptr    = params->data[2].of.i32;
-    i32   args_len    = params->data[3].of.i32;
-    b32   blocking_io = !params->data[4].of.i32;
-
-    char process_path[1024];
-    process_len = bh_min(1023, process_len);
-    memcpy(process_path, process_str, process_len);
-    process_path[process_len] = '\0';
-
-    OnyxProcess *process = malloc(sizeof(OnyxProcess));
-    memset(process, 0, sizeof(*process));
-    process->magic_number = ONYX_PROCESS_MAGIC_NUMBER;
-
-    #ifdef _BH_LINUX
-        char **process_args = alloca(sizeof(char *) * (args_len + 2));
-        byte_t* array_loc = ONYX_PTR(args_ptr);
-        fori (i, 0, args_len) {
-            char *arg_str = ONYX_PTR(*(i32 *) (array_loc + i * 2 * POINTER_SIZE));
-            i32   arg_len = *(i32 *) (array_loc + i * 2 * POINTER_SIZE + POINTER_SIZE);
-
-            char *arg = alloca(sizeof(char) * (arg_len + 1));
-            memcpy(arg, arg_str, arg_len);
-            arg[arg_len] = '\0';
-            process_args[i + 1] = arg;
-        }
-        process_args[0] = process_path;
-        process_args[args_len + 1] = NULL;
-
-        if (pipe(process->proc_to_host) || pipe(process->host_to_proc)) {
-            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run
-            return NULL;
-        }
-
-        pid_t pid;
-        switch (pid = fork()) {
-            case -1: // Bad fork
-                wasm_val_init_ptr(&results->data[0], NULL); // Failed to run
-
-                close(process->proc_to_host[0]);
-                close(process->proc_to_host[1]);
-                close(process->host_to_proc[0]);
-                close(process->host_to_proc[1]);
-                break;
-
-            case 0: // Child process
-                close(process->proc_to_host[0]);
-                close(process->host_to_proc[1]);
-                dup2(process->host_to_proc[0], 0); // Map the output to the pipe
-                dup2(process->proc_to_host[1], 1); // Map the output to the pipe
-                dup2(process->proc_to_host[1], 2); // Stderr to stdout
-
-                if (!blocking_io) {
-                    fcntl(0, F_SETFL, O_NONBLOCK);
-                    fcntl(1, F_SETFL, O_NONBLOCK);
-                }
-
-                execv(process_path, process_args);
-                exit(-1);
-                break;
-
-            default: {
-                process->pid = pid;
-                close(process->host_to_proc[0]);
-                close(process->proc_to_host[1]);
-
-                wasm_val_init_ptr(&results->data[0], process);
-                break;
-            }
-        }
-
-    #endif
-
-    #ifdef _BH_WINDOWS
-        // CLEANUP CLEANUP CLEANUP: This is so freaking bad...
-        char cmdLine[2048];
-        memset(cmdLine, 0, 2048);
-        strncat(cmdLine, process_path, 2047);
-
-        byte_t* array_loc = ONYX_PTR(args_ptr);
-        fori (i, 0, args_len) {
-            char *arg_str = ONYX_PTR(*(i32 *) (array_loc + i * 2 * POINTER_SIZE));
-            i32   arg_len = *(i32 *) (array_loc + i * 2 * POINTER_SIZE + 4);
-
-            strncat(cmdLine, " ", 2047);
-            strncat(cmdLine, arg_str, arg_len);
-        }
-
-        STARTUPINFOA startup;
-        memset(&startup, 0, sizeof startup);
-        startup.cb = sizeof(startup);
-
-        SECURITY_ATTRIBUTES saAttr;
-        saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
-        saAttr.lpSecurityDescriptor = NULL;
-        saAttr.bInheritHandle = 1;
-
-        i32 success = 1;
-        success = success && CreatePipe(&process->host_to_proc_read, &process->host_to_proc_write, &saAttr, 4096);
-        success = success && CreatePipe(&process->proc_to_host_read, &process->proc_to_host_write, &saAttr, 4096);
-        if (!success) {
-            // printf("FAILED TO CREATE PIPES: %d\n", GetLastError());
-            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
-            return NULL;
-        }
-
-        success = SetHandleInformation(process->proc_to_host_read, 1 /* HANDLE_FLAG_INHERIT */, 0);
-        success = success && SetHandleInformation(process->host_to_proc_write, 1 /* HANDLE_FLAG_INHERIT */, 0);
-        if (!success) {
-            // printf("FAILED TO CONFIGURE PIPES: %d\n", GetLastError());
-            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
-            return NULL;
-        }
-
-        startup.hStdInput  = process->host_to_proc_read;
-        startup.hStdOutput = process->proc_to_host_write;
-        startup.hStdError = process->proc_to_host_write;
-
-        startup.dwFlags |= STARTF_USESTDHANDLES;
-
-        memset(&process->proc_info, 0, sizeof process->proc_info);
-
-        success = CreateProcessA(process_path, cmdLine, &saAttr, &saAttr, 1, 0, NULL, NULL, &startup, &process->proc_info);
-        if (!success) {
-            // printf("FAILED TO CREATE PROCESS: %d\n", GetLastError());
-            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
-            return NULL;
-        }
-
-        CloseHandle(process->proc_to_host_write);
-        CloseHandle(process->host_to_proc_read);
-        wasm_val_init_ptr(&results->data[0], process);
-    #endif
-
-    return NULL;
-}
-
-WASM_INTEROP(onyx_process_read_impl) {
-    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
-    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
-        results->data[0] = WASM_I32_VAL(0);
-        return NULL;
-    }
-
-    i32 output_ptr = params->data[1].of.i32;
-    i32 output_len = params->data[2].of.i32;
-    u8 *buffer = ONYX_PTR(output_ptr);
-
-    i32 bytes_read;
-    #ifdef _BH_LINUX
-        bytes_read = read(process->proc_to_host[0], buffer, output_len);
-        bytes_read = bh_max(bytes_read, 0);  // Silently consume errors
-    #endif
-
-    #ifdef _BH_WINDOWS
-        i32 success = ReadFile(process->proc_to_host_read, buffer, output_len, &bytes_read, NULL);
-        if (!success) bytes_read = 0; 
-    #endif
-
-    results->data[0] = WASM_I32_VAL(bytes_read);
-    return NULL;
-}
-
-WASM_INTEROP(onyx_process_write_impl) {
-    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
-    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
-        results->data[0] = WASM_I32_VAL(0);
-        return NULL;
-    }
-
-    i32 input_ptr = params->data[1].of.i32;
-    i32 input_len = params->data[2].of.i32;
-    u8 *buffer = ONYX_PTR(input_ptr);
-
-    i32 bytes_written;
-    #ifdef _BH_LINUX
-        bytes_written = write(process->host_to_proc[1], buffer, input_len);
-        bytes_written = bh_max(bytes_written, 0);  // Silently consume errors
-    #endif
-
-    #ifdef _BH_WINDOWS
-        i32 success = WriteFile(process->host_to_proc_write, buffer, input_len, &bytes_written, NULL);
-        if (!success) bytes_written = 0;
-    #endif
-
-    results->data[0] = WASM_I32_VAL(bytes_written);
-    return NULL;
-}
-
-WASM_INTEROP(onyx_process_kill_impl) {
-    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
-    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
-        results->data[0] = WASM_I32_VAL(0);
-        return NULL;
-    }
-
-    #ifdef _BH_LINUX
-        i32 failed = kill(process->pid, SIGKILL);
-        results->data[0] = WASM_I32_VAL(!failed);
-    #endif
-
-    #ifdef _BH_WINDOWS
-        i32 success = TerminateProcess(process->proc_info.hProcess, 1);
-        results->data[0] = WASM_I32_VAL(success ? 1 : 0);
-    #endif
-
-    return NULL;
-}
-
-WASM_INTEROP(onyx_process_wait_impl) {
-    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
-    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
-        results->data[0] = WASM_I32_VAL(1);
-        return NULL;
-    }
-
-    #ifdef _BH_LINUX
-        i32 status;
-        waitpid(process->pid, &status, 0);
-
-        i32 exit_code = WEXITSTATUS(status);
-        results->data[0] = WASM_I32_VAL(exit_code != 0 ? 2 : 0);
-    #endif
-
-    #ifdef _BH_WINDOWS
-        DWORD exitCode;
-        while (1) {
-            if (!WaitForSingleObject(process->proc_info.hProcess, INFINITE)) {
-                // HACK HACK HACK
-                DWORD error = GetLastError();
-                if (error != 109 && error != 6) {
-                    // printf("ERROR IN WAIT FOR SINGLE: %d\n", error);
-                    results->data[0] = WASM_I32_VAL(1);
-                    return NULL; 
-                }
-            }
-
-            if (!GetExitCodeProcess(process->proc_info.hProcess, &exitCode)) {
-                // HACK HACK HACK
-                // Apparently, I'm doing something wrong (maybe?) where the process handle becomes
-                // invalid and causes error 6 "invalid handle". So I think I can safely assume that
-                // if that is the case, then the process exited? probably successfuly? hopefully?
-                // Honestly I don't know and I can't find any documentation describing when a process
-                // handle goes invalid, other than after you close it explicitly. But in the run_tests
-                // script, I'm not calling either process_kill or process_destroy, which are the only
-                // other functions that close the process handle. So I'm left in the dark as to why this
-                // is happening, but oh well. This works for now.
-                //                                                           - brendanfh 2021/12/03
-                if (GetLastError() == 6) {
-                    exitCode = 0;
-                    break;
-                }
-
-                results->data[0] = WASM_I32_VAL(3);
-                return NULL;
-            }
-
-            // 259 is STILL_ACTIVE (aka STATUS_PENDING), which means that the process has not yet exited
-            if (exitCode != 259) break;
-        }
-
-        results->data[0] = WASM_I32_VAL(exitCode != 0 ? 2 : 0);
-    #endif
-
-    return NULL;
-}
-
-WASM_INTEROP(onyx_process_destroy_impl) {
-    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
-    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
-        return NULL;
-    }
-
-    #ifdef _BH_LINUX
-        close(process->proc_to_host[0]);
-        close(process->host_to_proc[1]);
-    #endif
-
-    #ifdef _BH_WINDOWS
-        if (!CloseHandle(process->proc_info.hThread)
-         || !CloseHandle(process->proc_info.hProcess)) {
-            // printf("ERROR CLOSING HANDLES: %d\n", GetLastError());
-         }
-    #endif
-
-    free(process);
-
-    return NULL;
-}
-
 typedef void *(*LibraryLinker)(OnyxRuntime *runtime);
 static bh_arr(WasmFuncDefinition **) linkable_functions = NULL;
 static bh_arr(char *) library_paths = NULL;
@@ -690,87 +254,6 @@ b32 onyx_run_wasm(bh_buffer wasm_bytes, int argc, char *argv[]) {
             }
         }
 
-        if (wasm_name_equals_string(module_name, "env")) {
-            if (wasm_name_equals_string(import_name, "spawn_thread")) {
-                wasm_functype_t* func_type = wasm_functype_new_4_1(
-                    wasm_valtype_new_i32(), wasm_valtype_new_i32(), wasm_valtype_new_i32(), wasm_valtype_new_i32(),
-                    wasm_valtype_new_i32());
-
-                wasm_func_t* wasm_func = wasm_func_new(wasm_store, func_type, onyx_spawn_thread_impl);
-                import = wasm_func_as_extern(wasm_func);
-                goto import_found;
-            }
-
-            if (wasm_name_equals_string(import_name, "kill_thread")) {
-                wasm_functype_t* func_type = wasm_functype_new_1_1(wasm_valtype_new_i32(), wasm_valtype_new_i32());
-
-                wasm_func_t* wasm_func = wasm_func_new(wasm_store, func_type, onyx_kill_thread_impl);
-                import = wasm_func_as_extern(wasm_func);
-                goto import_found;
-            }
-
-            if (wasm_name_equals_string(import_name, "__process_spawn")) {
-                wasm_valtype_t* ps[5] = {
-                    wasm_valtype_new_i32(), wasm_valtype_new_i32(),
-                    wasm_valtype_new_i32(), wasm_valtype_new_i32(),
-                    wasm_valtype_new_i32()
-                };
-                wasm_valtype_t* rs[1] = { wasm_valtype_new_i64() };
-                wasm_valtype_vec_t params, results;
-                wasm_valtype_vec_new(&params, 5, ps);
-                wasm_valtype_vec_new(&results, 1, rs);
-                wasm_functype_t* func_type = wasm_functype_new(&params, &results);
-
-                wasm_func_t* wasm_func = wasm_func_new(wasm_store, func_type, onyx_process_spawn_impl);
-                import = wasm_func_as_extern(wasm_func);
-                goto import_found;
-            }
-
-            if (wasm_name_equals_string(import_name, "__process_read")) {
-                wasm_functype_t* func_type = wasm_functype_new_3_1(
-                    wasm_valtype_new_i64(), wasm_valtype_new_i32(), wasm_valtype_new_i32(),
-                    wasm_valtype_new_i32());
-
-                wasm_func_t* wasm_func = wasm_func_new(wasm_store, func_type, onyx_process_read_impl);
-                import = wasm_func_as_extern(wasm_func);
-                goto import_found;
-            }
-
-            if (wasm_name_equals_string(import_name, "__process_write")) {
-                wasm_functype_t* func_type = wasm_functype_new_3_1(
-                    wasm_valtype_new_i64(), wasm_valtype_new_i32(), wasm_valtype_new_i32(),
-                    wasm_valtype_new_i32());
-
-                wasm_func_t* wasm_func = wasm_func_new(wasm_store, func_type, onyx_process_write_impl);
-                import = wasm_func_as_extern(wasm_func);
-                goto import_found;
-            }
-
-            if (wasm_name_equals_string(import_name, "__process_kill")) {
-                wasm_functype_t* func_type = wasm_functype_new_1_1(wasm_valtype_new_i64(), wasm_valtype_new_i32());
-
-                wasm_func_t* wasm_func = wasm_func_new(wasm_store, func_type, onyx_process_kill_impl);
-                import = wasm_func_as_extern(wasm_func);
-                goto import_found;
-            }
-
-            if (wasm_name_equals_string(import_name, "__process_wait")) {
-                wasm_functype_t* func_type = wasm_functype_new_1_1(wasm_valtype_new_i64(), wasm_valtype_new_i32());
-
-                wasm_func_t* wasm_func = wasm_func_new(wasm_store, func_type, onyx_process_wait_impl);
-                import = wasm_func_as_extern(wasm_func);
-                goto import_found;
-            }
-
-            if (wasm_name_equals_string(import_name, "__process_destroy")) {
-                wasm_functype_t* func_type = wasm_functype_new_1_0(wasm_valtype_new_i64());
-
-                wasm_func_t* wasm_func = wasm_func_new(wasm_store, func_type, onyx_process_destroy_impl);
-                import = wasm_func_as_extern(wasm_func);
-                goto import_found;
-            }
-        }
-
         bh_arr_each(WasmFuncDefinition **, library_funcs, linkable_functions) {
             WasmFuncDefinition **pcurrent_function = *library_funcs;
             while (*pcurrent_function != NULL) {
@@ -815,12 +298,15 @@ b32 onyx_run_wasm(bh_buffer wasm_bytes, int argc, char *argv[]) {
     wasm_runtime.wasm_instance = wasm_instance;
     wasm_runtime.wasm_module = wasm_module;
     wasm_runtime.wasm_memory = wasm_memory;
+    wasm_runtime.wasm_store = wasm_store;
+    wasm_runtime.wasm_imports = wasm_imports;
     
     // See comment in onyx_library.h about us being the linker.
     wasm_runtime.wasm_memory_data = &wasm_memory_data;
     wasm_runtime.wasm_extern_lookup_by_name = &wasm_extern_lookup_by_name;
     wasm_runtime.wasm_extern_as_func = &wasm_extern_as_func;
     wasm_runtime.wasm_func_call = &wasm_func_call;
+    wasm_runtime.wasm_instance_new = &wasm_instance_new;
 
     wasm_extern_t* start_extern = wasm_extern_lookup_by_name(wasm_module, wasm_instance, "_start");
     wasm_func_t*   start_func   = wasm_extern_as_func(start_extern);