updated js loader to support multiple threads
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 18 Oct 2021 14:08:09 +0000 (09:08 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 18 Oct 2021 14:08:09 +0000 (09:08 -0500)
bin/onyx
bin/onyx-js
bin/onyx-loader.js
bin/onyx-thread.js [new file with mode: 0644]
core/builtin.onyx
core/runtime/js.onyx
core/runtime/wasi.onyx
modules/js_events/js_events.js
modules/webgl2/webgl2.js
src/wasm_intrinsics.c

index 4e59708766129272c5fb780f1c061a316a4eef0e..9e067728a473052842973a1678efc863f8730d74 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index 80c843d242a6e3a96bb495b7919f0cb27f2ef639..8bf494fb20e8982295dc2e32ddba391574f5f5c1 100755 (executable)
@@ -87,7 +87,7 @@ if (isMainThread) {
             wasm_instance = res.instance;
 
             const lib = res.instance.exports;
-            lib._thread_start(funcidx, dataptr);
+            lib._thread_start(thread_id, funcidx, dataptr);
             lib._thread_exit(thread_id);
         });
 }
index a48a38acfd9880f61a852f73b5155e802aaddcf9..017dbbb168a4af49268e78c1e2133cdee44398d0 100644 (file)
@@ -2,19 +2,62 @@
 window.ONYX_MODULES  = window.ONYX_MODULES || [];
 window.ONYX_MEMORY   = null;
 window.ONYX_INSTANCE = null;
+window.ONYX_BYTES    = null;
+window.ONYX_THREAD_SCRIPT = "onyx-thread.js";
 
 window.ONYX_MODULES.push({
     module_name: "host",
 
     print_str: function(ptr, len) {
-        var buffer = new Uint8Array(ONYX_MEMORY.buffer, ptr, len);
-        var string = new TextDecoder().decode(buffer);
-        console.log(string);
+        console.log(onyx_decode_text(ptr, len));
     },
 
-    exit: function() { debugger; }
+    exit: function() { debugger; },
+
+    spawn_thread: function(id, funcidx, dataptr) {
+        try {
+            let needed_imports = {};
+
+            for (let i = 0; i < window.ONYX_MODULES.length; i++) {
+                needed_imports[window.ONYX_MODULES[i].module_name] = [];
+
+                for (let k of Object.keys(window.ONYX_MODULES[i])) {
+                    if (k == "module_name") continue;
+
+                    needed_imports[window.ONYX_MODULES[i].module_name].push(k);
+                }
+            }
+
+            const worker = new Worker(window.ONYX_THREAD_SCRIPT);
+            worker.postMessage({
+                thread_id  : id,
+                memory     : window.ONYX_MEMORY,
+                wasm_bytes : window.ONYX_BYTES,
+                funcidx    : funcidx,
+                dataptr    : dataptr,
+                imports    : needed_imports,
+            });
+
+            return 1;
+
+        } catch (e) {
+            console.error(e);
+            return 0;
+        }
+    },
 });
 
+function onyx_decode_text(ptr, len) {
+    let v = new DataView(window.ONYX_MEMORY.buffer);
+
+    let s = "";
+    for (let i = 0; i < len; i++) {
+        s += String.fromCharCode(v.getUint8(ptr + i));
+    }
+
+    return s;
+}
+
 function launch_onyx_program(script_path, call_start) {
     fetch(script_path)
     .then(function(res) { return res.arrayBuffer(); })
@@ -35,13 +78,41 @@ function launch_onyx_program(script_path, call_start) {
     });
 }
 
+function launch_multi_threaded_onyx_program(script_path, data_path, call_start) {
+    Promise.all([fetch(script_path), fetch(data_path)])
+    .then(function(xs) { return Promise.all([xs[0].arrayBuffer(), xs[1].arrayBuffer()]); })
+    .then(function(data) {
+        var import_object = {};
+
+        for (var i = 0; i < window.ONYX_MODULES.length; i++) {
+            import_object[window.ONYX_MODULES[i].module_name] = window.ONYX_MODULES[i];
+        }
+
+        import_object["onyx"] = { memory: new WebAssembly.Memory({ initial: 1024, maximum: 65536, shared: true }) };
+        window.ONYX_MEMORY = import_object["onyx"]["memory"];
+        window.ONYX_BYTES  = data[0];
+
+        WebAssembly.instantiate(data[1], import_object)
+        .then(function (data_module) {
+            WebAssembly.instantiate(data[0], import_object)
+            .then(function (code_module) {
+                window.ONYX_INSTANCE = code_module.instance;
+                code_module.instance.exports._start();
+            });
+        });
+    });
+}
+
 window.onload = function() {
     var script_tags = document.getElementsByTagName("script");
 
     for (var i = 0; i < script_tags.length; i++) {
         if (script_tags[i].getAttribute("type") == "application/onyx") {
-            // @ROBUSTNESS: It should be configurable which function is called on start up of a Onyx WASM module.
-            launch_onyx_program(script_tags[i].getAttribute("src"), true);
+            if (script_tags[i].getAttribute("multi-threaded")) {
+                launch_multi_threaded_onyx_program(script_tags[i].getAttribute("src"), script_tags[i].getAttribute("data"), true);
+            } else {
+                launch_onyx_program(script_tags[i].getAttribute("src"), true);
+            }
         }
     }
 };
