From 4a43240de32297026b7404747839093c27d1a769 Mon Sep 17 00:00:00 2001 From: Callum Watson Date: Wed, 11 Feb 2026 13:14:34 +0000 Subject: [PATCH 01/10] Fix use of struct from WASM in JS for audio format --- src/unix/web/matoya-worker.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/unix/web/matoya-worker.js b/src/unix/web/matoya-worker.js index db1b241a..94ccc54d 100644 --- a/src/unix/web/matoya-worker.js +++ b/src/unix/web/matoya-worker.js @@ -313,7 +313,28 @@ function mty_mutex_unlock(mutex, index, notify) { } const MTY_AUDIO_API = { - MTY_AudioCreate: function (format, minBuffer, maxBuffer, deviceID, fallback) { + MTY_AudioCreate: function (formatPtr, minBuffer, maxBuffer, deviceID, fallback) { + if (formatPtr === null || formatPtr === undefined) + return 0; + + let format; + if (typeof formatPtr === 'number') { + const memoryBuffer = new DataView(MTY_MEMORY.buffer); + format = { + channels: memoryBuffer.getUint32(formatPtr, true), + sampleRate: memoryBuffer.getUint32(formatPtr + 4, true), + }; + + } else if (typeof formatPtr === 'object') { + format = { + channels: formatPtr.channels, + sampleRate: formatPtr.sampleRate, + }; + + } else { + throw new Error('Invalid formatPtr type'); + } + MTY.audio = { sampleRate: format.sampleRate, minBuffer, From c790b6a5cfe224e6a49ed830f1ce30aa72f686af Mon Sep 17 00:00:00 2001 From: Martin Trang Date: Wed, 4 Mar 2026 16:04:37 -0700 Subject: [PATCH 02/10] add new method to run loops on main thread --- src/unix/web/app.c | 6 ++++-- src/unix/web/matoya-worker.js | 11 ++++++++++- src/unix/web/web.h | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/unix/web/app.c b/src/unix/web/app.c index 2b5a9e7f..4af4a4c1 100644 --- a/src/unix/web/app.c +++ b/src/unix/web/app.c @@ -335,7 +335,8 @@ void MTY_AppDestroy(MTY_App **app) void MTY_AppRun(MTY_App *ctx) { - web_run_and_yield(ctx->app_func, ctx->opaque); + // NOTE: This function will never complete / return. + web_run_main_thread(ctx->app_func, ctx->opaque); } void MTY_AppSetTimeout(MTY_App *ctx, uint32_t timeout) @@ -707,5 +708,6 @@ void *MTY_GLGetProcAddress(const char *name) void MTY_RunAndYield(MTY_IterFunc iter, void *opaque) { - web_run_and_yield(iter, opaque); + while(iter(opaque)); + // web_run_and_yield(iter, opaque); } diff --git a/src/unix/web/matoya-worker.js b/src/unix/web/matoya-worker.js index 94ccc54d..306c2d01 100644 --- a/src/unix/web/matoya-worker.js +++ b/src/unix/web/matoya-worker.js @@ -826,6 +826,14 @@ const MTY_WEB_API = { mty_update_window(app, MTY.initWindowInfo); }, web_run_and_yield: function (iter, opaque) { + // Must run on a non-main thread since a tight loop on the main thread + // would block the event loop and prevent yielding + while (mty_cfunc(iter)(opaque)); + }, + web_run_main_thread: function (iter, opaque) { + // Cannot use web_run_and_yield on the main thread + // since the tight loop would block the event loop and prevent yielding, + // so we use setTimeout to yield after each iteration MTY.exports.mty_app_set_keys(); const step = () => { @@ -834,7 +842,8 @@ const MTY_WEB_API = { }; setTimeout(step, 0); - throw 'MTY_RunAndYield halted execution'; + // Throw exception to ensure execution does not halt when this function finishes. + throw 'run_main_thread halted execution'; }, }; diff --git a/src/unix/web/web.h b/src/unix/web/web.h index 3fa0adca..e41c8518 100644 --- a/src/unix/web/web.h +++ b/src/unix/web/web.h @@ -26,6 +26,7 @@ char *web_get_clipboard(void); void web_set_clipboard(const char *text); void web_set_title(const char *title); void web_run_and_yield(MTY_IterFunc iter, void *opaque); +void web_run_main_thread(MTY_IterFunc iter, void *opaque); void web_gl_flush(void); void web_set_gfx(void); void web_set_canvas_size(uint32_t width, uint32_t height); From ade1e6d6c0d16f9e5d55a12e2bc3627ba95166f3 Mon Sep 17 00:00:00 2001 From: Martin Trang Date: Wed, 4 Mar 2026 16:24:43 -0700 Subject: [PATCH 03/10] cleanup --- src/unix/web/app.c | 2 +- src/unix/web/matoya-worker.js | 5 ----- src/unix/web/web.h | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/unix/web/app.c b/src/unix/web/app.c index 4af4a4c1..d6cb9e0e 100644 --- a/src/unix/web/app.c +++ b/src/unix/web/app.c @@ -706,8 +706,8 @@ void *MTY_GLGetProcAddress(const char *name) return NULL; } +// Cannot be called on the main thread since the tight loop would block the event loop and prevent yielding. void MTY_RunAndYield(MTY_IterFunc iter, void *opaque) { while(iter(opaque)); - // web_run_and_yield(iter, opaque); } diff --git a/src/unix/web/matoya-worker.js b/src/unix/web/matoya-worker.js index 306c2d01..ac3808ed 100644 --- a/src/unix/web/matoya-worker.js +++ b/src/unix/web/matoya-worker.js @@ -825,11 +825,6 @@ const MTY_WEB_API = { MTY.app = app; mty_update_window(app, MTY.initWindowInfo); }, - web_run_and_yield: function (iter, opaque) { - // Must run on a non-main thread since a tight loop on the main thread - // would block the event loop and prevent yielding - while (mty_cfunc(iter)(opaque)); - }, web_run_main_thread: function (iter, opaque) { // Cannot use web_run_and_yield on the main thread // since the tight loop would block the event loop and prevent yielding, diff --git a/src/unix/web/web.h b/src/unix/web/web.h index e41c8518..3ffc16ac 100644 --- a/src/unix/web/web.h +++ b/src/unix/web/web.h @@ -25,7 +25,6 @@ char *web_get_hostname(void); char *web_get_clipboard(void); void web_set_clipboard(const char *text); void web_set_title(const char *title); -void web_run_and_yield(MTY_IterFunc iter, void *opaque); void web_run_main_thread(MTY_IterFunc iter, void *opaque); void web_gl_flush(void); void web_set_gfx(void); From d6f9430e284e4165aec56ead904d0f41dd19bdc1 Mon Sep 17 00:00:00 2001 From: Martin Trang Date: Fri, 6 Mar 2026 15:40:46 -0700 Subject: [PATCH 04/10] fix exception suppression --- src/unix/web/matoya-worker.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/unix/web/matoya-worker.js b/src/unix/web/matoya-worker.js index ac3808ed..b1d66984 100644 --- a/src/unix/web/matoya-worker.js +++ b/src/unix/web/matoya-worker.js @@ -1180,7 +1180,8 @@ onmessage = async (ev) => { close(); } catch (e) { - if (e.toString().search('MTY_RunAndYield') == -1) + // Ignore known exception that we throw to continue main thread execution + if (e.toString().search('run_main_thread halted execution') == -1) console.error(e); } break; From a8ce4d4c20ea6001ec3fcac3a6a45e7e740c722d Mon Sep 17 00:00:00 2001 From: Martin Trang Date: Thu, 12 Mar 2026 08:30:04 -0600 Subject: [PATCH 05/10] wip - willRead --- src/unix/web/matoya.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/unix/web/matoya.js b/src/unix/web/matoya.js index 90229262..2f4456bc 100644 --- a/src/unix/web/matoya.js +++ b/src/unix/web/matoya.js @@ -530,7 +530,7 @@ function mty_set_rgba_cursor(buf, width, height, hot_x, hot_y) { if (buf) { if (!MTY.ccanvas) { MTY.ccanvas = document.createElement('canvas'); - MTY.cctx = MTY.ccanvas.getContext('2d'); + MTY.cctx = MTY.ccanvas.getContext('2d', {"willReadFrequently": true}); } MTY.ccanvas.width = width; @@ -665,7 +665,7 @@ async function mty_decode_image(input) { const height = img.naturalHeight; const canvas = new OffscreenCanvas(width, height); - const ctx = canvas.getContext('2d'); + const ctx = canvas.getContext('2d', {"willReadFrequently": true}); ctx.drawImage(img, 0, 0, width, height); return ctx.getImageData(0, 0, width, height); From 8ea5f100973c61d8f7029762d5f473dd0f6330ef Mon Sep 17 00:00:00 2001 From: Martin Trang Date: Thu, 12 Mar 2026 14:58:57 -0600 Subject: [PATCH 06/10] wip - direct codex need to cleanup --- src/unix/web/app.c | 2 +- src/unix/web/matoya-worker.js | 94 ++++++++++++++++++++++++++++++++++- src/unix/web/matoya.js | 17 +++++++ src/unix/web/web.h | 1 + 4 files changed, 111 insertions(+), 3 deletions(-) diff --git a/src/unix/web/app.c b/src/unix/web/app.c index d6cb9e0e..d68378d6 100644 --- a/src/unix/web/app.c +++ b/src/unix/web/app.c @@ -709,5 +709,5 @@ void *MTY_GLGetProcAddress(const char *name) // Cannot be called on the main thread since the tight loop would block the event loop and prevent yielding. void MTY_RunAndYield(MTY_IterFunc iter, void *opaque) { - while(iter(opaque)); + web_run_and_yield(iter, opaque); } diff --git a/src/unix/web/matoya-worker.js b/src/unix/web/matoya-worker.js index b1d66984..481f4544 100644 --- a/src/unix/web/matoya-worker.js +++ b/src/unix/web/matoya-worker.js @@ -12,6 +12,8 @@ const MTY = { glObj: {}, fds: {}, fdIndex: 0, + jspi: false, + promising: {}, }; @@ -36,6 +38,64 @@ function mty_dup_c(buf) { return ptr; } +function mty_schedule(func) { + if (typeof scheduler != 'undefined' && typeof scheduler.postTask == 'function') { + scheduler.postTask(func); + + } else { + setTimeout(func, 0); + } +} + +function mty_run_until_false(cfunc, opaque) { + return new Promise(resolve => { + const step = () => { + if (cfunc(opaque)) { + mty_schedule(step); + + } else { + resolve(); + } + }; + + step(); + }); +} + +function mty_supports_jspi() { + return typeof WebAssembly.Suspending == 'function' && typeof WebAssembly.promising == 'function'; +} + +async function mty_call_export(name, ...args) { + const fn = MTY.exports[name]; + + if (!fn) + throw new Error('Missing export: ' + name); + + if (!MTY.jspi) + return fn(...args); + + if (!MTY.promising[name]) { + try { + MTY.promising[name] = WebAssembly.promising(fn); + + } catch (e) { + console.warn('JSPI export wrap failed for', name, e); + MTY.jspi = false; + return fn(...args); + } + } + + return await MTY.promising[name](...args); +} + +async function mty_web_run_and_yield_async(iter, opaque) { + MTY.exports.mty_app_set_keys(); + + const cfunc = mty_cfunc(iter); + await mty_run_until_false(cfunc, opaque); +} + // window.localStorage @@ -825,6 +885,19 @@ const MTY_WEB_API = { MTY.app = app; mty_update_window(app, MTY.initWindowInfo); }, + // web_run_and_yield: function (iter, opaque) { + // // Cannot be called on the main thread since the tight loop would block the event loop and prevent yielding. + // // MTY.exports.mty_app_set_keys(); + + // const cfunc = mty_cfunc(iter); + // let count = 0; + + // while (cfunc(opaque)) { + // // Periodically block for 1 ms to reduce allocator/GC pressure in long-running loops. + // if ((++count & 0x3FF) == 0) + // Atomics.wait(MTY.sleeper, 0, 0, 1); + // } + // }, web_run_main_thread: function (iter, opaque) { // Cannot use web_run_and_yield on the main thread // since the tight loop would block the event loop and prevent yielding, @@ -1115,6 +1188,22 @@ async function mty_instantiate_wasm(wasmBuf, userEnv) { }, } + if (MTY.jspi) { + if (mty_supports_jspi()) { + try { + imports.env.web_run_and_yield = new WebAssembly.Suspending(mty_web_run_and_yield_async); + + } catch (e) { + console.warn('JSPI import setup failed, falling back to sync imports', e); + MTY.jspi = false; + } + + } else { + console.warn('JSPI requested but not supported by this runtime, falling back to sync imports'); + MTY.jspi = false; + } + } + // Add userEnv to imports, run on the main thread for (let x = 0; x < userEnv.length; x++) { const key = userEnv[x]; @@ -1158,6 +1247,7 @@ onmessage = async (ev) => { MTY.psync = msg.psync; MTY.audioObjs = msg.audioObjs; MTY.initWindowInfo = msg.windowInfo; + MTY.jspi = msg.jspi === true; MTY.sync = new Int32Array(new SharedArrayBuffer(4)); MTY.sleeper = new Int32Array(new SharedArrayBuffer(4)); MTY.module = await mty_instantiate_wasm(msg.wasmBuf, msg.userEnv); @@ -1170,11 +1260,11 @@ onmessage = async (ev) => { try { // Additional thread if (msg.startArg) { - MTY.exports.wasi_thread_start(msg.threadId, msg.startArg); + await mty_call_export('wasi_thread_start', msg.threadId, msg.startArg); // Main thread } else { - MTY.exports._start(); + await mty_call_export('_start'); } close(); diff --git a/src/unix/web/matoya.js b/src/unix/web/matoya.js index 2f4456bc..c8b1102c 100644 --- a/src/unix/web/matoya.js +++ b/src/unix/web/matoya.js @@ -783,6 +783,21 @@ function mty_supports_web_gl() { return false; } +function mty_use_jspi() { + if (typeof WebAssembly == 'undefined') + return false; + + const params = new URLSearchParams(window.location.search); + + if (params.get('mty-jspi') == '1') + return true; + + if (params.get('mty-jspi') == '0') + return false; + + return typeof WebAssembly.Suspending == 'function' && typeof WebAssembly.promising == 'function'; +} + function mty_update_interval(thread) { // Poll gamepads if (document.hasFocus()) @@ -826,6 +841,7 @@ function mty_thread_start(threadId, bin, wasmBuf, memory, startArg, userEnv, kbM threadId: threadId, memory: memory, audioObjs, + jspi: MTY.jspi, }); return worker; @@ -837,6 +853,7 @@ async function MTY_Start(bin, container, userEnv) { MTY.bin = bin; MTY.userEnv = userEnv; + MTY.jspi = mty_use_jspi(); MTY.psync = new Int32Array(new SharedArrayBuffer(4)); MTY.audioObjs = { buf: new Int16Array(new SharedArrayBuffer(1024 * 1024)), diff --git a/src/unix/web/web.h b/src/unix/web/web.h index 3ffc16ac..e41c8518 100644 --- a/src/unix/web/web.h +++ b/src/unix/web/web.h @@ -25,6 +25,7 @@ char *web_get_hostname(void); char *web_get_clipboard(void); void web_set_clipboard(const char *text); void web_set_title(const char *title); +void web_run_and_yield(MTY_IterFunc iter, void *opaque); void web_run_main_thread(MTY_IterFunc iter, void *opaque); void web_gl_flush(void); void web_set_gfx(void); From 4322b59a69561b07c053617cc1e04c5f38422cc0 Mon Sep 17 00:00:00 2001 From: Martin Trang Date: Thu, 12 Mar 2026 15:30:35 -0600 Subject: [PATCH 07/10] Cleanup code --- src/unix/web/app.c | 3 +- src/unix/web/matoya-worker.js | 57 ++++++++--------------------------- src/unix/web/matoya.js | 22 ++++---------- src/unix/web/web.h | 1 - 4 files changed, 20 insertions(+), 63 deletions(-) diff --git a/src/unix/web/app.c b/src/unix/web/app.c index d68378d6..a0540823 100644 --- a/src/unix/web/app.c +++ b/src/unix/web/app.c @@ -335,8 +335,7 @@ void MTY_AppDestroy(MTY_App **app) void MTY_AppRun(MTY_App *ctx) { - // NOTE: This function will never complete / return. - web_run_main_thread(ctx->app_func, ctx->opaque); + web_run_and_yield(ctx->app_func, ctx->opaque); } void MTY_AppSetTimeout(MTY_App *ctx, uint32_t timeout) diff --git a/src/unix/web/matoya-worker.js b/src/unix/web/matoya-worker.js index 481f4544..1e5e6d25 100644 --- a/src/unix/web/matoya-worker.js +++ b/src/unix/web/matoya-worker.js @@ -38,20 +38,11 @@ function mty_dup_c(buf) { return ptr; } -function mty_schedule(func) { - if (typeof scheduler != 'undefined' && typeof scheduler.postTask == 'function') { - scheduler.postTask(func); - - } else { - setTimeout(func, 0); - } -} - function mty_run_until_false(cfunc, opaque) { return new Promise(resolve => { const step = () => { if (cfunc(opaque)) { - mty_schedule(step); + setTimeout(step, 0); } else { resolve(); @@ -62,10 +53,8 @@ function mty_run_until_false(cfunc, opaque) { }); } -function mty_supports_jspi() { - return typeof WebAssembly.Suspending == 'function' && typeof WebAssembly.promising == 'function'; -} - +// Wraps the calling function to allow a Promise to be returned (if needed). +// Fallback to a direct call if JSPI is not supported or the export cannot be wrapped. async function mty_call_export(name, ...args) { const fn = MTY.exports[name]; @@ -885,23 +874,9 @@ const MTY_WEB_API = { MTY.app = app; mty_update_window(app, MTY.initWindowInfo); }, - // web_run_and_yield: function (iter, opaque) { - // // Cannot be called on the main thread since the tight loop would block the event loop and prevent yielding. - // // MTY.exports.mty_app_set_keys(); - - // const cfunc = mty_cfunc(iter); - // let count = 0; - - // while (cfunc(opaque)) { - // // Periodically block for 1 ms to reduce allocator/GC pressure in long-running loops. - // if ((++count & 0x3FF) == 0) - // Atomics.wait(MTY.sleeper, 0, 0, 1); - // } - // }, - web_run_main_thread: function (iter, opaque) { - // Cannot use web_run_and_yield on the main thread - // since the tight loop would block the event loop and prevent yielding, - // so we use setTimeout to yield after each iteration + + // Fallback in case the browser does not support JSPI. + web_run_and_yield: function (iter, opaque) { MTY.exports.mty_app_set_keys(); const step = () => { @@ -911,7 +886,8 @@ const MTY_WEB_API = { setTimeout(step, 0); // Throw exception to ensure execution does not halt when this function finishes. - throw 'run_main_thread halted execution'; + // NOTE: This will end up blocking the caller indefinitely. + throw 'run_and_yield halted execution'; }, }; @@ -1189,17 +1165,10 @@ async function mty_instantiate_wasm(wasmBuf, userEnv) { } if (MTY.jspi) { - if (mty_supports_jspi()) { - try { - imports.env.web_run_and_yield = new WebAssembly.Suspending(mty_web_run_and_yield_async); - - } catch (e) { - console.warn('JSPI import setup failed, falling back to sync imports', e); - MTY.jspi = false; - } - - } else { - console.warn('JSPI requested but not supported by this runtime, falling back to sync imports'); + try { + imports.env.web_run_and_yield = new WebAssembly.Suspending(mty_web_run_and_yield_async); + } catch (e) { + console.warn('JSPI import setup failed, falling back to sync imports', e); MTY.jspi = false; } } @@ -1271,7 +1240,7 @@ onmessage = async (ev) => { } catch (e) { // Ignore known exception that we throw to continue main thread execution - if (e.toString().search('run_main_thread halted execution') == -1) + if (e.toString().search('run_and_yield halted execution') == -1) console.error(e); } break; diff --git a/src/unix/web/matoya.js b/src/unix/web/matoya.js index c8b1102c..61e5cda9 100644 --- a/src/unix/web/matoya.js +++ b/src/unix/web/matoya.js @@ -783,21 +783,6 @@ function mty_supports_web_gl() { return false; } -function mty_use_jspi() { - if (typeof WebAssembly == 'undefined') - return false; - - const params = new URLSearchParams(window.location.search); - - if (params.get('mty-jspi') == '1') - return true; - - if (params.get('mty-jspi') == '0') - return false; - - return typeof WebAssembly.Suspending == 'function' && typeof WebAssembly.promising == 'function'; -} - function mty_update_interval(thread) { // Poll gamepads if (document.hasFocus()) @@ -853,7 +838,12 @@ async function MTY_Start(bin, container, userEnv) { MTY.bin = bin; MTY.userEnv = userEnv; - MTY.jspi = mty_use_jspi(); + + // For now, we'll allow execution if the client does not support JSPI + // by falling back to non-promise based functions + MTY.jspi = typeof WebAssembly !== 'undefined' && + typeof WebAssembly.Suspending === 'function' && + typeof WebAssembly.promising === 'function'; MTY.psync = new Int32Array(new SharedArrayBuffer(4)); MTY.audioObjs = { buf: new Int16Array(new SharedArrayBuffer(1024 * 1024)), diff --git a/src/unix/web/web.h b/src/unix/web/web.h index e41c8518..3fa0adca 100644 --- a/src/unix/web/web.h +++ b/src/unix/web/web.h @@ -26,7 +26,6 @@ char *web_get_clipboard(void); void web_set_clipboard(const char *text); void web_set_title(const char *title); void web_run_and_yield(MTY_IterFunc iter, void *opaque); -void web_run_main_thread(MTY_IterFunc iter, void *opaque); void web_gl_flush(void); void web_set_gfx(void); void web_set_canvas_size(uint32_t width, uint32_t height); From 29a7c82d1bc9236eaf73ed0927fa7e24a82e5016 Mon Sep 17 00:00:00 2001 From: Martin Trang Date: Thu, 12 Mar 2026 15:32:12 -0600 Subject: [PATCH 08/10] Cleanup canvas --- src/unix/web/matoya.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unix/web/matoya.js b/src/unix/web/matoya.js index 61e5cda9..c7372ddc 100644 --- a/src/unix/web/matoya.js +++ b/src/unix/web/matoya.js @@ -665,7 +665,7 @@ async function mty_decode_image(input) { const height = img.naturalHeight; const canvas = new OffscreenCanvas(width, height); - const ctx = canvas.getContext('2d', {"willReadFrequently": true}); + const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, width, height); return ctx.getImageData(0, 0, width, height); From 01af3e8500ff84b5d2f5179bd767305cc0ce4b4c Mon Sep 17 00:00:00 2001 From: Martin Trang Date: Thu, 12 Mar 2026 15:40:06 -0600 Subject: [PATCH 09/10] More cleanup of JSPI state --- src/unix/web/matoya-worker.js | 6 ++++-- src/unix/web/matoya.js | 6 ------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/unix/web/matoya-worker.js b/src/unix/web/matoya-worker.js index 1e5e6d25..c82291c7 100644 --- a/src/unix/web/matoya-worker.js +++ b/src/unix/web/matoya-worker.js @@ -12,7 +12,9 @@ const MTY = { glObj: {}, fds: {}, fdIndex: 0, - jspi: false, + jspi: typeof WebAssembly !== 'undefined' && + typeof WebAssembly.Suspending === 'function' && + typeof WebAssembly.promising === 'function', promising: {}, }; @@ -877,6 +879,7 @@ const MTY_WEB_API = { // Fallback in case the browser does not support JSPI. web_run_and_yield: function (iter, opaque) { + console.warn('JSPI not supported. Fallback to old behavior. This may cause issues with blocking calls.'); MTY.exports.mty_app_set_keys(); const step = () => { @@ -1216,7 +1219,6 @@ onmessage = async (ev) => { MTY.psync = msg.psync; MTY.audioObjs = msg.audioObjs; MTY.initWindowInfo = msg.windowInfo; - MTY.jspi = msg.jspi === true; MTY.sync = new Int32Array(new SharedArrayBuffer(4)); MTY.sleeper = new Int32Array(new SharedArrayBuffer(4)); MTY.module = await mty_instantiate_wasm(msg.wasmBuf, msg.userEnv); diff --git a/src/unix/web/matoya.js b/src/unix/web/matoya.js index c7372ddc..9ed5b481 100644 --- a/src/unix/web/matoya.js +++ b/src/unix/web/matoya.js @@ -826,7 +826,6 @@ function mty_thread_start(threadId, bin, wasmBuf, memory, startArg, userEnv, kbM threadId: threadId, memory: memory, audioObjs, - jspi: MTY.jspi, }); return worker; @@ -839,11 +838,6 @@ async function MTY_Start(bin, container, userEnv) { MTY.bin = bin; MTY.userEnv = userEnv; - // For now, we'll allow execution if the client does not support JSPI - // by falling back to non-promise based functions - MTY.jspi = typeof WebAssembly !== 'undefined' && - typeof WebAssembly.Suspending === 'function' && - typeof WebAssembly.promising === 'function'; MTY.psync = new Int32Array(new SharedArrayBuffer(4)); MTY.audioObjs = { buf: new Int16Array(new SharedArrayBuffer(1024 * 1024)), From b266b9c0b8dc93a2ed06c128d5409e1f41d18f55 Mon Sep 17 00:00:00 2001 From: Martin Trang Date: Thu, 12 Mar 2026 15:49:14 -0600 Subject: [PATCH 10/10] mo betta --- src/unix/web/app.c | 1 - src/unix/web/matoya-worker.js | 31 ++++++++++++++----------------- src/unix/web/matoya.js | 1 - 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/unix/web/app.c b/src/unix/web/app.c index a0540823..2b5a9e7f 100644 --- a/src/unix/web/app.c +++ b/src/unix/web/app.c @@ -705,7 +705,6 @@ void *MTY_GLGetProcAddress(const char *name) return NULL; } -// Cannot be called on the main thread since the tight loop would block the event loop and prevent yielding. void MTY_RunAndYield(MTY_IterFunc iter, void *opaque) { web_run_and_yield(iter, opaque); diff --git a/src/unix/web/matoya-worker.js b/src/unix/web/matoya-worker.js index c82291c7..63564f1b 100644 --- a/src/unix/web/matoya-worker.js +++ b/src/unix/web/matoya-worker.js @@ -40,21 +40,6 @@ function mty_dup_c(buf) { return ptr; } -function mty_run_until_false(cfunc, opaque) { - return new Promise(resolve => { - const step = () => { - if (cfunc(opaque)) { - setTimeout(step, 0); - - } else { - resolve(); - } - }; - - step(); - }); -} - // Wraps the calling function to allow a Promise to be returned (if needed). // Fallback to a direct call if JSPI is not supported or the export cannot be wrapped. async function mty_call_export(name, ...args) { @@ -84,7 +69,19 @@ async function mty_web_run_and_yield_async(iter, opaque) { MTY.exports.mty_app_set_keys(); const cfunc = mty_cfunc(iter); - await mty_run_until_false(cfunc, opaque); + + await new Promise(resolve => { + const step = () => { + if (cfunc(opaque)) { + setTimeout(step, 0); + + } else { + resolve(); + } + }; + + step(); + }); } @@ -1241,7 +1238,7 @@ onmessage = async (ev) => { close(); } catch (e) { - // Ignore known exception that we throw to continue main thread execution + // Ignore known exception that we throw to continue thread execution if (e.toString().search('run_and_yield halted execution') == -1) console.error(e); } diff --git a/src/unix/web/matoya.js b/src/unix/web/matoya.js index 9ed5b481..9f595cb7 100644 --- a/src/unix/web/matoya.js +++ b/src/unix/web/matoya.js @@ -837,7 +837,6 @@ async function MTY_Start(bin, container, userEnv) { MTY.bin = bin; MTY.userEnv = userEnv; - MTY.psync = new Int32Array(new SharedArrayBuffer(4)); MTY.audioObjs = { buf: new Int16Array(new SharedArrayBuffer(1024 * 1024)),