From 08ffd6594a8b1c281e50b8572ebb2e12ffed5de4 Mon Sep 17 00:00:00 2001 From: oligami Date: Sun, 6 Oct 2024 19:52:28 +0900 Subject: [PATCH] Solution to deadlock problem when lock_double_fd is the same fd. rewritten fd_renumber. add .vscode on .gitignore. rm unused change. Pass fd_map as is to child threads. Change .gitmodules to tabs. reset run-testsuite.sh Minor modifications to the code. patch about null equal undefined on parse JSON. --- .gitignore | 1 + .gitmodules | 2 +- src/fs_mem.ts | 2 +- src/wasi.ts | 8 +- src/wasi_farm/animals.ts | 43 +++++---- src/wasi_farm/shared_array_buffer/ref.ts | 8 ++ .../shared_array_buffer/thread_spawn.ts | 95 ++++++++++--------- .../worker_background/worker.ts | 60 ++++++------ .../worker_background/worker_blob.ts | 2 +- test/run-testsuite.sh | 0 10 files changed, 123 insertions(+), 98 deletions(-) mode change 100644 => 100755 test/run-testsuite.sh diff --git a/.gitignore b/.gitignore index 149320b..c7168c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ dist/ typings/ +.vscode/ diff --git a/.gitmodules b/.gitmodules index 2d9d707..1519d32 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,4 +8,4 @@ [submodule "examples/wasi_multi_threads_rustc/rust_wasm"] path = examples/wasi_multi_threads_rustc/rust_wasm url = https://github.com/oligamiq/rust_wasm - sharrow = true + sharrow = true diff --git a/src/fs_mem.ts b/src/fs_mem.ts index dcfaf7d..6f540ee 100644 --- a/src/fs_mem.ts +++ b/src/fs_mem.ts @@ -128,7 +128,7 @@ export class OpenDirectory extends Fd { super(); this.dir = dir; } - OpenDirectory; + // eslint-disable-next-line @typescript-eslint/no-unused-vars fd_seek(offset: bigint, whence: number): { ret: number; offset: bigint } { return { ret: wasi.ERRNO_BADF, offset: 0n }; diff --git a/src/wasi.ts b/src/wasi.ts index b5e2428..4cb409b 100644 --- a/src/wasi.ts +++ b/src/wasi.ts @@ -16,10 +16,10 @@ export class WASIProcExit extends Error { } export default class WASI { - private args: Array = []; - private env: Array = []; - private fds: Array = []; - private inst: { exports: { memory: WebAssembly.Memory } }; + args: Array = []; + env: Array = []; + fds: Array = []; + inst: { exports: { memory: WebAssembly.Memory } }; // eslint-disable-next-line @typescript-eslint/no-explicit-any wasiImport: { [key: string]: (...args: Array) => unknown }; diff --git a/src/wasi_farm/animals.ts b/src/wasi_farm/animals.ts index 8818104..5ae5b44 100644 --- a/src/wasi_farm/animals.ts +++ b/src/wasi_farm/animals.ts @@ -121,7 +121,11 @@ export class WASIFarmAnimal { const view = new Uint8Array(this.get_share_memory().buffer); view.fill(0); - await this.thread_spawner.async_start_on_thread(this.args, this.env); + await this.thread_spawner.async_start_on_thread( + this.args, + this.env, + this.fd_map, + ); const code = await this.thread_spawner.async_wait_done_or_error(); @@ -148,7 +152,7 @@ export class WASIFarmAnimal { console.log("block_start_on_thread: start"); - this.thread_spawner.block_start_on_thread(this.args, this.env); + this.thread_spawner.block_start_on_thread(this.args, this.env, this.fd_map); console.log("block_start_on_thread: wait"); @@ -303,7 +307,7 @@ export class WASIFarmAnimal { if (rm_fds.length > 0) { for (let i = 0; i < this.fd_map.length; i++) { const fd_and_wasi_ref_n = this.fd_map[i]; - if (fd_and_wasi_ref_n === undefined) { + if (!fd_and_wasi_ref_n) { continue; } const [fd, wasi_ref_n] = fd_and_wasi_ref_n; @@ -333,6 +337,7 @@ export class WASIFarmAnimal { can_thread_spawn?: boolean; thread_spawn_worker_url?: string; thread_spawn_wasm?: WebAssembly.Module; + hand_override_fd_map?: Array<[number, number]>; } = {}, override_fd_maps?: Array, thread_spawner?: ThreadSpawner, @@ -401,6 +406,10 @@ export class WASIFarmAnimal { this.mapping_fds(this.wasi_farm_refs, override_fd_maps); + if (options.hand_override_fd_map) { + this.fd_map = options.hand_override_fd_map; + } + // console.log("this.fd_map", this.fd_map); this.args = args; @@ -848,26 +857,23 @@ export class WASIFarmAnimal { fd_renumber(fd: number, to: number) { self.check_fds(); - const [mapped_fd, wasi_farm_ref_n] = self.get_fd_and_wasi_ref_n(fd); const [mapped_to, wasi_farm_ref_to] = self.get_fd_and_wasi_ref(to); - if ( - mapped_fd === undefined || - wasi_farm_ref_n === undefined || - mapped_to === undefined || - wasi_farm_ref_to === undefined - ) { - return wasi.ERRNO_BADF; + if (mapped_to !== undefined && wasi_farm_ref_to !== undefined) { + const ret = wasi_farm_ref_to.fd_close(mapped_to); + self.check_fds(); + if (ret !== wasi.ERRNO_SUCCESS) { + return ret; + } } - const ret = wasi_farm_ref_to.fd_close(mapped_to); - self.check_fds(); - - if (ret !== wasi.ERRNO_SUCCESS) { - return ret; + if (self.fd_map[to]) { + throw new Error("fd is already mapped"); } - self.map_set_fd_and_notify(mapped_fd, wasi_farm_ref_n, to); + self.fd_map[to] = self.fd_map[fd]; + + self.fd_map[fd] = undefined; return wasi.ERRNO_SUCCESS; }, @@ -1147,6 +1153,9 @@ export class WASIFarmAnimal { new_path_ptr: number, new_path_len: number, ) { + if (old_fd === new_fd) { + return wasi.ERRNO_SUCCESS; + } self.check_fds(); const [mapped_old_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(old_fd); const [mapped_new_fd, wasi_farm_ref_new] = diff --git a/src/wasi_farm/shared_array_buffer/ref.ts b/src/wasi_farm/shared_array_buffer/ref.ts index acae92b..29a209c 100644 --- a/src/wasi_farm/shared_array_buffer/ref.ts +++ b/src/wasi_farm/shared_array_buffer/ref.ts @@ -175,6 +175,10 @@ export class WASIFarmRefUseArrayBuffer extends WASIFarmRef { private lock_double_fd(fd1: number, fd2: number) { // console.log("lock_double_fd", fd1, fd2); + if (fd1 === fd2) { + this.lock_fd(fd1); + return; + } const view = new Int32Array(this.lock_fds); // eslint-disable-next-line no-constant-condition while (true) { @@ -213,6 +217,10 @@ export class WASIFarmRefUseArrayBuffer extends WASIFarmRef { private release_double_fd(fd1: number, fd2: number) { // console.log("release_double_fd", fd1, fd2); + if (fd1 === fd2) { + this.release_fd(fd1); + return; + } const view = new Int32Array(this.lock_fds); Atomics.store(view, fd1 * 3, 0); Atomics.notify(view, fd1 * 3, 1); diff --git a/src/wasi_farm/shared_array_buffer/thread_spawn.ts b/src/wasi_farm/shared_array_buffer/thread_spawn.ts index dc3ba3b..68fcc05 100644 --- a/src/wasi_farm/shared_array_buffer/thread_spawn.ts +++ b/src/wasi_farm/shared_array_buffer/thread_spawn.ts @@ -111,8 +111,9 @@ export class ThreadSpawner { worker_background_ref_object: this.worker_background_ref_object, }); } else { + this.worker_background_ref_object = worker_background_ref_object; this.worker_background_ref = WorkerBackgroundRef.init_self( - worker_background_ref_object, + this.worker_background_ref_object, ); } } @@ -161,6 +162,7 @@ export class ThreadSpawner { async async_start_on_thread( args: Array, env: Array, + fd_map: Array<[number, number]>, ): Promise { if (!self.Worker.toString().includes("[native code]")) { if (self.Worker.toString().includes("function")) { @@ -178,11 +180,16 @@ export class ThreadSpawner { this_is_start: true, args, env, + fd_map, }, ); } - block_start_on_thread(args: Array, env: Array): void { + block_start_on_thread( + args: Array, + env: Array, + fd_map: Array<[number, number]>, + ): void { if (!self.Worker.toString().includes("[native code]")) { if (self.Worker.toString().includes("function")) { console.warn("SubWorker(new Worker on Worker) is polyfilled maybe."); @@ -199,6 +206,7 @@ export class ThreadSpawner { this_is_start: true, args, env, + fd_map, }, ); } @@ -278,29 +286,56 @@ export const thread_spawn_on_worker = async (msg: { thread_spawn_wasm: WebAssembly.Module; args: Array; env: Array; - fd_map: Array; + fd_map: [number, number][]; this_is_start?: boolean; }): Promise => { if (msg.this_is_thread_spawn) { - if (msg.this_is_start) { - const thread_spawner = ThreadSpawner.init_self_with_worker_background_ref( - msg.sl_object, - msg.worker_background_ref, - ); + const { + sl_object, + fd_map, + worker_background_ref, + thread_spawn_wasm, + args, + env, + } = msg; + + const override_fd_map: Array = new Array( + sl_object.wasi_farm_refs_object.length, + ); + + // Possibly null (undefined) + for (const fd_and_wasi_ref_n of fd_map) { + // biome-ignore lint/suspicious/noDoubleEquals: + if (fd_and_wasi_ref_n == undefined) { + continue; + } + const [fd, wasi_ref_n] = fd_and_wasi_ref_n; + if (override_fd_map[wasi_ref_n] === undefined) { + override_fd_map[wasi_ref_n] = []; + } + override_fd_map[wasi_ref_n].push(fd); + } + + const thread_spawner = ThreadSpawner.init_self_with_worker_background_ref( + sl_object, + worker_background_ref, + ); + if (msg.this_is_start) { const wasi = new WASIFarmAnimal( - msg.sl_object.wasi_farm_refs_object, - msg.args, - msg.env, + sl_object.wasi_farm_refs_object, + args, + env, { can_thread_spawn: true, - thread_spawn_worker_url: msg.sl_object.worker_url, + thread_spawn_worker_url: sl_object.worker_url, + hand_override_fd_map: fd_map, }, - undefined, + override_fd_map, thread_spawner, ); - const inst = await WebAssembly.instantiate(msg.thread_spawn_wasm, { + const inst = await WebAssembly.instantiate(thread_spawn_wasm, { env: { memory: wasi.get_share_memory(), }, @@ -324,39 +359,10 @@ export const thread_spawn_on_worker = async (msg: { return wasi; } - const { - worker_id: thread_id, - start_arg, - args, - env, - sl_object, - thread_spawn_wasm, - } = msg; + const { worker_id: thread_id, start_arg } = msg; console.log(`thread_spawn worker ${thread_id} start`); - const thread_spawner = ThreadSpawner.init_self_with_worker_background_ref( - sl_object, - msg.worker_background_ref, - ); - - const override_fd_map: Array = new Array( - sl_object.wasi_farm_refs_object.length, - ); - - // Possibly null (undefined) - for (const fd_and_wasi_ref_n of msg.fd_map) { - // biome-ignore lint/suspicious/noDoubleEquals: - if (fd_and_wasi_ref_n == undefined) { - continue; - } - const [fd, wasi_ref_n] = fd_and_wasi_ref_n; - if (override_fd_map[wasi_ref_n] === undefined) { - override_fd_map[wasi_ref_n] = []; - } - override_fd_map[wasi_ref_n].push(fd); - } - const wasi = new WASIFarmAnimal( sl_object.wasi_farm_refs_object, args, @@ -364,6 +370,7 @@ export const thread_spawn_on_worker = async (msg: { { can_thread_spawn: true, thread_spawn_worker_url: sl_object.worker_url, + hand_override_fd_map: fd_map, }, override_fd_map, thread_spawner, diff --git a/src/wasi_farm/shared_array_buffer/worker_background/worker.ts b/src/wasi_farm/shared_array_buffer/worker_background/worker.ts index 9db336e..c779c17 100644 --- a/src/wasi_farm/shared_array_buffer/worker_background/worker.ts +++ b/src/wasi_farm/shared_array_buffer/worker_background/worker.ts @@ -96,25 +96,35 @@ class WorkerBackground { throw new Error("locked"); } + const gen_worker = () => { + console.log("gen_worker"); + const url_ptr = Atomics.load(signature_input_view, 1); + const url_len = Atomics.load(signature_input_view, 2); + const url_buff = this.allocator.get_memory(url_ptr, url_len); + this.allocator.free(url_ptr, url_len); + const url = new TextDecoder().decode(url_buff); + const is_module = Atomics.load(signature_input_view, 3) === 1; + return new Worker(url, { + type: is_module ? "module" : "classic", + }); + }; + + const gen_obj = () => { + console.log("gen_obj"); + const json_ptr = Atomics.load(signature_input_view, 4); + const json_len = Atomics.load(signature_input_view, 5); + const json_buff = this.allocator.get_memory(json_ptr, json_len); + this.allocator.free(json_ptr, json_len); + const json = new TextDecoder().decode(json_buff); + return JSON.parse(json); + }; + const signature_input = Atomics.load(signature_input_view, 0); switch (signature_input) { // create new worker case 1: { - const url_ptr = Atomics.load(signature_input_view, 1); - const url_len = Atomics.load(signature_input_view, 2); - const url_buff = this.allocator.get_memory(url_ptr, url_len); - this.allocator.free(url_ptr, url_len); - const url = new TextDecoder().decode(url_buff); - const is_module = Atomics.load(signature_input_view, 3) === 1; - const worker = new Worker(url, { - type: is_module ? "module" : "classic", - }); - const json_ptr = Atomics.load(signature_input_view, 4); - const json_len = Atomics.load(signature_input_view, 5); - const json_buff = this.allocator.get_memory(json_ptr, json_len); - this.allocator.free(json_ptr, json_len); - const json = new TextDecoder().decode(json_buff); - const obj = JSON.parse(json); + const worker = gen_worker(); + const obj = gen_obj(); const worker_id = this.assign_worker_id(); @@ -212,21 +222,8 @@ class WorkerBackground { } // create start case 2: { - const url_ptr = Atomics.load(signature_input_view, 1); - const url_len = Atomics.load(signature_input_view, 2); - const url_buff = this.allocator.get_memory(url_ptr, url_len); - this.allocator.free(url_ptr, url_len); - const url = new TextDecoder().decode(url_buff); - const is_module = Atomics.load(signature_input_view, 3) === 1; - this.start_worker = new Worker(url, { - type: is_module ? "module" : "classic", - }); - const json_ptr = Atomics.load(signature_input_view, 4); - const json_len = Atomics.load(signature_input_view, 5); - const json_buff = this.allocator.get_memory(json_ptr, json_len); - this.allocator.free(json_ptr, json_len); - const json = new TextDecoder().decode(json_buff); - const obj = JSON.parse(json); + this.start_worker = gen_worker(); + const obj = gen_obj(); this.start_worker.onmessage = async (e) => { const { msg } = e.data; @@ -272,6 +269,9 @@ class WorkerBackground { } } catch (e) { console.error(e); + + // sleep 1000 + await new Promise((resolve) => setTimeout(resolve, 1000)); } } } diff --git a/src/wasi_farm/shared_array_buffer/worker_background/worker_blob.ts b/src/wasi_farm/shared_array_buffer/worker_background/worker_blob.ts index 8a1adf3..0814b1e 100644 --- a/src/wasi_farm/shared_array_buffer/worker_background/worker_blob.ts +++ b/src/wasi_farm/shared_array_buffer/worker_background/worker_blob.ts @@ -1,6 +1,6 @@ export const url = () => { const code = - 'let worker_background;function _define_property(e,r,t){return r in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}let serialize=e=>({message:e.message,name:e.name,stack:e.stack,cause:e.cause});class AllocatorUseArrayBuffer{static init_self(e){return new AllocatorUseArrayBuffer(e.share_arrays_memory)}async async_write(e,r,t){let o=new Int32Array(this.share_arrays_memory);for(;;){let{value:s}=Atomics.waitAsync(o,0,1);if("timed-out"===(s instanceof Promise?await s:s))throw Error("timed-out lock");if(0!==Atomics.compareExchange(o,0,0,1))continue;let i=this.write_inner(e,r,t);return Atomics.store(o,0,0),Atomics.notify(o,0,1),i}}block_write(e,r,t){for(;;){let o=new Int32Array(this.share_arrays_memory);if("timed-out"===Atomics.wait(o,0,1))throw Error("timed-out lock");if(0!==Atomics.compareExchange(o,0,0,1))continue;let s=this.write_inner(e,r,t);return Atomics.store(o,0,0),Atomics.notify(o,0,1),s}}write_inner(e,r,t){let o,s;let i=new Int32Array(this.share_arrays_memory),a=new Uint8Array(this.share_arrays_memory);o=0===Atomics.add(i,1,1)?Atomics.store(i,2,12):Atomics.load(i,2);let n=this.share_arrays_memory.byteLength,c=e.byteLength,l=o+c;if(n{let{msg:r}=e.data;if("ready"===r&&y(),"done"===r&&(this.workers[w].terminate(),this.workers[w]=void 0,console.log(`worker ${w} done so terminate`)),"error"===r){this.workers[w].terminate(),this.workers[w]=void 0;let r=0;for(let e of this.workers)void 0!==e&&(e.terminate(),console.warn("wasi throw error but child process exists, terminate "+r)),r++;void 0!==this.start_worker&&(this.start_worker.terminate(),console.warn("wasi throw error but wasi exists, terminate wasi")),this.workers=[void 0],this.start_worker=void 0;let t=e.data.error,o=new Int32Array(this.lock,8),s=serialize(t),[i,a]=await this.allocator.async_write(new TextEncoder().encode(JSON.stringify(s)),this.lock,3),n=Atomics.compareExchange(o,0,0,1);if(0!==n){console.error("what happened?"),this.allocator.free(i,a);return}let c=Atomics.notify(o,0);0===c&&(console.error(t),this.allocator.free(i,a),Atomics.store(o,0,0))}},a.postMessage({...this.override_object,...m,worker_id:w,worker_background_ref:this.ref()}),await d,Atomics.store(r,0,w);break}case 2:{let e=Atomics.load(r,1),t=Atomics.load(r,2),o=this.allocator.get_memory(e,t);this.allocator.free(e,t);let s=new TextDecoder().decode(o),i=1===Atomics.load(r,3);this.start_worker=new Worker(s,{type:i?"module":"classic"});let a=Atomics.load(r,4),n=Atomics.load(r,5),c=this.allocator.get_memory(a,n);this.allocator.free(a,n);let l=new TextDecoder().decode(c),h=JSON.parse(l);this.start_worker.onmessage=async e=>{let{msg:r}=e.data;if("done"===r){let e=0;for(let r of this.workers)void 0!==r&&(r.terminate(),console.warn("wasi done but worker exists, terminate "+e)),e++;this.start_worker.terminate(),this.start_worker=void 0,console.log("start worker done so terminate")}},this.start_worker.postMessage({...this.override_object,...h,worker_background_ref:this.ref()})}}let i=Atomics.exchange(e,1,0);if(1!==i)throw Error("Lock is already set");let a=Atomics.notify(e,1,1);if(1!==a){if(0===a){console.warn("notify failed, waiter is late");continue}throw Error("notify failed: "+a)}}catch(e){console.error(e)}}constructor(e,r,t,o){_define_property(this,"override_object",void 0),_define_property(this,"allocator",void 0),_define_property(this,"lock",void 0),_define_property(this,"signature_input",void 0),_define_property(this,"workers",[void 0]),_define_property(this,"start_worker",void 0),_define_property(this,"listen_holder",void 0),this.override_object=e,this.lock=r??new SharedArrayBuffer(20),this.allocator=t??new AllocatorUseArrayBuffer(new SharedArrayBuffer(10240)),this.signature_input=o??new SharedArrayBuffer(24),this.listen_holder=this.listen()}};globalThis.onmessage=e=>{let{override_object:r,worker_background_ref_object:t}=e.data;worker_background=WorkerBackground.init_self(r,t),postMessage("ready")};'; + 'let worker_background;function _define_property(e,r,t){return r in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}let serialize=e=>({message:e.message,name:e.name,stack:e.stack,cause:e.cause});class AllocatorUseArrayBuffer{static init_self(e){return new AllocatorUseArrayBuffer(e.share_arrays_memory)}async async_write(e,r,t){let o=new Int32Array(this.share_arrays_memory);for(;;){let{value:s}=Atomics.waitAsync(o,0,1);if("timed-out"===(s instanceof Promise?await s:s))throw Error("timed-out lock");if(0!==Atomics.compareExchange(o,0,0,1))continue;let i=this.write_inner(e,r,t);return Atomics.store(o,0,0),Atomics.notify(o,0,1),i}}block_write(e,r,t){for(;;){let o=new Int32Array(this.share_arrays_memory);if("timed-out"===Atomics.wait(o,0,1))throw Error("timed-out lock");if(0!==Atomics.compareExchange(o,0,0,1))continue;let s=this.write_inner(e,r,t);return Atomics.store(o,0,0),Atomics.notify(o,0,1),s}}write_inner(e,r,t){let o,s;let i=new Int32Array(this.share_arrays_memory),a=new Uint8Array(this.share_arrays_memory);o=0===Atomics.add(i,1,1)?Atomics.store(i,2,12):Atomics.load(i,2);let n=this.share_arrays_memory.byteLength,l=e.byteLength,c=o+l;if(n{console.log("gen_worker");let e=Atomics.load(r,1),t=Atomics.load(r,2),o=this.allocator.get_memory(e,t);this.allocator.free(e,t);let s=new TextDecoder().decode(o),i=1===Atomics.load(r,3);return new Worker(s,{type:i?"module":"classic"})},a=()=>{console.log("gen_obj");let e=Atomics.load(r,4),t=Atomics.load(r,5),o=this.allocator.get_memory(e,t);this.allocator.free(e,t);let s=new TextDecoder().decode(o);return JSON.parse(s)};switch(Atomics.load(r,0)){case 1:{let e=i(),t=a(),o=this.assign_worker_id();console.log("new worker "+o),this.workers[o]=e;let{promise:s,resolve:n}=Promise.withResolvers();e.onmessage=async e=>{let{msg:r}=e.data;if("ready"===r&&n(),"done"===r&&(this.workers[o].terminate(),this.workers[o]=void 0,console.log(`worker ${o} done so terminate`)),"error"===r){this.workers[o].terminate(),this.workers[o]=void 0;let r=0;for(let e of this.workers)void 0!==e&&(e.terminate(),console.warn("wasi throw error but child process exists, terminate "+r)),r++;void 0!==this.start_worker&&(this.start_worker.terminate(),console.warn("wasi throw error but wasi exists, terminate wasi")),this.workers=[void 0],this.start_worker=void 0;let t=e.data.error,s=new Int32Array(this.lock,8),i=serialize(t),[a,n]=await this.allocator.async_write(new TextEncoder().encode(JSON.stringify(i)),this.lock,3),l=Atomics.compareExchange(s,0,0,1);if(0!==l){console.error("what happened?"),this.allocator.free(a,n);return}let c=Atomics.notify(s,0);0===c&&(console.error(t),this.allocator.free(a,n),Atomics.store(s,0,0))}},e.postMessage({...this.override_object,...t,worker_id:o,worker_background_ref:this.ref()}),await s,Atomics.store(r,0,o);break}case 2:{this.start_worker=i();let e=a();this.start_worker.onmessage=async e=>{let{msg:r}=e.data;if("done"===r){let e=0;for(let r of this.workers)void 0!==r&&(r.terminate(),console.warn("wasi done but worker exists, terminate "+e)),e++;this.start_worker.terminate(),this.start_worker=void 0,console.log("start worker done so terminate")}},this.start_worker.postMessage({...this.override_object,...e,worker_background_ref:this.ref()})}}let n=Atomics.exchange(e,1,0);if(1!==n)throw Error("Lock is already set");let l=Atomics.notify(e,1,1);if(1!==l){if(0===l){console.warn("notify failed, waiter is late");continue}throw Error("notify failed: "+l)}}catch(e){console.error(e),await new Promise(e=>setTimeout(e,1e3))}}constructor(e,r,t,o){_define_property(this,"override_object",void 0),_define_property(this,"allocator",void 0),_define_property(this,"lock",void 0),_define_property(this,"signature_input",void 0),_define_property(this,"workers",[void 0]),_define_property(this,"start_worker",void 0),_define_property(this,"listen_holder",void 0),this.override_object=e,this.lock=r??new SharedArrayBuffer(20),this.allocator=t??new AllocatorUseArrayBuffer(new SharedArrayBuffer(10240)),this.signature_input=o??new SharedArrayBuffer(24),this.listen_holder=this.listen()}};globalThis.onmessage=e=>{let{override_object:r,worker_background_ref_object:t}=e.data;worker_background=WorkerBackground.init_self(r,t),postMessage("ready")};'; const blob = new Blob([code], { type: "application/javascript" }); diff --git a/test/run-testsuite.sh b/test/run-testsuite.sh old mode 100644 new mode 100755