diff --git a/bin/onyx-thread.js b/bin/onyx-thread.js
new file mode 100644 (file)
index 0000000..881b2ee
--- /dev/null
@@ -0,0 +1,38 @@
+
+function onyx_decode_text(ptr, len) {
+    let v = new DataView(self.ONYX_MEMORY.buffer);
+
+    let s = "";
+    for (let i = 0; i < len; i++) {
+        s += String.fromCharCode(v.getUint8(ptr + i));
+    }
+
+    return s;
+}
+
+
+self.onmessage = function (msg) {
+    const data = msg.data;
+
+    let import_object = {};
+    for (let k of Object.keys(data.imports)) {
+        import_object[k] = {};
+        for (let v of data.imports[k]) {
+            import_object[k][v] = function(a, b, c, d, e, f, g, h, i, j) {
+                console.error("ATTEMPT TO CALL MAIN THREAD FUNCTION FROM WORKER THREAD! " + v + "." + k);
+            }
+        }
+    }
+
+    import_object.host.print_str = function(ptr, len) { console.log(onyx_decode_text(ptr, len)); };
+    import_object.host.exit      = function() { debugger; };
+    import_object.onyx           = { memory: data.memory };
+
+    WebAssembly.instantiate(new Uint8Array(data.wasm_bytes), import_object)
+    .then(function(res) {
+        self.ONYX_MEMORY = data.memory;
+        
+        res.instance.exports._thread_start(data.thread_id, data.funcidx, data.dataptr);
+        res.instance.exports._thread_exit(data.thread_id);
+    });
+}
\ No newline at end of file
index f20a4805dbf834f351a279f9d46ed915605dd719..7cfa05c1499f4c92027a01b740d221f07fafc313 100644 (file)
@@ -56,6 +56,8 @@ OnyxContext :: struct {
     logger         : Logger = .{ default_logger, null };
 
     assert_handler : (msg: str, site: CallSite) -> void;
+
+    thread_id      : i32;
 }
 
 #if runtime.Runtime != runtime.Runtime_Custom {
index d3bce96d68f65ce55721b80ae924bcc9b3d21138..c70a4f85190d5adc4bafe3614ae874b5dbb34f7b 100644 (file)
@@ -4,13 +4,14 @@ package runtime
 
 use package core
 
-__output_string :: (s: str) -> u32 #foreign "host" "print_str" ---
+__output_string :: (s: str)      -> u32  #foreign "host" "print_str" ---
 __exit          :: (status: i32) -> void #foreign "host" "exit" ---
 
 // The builtin _start proc.
 // Sets up everything needed for execution.
 #export "_start" () {
     __runtime_initialize();
+    context.thread_id = 0;
 
     args: [] cstr = .{ null, 0 };
     (package main).main(args);
@@ -21,10 +22,12 @@ __exit          :: (status: i32) -> void #foreign "host" "exit" ---
 #if Multi_Threading_Enabled {
     __spawn_thread :: (id: i32, func: (data: rawptr) -> void, data: rawptr) -> bool #foreign "host" "spawn_thread" ---
 
-    #export "_thread_start" (func: (data: rawptr) -> void, data: rawptr) {
+    #export "_thread_start" (id: i32, func: (data: rawptr) -> void, data: rawptr) {
         __stack_top = raw_alloc(alloc.heap_allocator, 1 << 20);
         __thread_initialize();
 
+        context.thread_id = id;
+
         func(data);
 
         __flush_stdio();
