converted to a multi-threaded program
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 18 Oct 2021 14:06:18 +0000 (09:06 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 18 Oct 2021 14:06:18 +0000 (09:06 -0500)
build.bat
build.sh
server.py [new file with mode: 0755]
site/index.html
site/js/decompiler.js
site/js/js_events.js
site/js/onyx-loader.js
site/js/onyx-thread.js [new file with mode: 0644]
site/js/webgl2.js
src/app/app.onyx

index 400374ee83c72add34bd890a328308b9cbef968c..ec49a10b24feb6a797b9b5e62a73a9cab7139b63 100644 (file)
--- a/build.bat
+++ b/build.bat
@@ -4,7 +4,8 @@ set TARGET=site\analyzer.wasm
 set ONYX_MODULE_DIRECTORY=\dev\onyx
 
 copy "%ONYX_MODULE_DIRECTORY%\bin\onyx-loader.js" .\site\js\onyx-loader.js >NUL
+copy "%ONYX_MODULE_DIRECTORY%\bin\onyx-thread.js" .\site\js\onyx-thread.js >NUL
 copy "%ONYX_MODULE_DIRECTORY%\modules\webgl2\webgl2.js" .\site\js\webgl2.js >NUL
 copy "%ONYX_MODULE_DIRECTORY%\modules\js_events\js_events.js" .\site\js\js_events.js >NUL
 
-\dev\onyx\onyx --no-colors -r js -V --use-post-mvp-features -I %ONYX_MODULE_DIRECTORY% --doc doc\source_reference -o %TARGET% src\build.onyx
\ No newline at end of file
+\dev\onyx\onyx --no-colors -r js -V --use-post-mvp-features --use-multi-threading -I %ONYX_MODULE_DIRECTORY% --doc doc\source_reference -o %TARGET% src\build.onyx
\ No newline at end of file
index dd1f3646d4e09cf1284eaa41bfb3f54db0e48c04..6048a03f1bfe865c52baa9cba41f219d1410d10b 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -5,6 +5,7 @@ ONYX_MODULE_DIRECTORY=/home/brendan/dev/c/onyx
 
 # Copy relative javascript modules from Onyx source directory
 cp "$ONYX_MODULE_DIRECTORY/bin/onyx-loader.js" ./site/js/onyx-loader.js
+cp "$ONYX_MODULE_DIRECTORY/bin/onyx-thread.js" ./site/js/onyx-thread.js
 cp "$ONYX_MODULE_DIRECTORY/modules/webgl2/webgl2.js" ./site/js/webgl2.js
 cp "$ONYX_MODULE_DIRECTORY/modules/js_events/js_events.js" ./site/js/js_events.js
 
@@ -16,6 +17,7 @@ RUNTIME=js
 
 onyx --no-colors $FLAGS -r $RUNTIME -V \
     --use-post-mvp-features \
+    --use-multi-threading \
     -I $ONYX_MODULE_DIRECTORY \
     --doc doc/source_reference \
     -o $TARGET \
diff --git a/server.py b/server.py
new file mode 100755 (executable)
index 0000000..67cdccb
--- /dev/null
+++ b/server.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+
+from http import server
+
+class COOPServer(server.SimpleHTTPRequestHandler):
+    def end_headers(self):
+        self.send_header("Cross-Origin-Embedder-Policy", "require-corp")
+        self.send_header("Cross-Origin-Opener-Policy", "same-origin")
+
+        server.SimpleHTTPRequestHandler.end_headers(self)
+
+if __name__ == '__main__':
+    httpd = server.HTTPServer(('', 8000), COOPServer)
+    httpd.serve_forever()
index dfefdadeeadbb246adb205e48960b9fc76e8c905..b68943b7c7193bbba44ddbfc1f472006557bfa65 100644 (file)
         }
         </style>
 
-        <script type="application/onyx" src="analyzer.wasm"></script>
+        <script type="application/onyx" multi-threaded="true" data="analyzer.wasm.data" src="analyzer.wasm"></script>
         <script type="text/javascript" src="js/webgl2.js"></script>
         <script type="text/javascript" src="js/js_events.js"></script>
         <script type="text/javascript" src="js/decompiler.js"></script>
         <script type="text/javascript" src="js/onyx-loader.js"></script>
+
+        <script>window.ONYX_THREAD_SCRIPT = "js/onyx-thread.js";</script>
     </head>
 
     <body>
index bc5169798d475a47b797eaa78f7dacc21d497f47..56024b4f1528c00b5dba4aadbb84341e875c932c 100644 (file)
@@ -30,39 +30,31 @@ window.ONYX_MODULES.push({
     },
 
     set_cursor: function(curptr, curlen) {
-        const decoder = new TextDecoder();
-        const data = new Uint8Array(window.ONYX_MEMORY.buffer, curptr, curlen);
-        const str = decoder.decode(data);
+        const str = onyx_decode_text(curptr, curlen);
 
         document.getElementById("main_canvas").style.cursor = str;
     },
 
     local_storage_store: function(keyptr, keylen, valueptr, valuelen) {
-        const decoder = new TextDecoder();
-
-        const key = decoder.decode(new Uint8Array(window.ONYX_MEMORY.buffer, keyptr, keylen));
-        const value = decoder.decode(new Uint8Array(window.ONYX_MEMORY.buffer, valueptr, valuelen));
+        const key   = onyx_decode_text(keyptr, keylen);
+        const value = onyx_decode_text(valueptr, valuelen);
 
         localStorage[key] = value;
     },
 
     local_storage_value_length: function(keyptr, keylen) {
-        const decoder = new TextDecoder();
         const encoder = new TextEncoder();
-
-        const key = decoder.decode(new Uint8Array(window.ONYX_MEMORY.buffer, keyptr, keylen));
+        const key = onyx_decode_text(keyptr, keylen);
 
         return encoder.encode(localStorage[key]).length;
     },
 
     local_storage_load: function(keyptr, keylen, bufferptr, bufferlen) {
-        const decoder = new TextDecoder();
-        const encoder = new TextEncoder();
-
-        const key = decoder.decode(new Uint8Array(window.ONYX_MEMORY.buffer, keyptr, keylen));
+        const key = onyx_decode_text(keyptr, keylen);
 
+        const encoder = new TextEncoder();
         const value_data = encoder.encode(localStorage[key]);
-        
+
         let WASM_U8 = new Uint8Array(window.ONYX_MEMORY.buffer);
         WASM_U8.set(value_data, bufferptr);
     }
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 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/site/js/onyx-thread.js b/site/js/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 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 eb4640c92adde324967522d79fb3007d9c7be4a1..e86e6b64fb16273c83e6edc63d48a758f0f48b2c 100644 (file)
@@ -195,6 +195,28 @@ init :: () {
             }
         }
     }
+
+    use package core.intrinsics.atomics
+
+    t, t2: thread.Thread;
+    thread.spawn(^t, (_: ^i32) {
+        printf("Hello from another thread!\n");
+
+        for i: 1000 {
+            __atomic_wait(_, 0, 1000000000);
+            printf("Hello from another thread! {}\n", i);
+        }
+
+    }, null);
+
+    thread.spawn(^t2, (_: ^i32) {
+        i := 1;
+        while true {
+            i *= 2;
+            __atomic_wait(_, 0, 1000000000);
+            debug_log(.Warning, "Debug message: {}\n", i);
+        }
+    }, null);
 }
 
 handle_event :: (event: ^events.Event) {