index be8f7fad57fedd562b421a48ea3935a4a0d3f516..18fa2b5eaf5f7c20bc7b63d8ae12af9024b98bbf 100644 (file)
@@ -22,6 +22,7 @@ __exit :: (status: i32) do proc_exit(status);
 // Sets up everything needed for execution.
 #export "_start" () {
     __runtime_initialize();
+    context.thread_id = 0;
 
     args : [] cstr;
     argv_buf_size : Size;
index 2679c3e29ece6a9b0bed75f75eaff45256e07303..9a4060377128f83a39ddd8eea7113d507a9c02cf 100644 (file)
@@ -66,7 +66,7 @@ window.ONYX_MODULES.push({
             if (ev.altKey)   modifiers |= 0x02;
             if (ev.metaKey)  modifiers |= 0x04;
             if (ev.shiftKey) modifiers |= 0x08;
-            
+
             push_event_to_buffer(esp, event_size, 0x05, [ ev.keyCode, modifiers ]);
 
             var keyname = ev.code;
@@ -141,8 +141,9 @@ window.ONYX_MODULES.push({
     request_file(esp, event_size, filename_ptr, filename_len, fileid) {
         esp /= 4;
 
-        var path_memory = new Uint8Array(ONYX_MEMORY.buffer, filename_ptr, filename_len);
-        var path = new TextDecoder("utf-8").decode(path_memory);
+        var data_view   = new DataView(ONYX_MEMORY.buffer);
+        var path        = "";
+        for (var i = 0; i < filename_len; i++) path += String.fromCharCode(data_view.getUint8(filename_ptr + i));
         console.log(`Requesting file '${path}'`);
 
         fetch(path)
index df8936d2cddac644a318831807574d45c2bbe742..6964651b794447977e0903c6ac98bc01548a917f 100644 (file)
@@ -15,9 +15,7 @@ window.ONYX_MODULES.push({
     module_name: "gl",
 
     init(name, namelen) {
-        const decoder = new TextDecoder();
-        const str = new Uint8Array(window.ONYX_MEMORY.buffer, name, namelen);
-        const canvasname = decoder.decode(str);
+        const canvasname = onyx_decode_text(name, namelen);
 
         canvas = document.getElementById(canvasname);
         if (canvas == null) return 0;
@@ -207,9 +205,7 @@ window.ONYX_MODULES.push({
     },
     // getAttachedShaders() { console.log("NOT IMPLEMENTED!"); },
     getAttribLocation(program, name, namelen) {
-        const decoder = new TextDecoder();
-        const str = new Uint8Array(window.ONYX_MEMORY.buffer, name, namelen);
-        const attribname = decoder.decode(str);
+        const attribname = onyx_decode_text(name, namelen);
 
         return gl.getAttribLocation(programs[program], attribname);
     },
@@ -224,9 +220,7 @@ window.ONYX_MODULES.push({
     getShaderParameter(shader, param) { return gl.getShaderParameter(shaders[shader], param); },
     getProgramParameter(program, param) { return gl.getProgramParameter(programs[program], param); },
     getUniformLocation(program, name, namelen) {
-        const decoder = new TextDecoder();
-        const str = new Int8Array(window.ONYX_MEMORY.buffer, name, namelen);
-        const uniname = decoder.decode(str);
+        const uniname = onyx_decode_text(name, namelen);
 
         uniformlocs.push(gl.getUniformLocation(programs[program], uniname));
         return uniformlocs.length - 1;
@@ -261,9 +255,7 @@ window.ONYX_MODULES.push({
     scissor(x, y, width, height) { gl.scissor(x, y, width, height); },
     setSize(width, height) { canvas.width = width; canvas.height = height; },
     shaderSource(shader, source, sourcelen) {
-        const decoder = new TextDecoder();
-        const str = new Int8Array(window.ONYX_MEMORY.buffer, source, sourcelen);
-        const sourcedata = decoder.decode(str);
+        const sourcedata = onyx_decode_text(source, sourcelen);
 
         gl.shaderSource(shaders[shader], sourcedata);
     },
index 2c0ad87723eee0e979bb13ba8c2cfd3f85a80d96..c563d51e360e56ff4c2d7b99dd68e02c8c9d732a 100644 (file)
@@ -176,7 +176,7 @@ bad_type:
 
 EMIT_FUNC_NO_ARGS(intrinsic_atomic_notify) {
     bh_arr(WasmInstruction) code = *pcode;
-    WI(WI_ATOMIC_NOTIFY);
+    WID(WI_ATOMIC_NOTIFY, ((WasmInstructionData) { 2, 0 }));
     *pcode = code;
 }