From 01b5fdd9218ac73c5907a7eedf8f9cba0d13594e Mon Sep 17 00:00:00 2001 From: toyobayashi Date: Wed, 8 May 2024 22:21:41 +0800 Subject: [PATCH] refactor: use @emnapi/wasi-threads as dep --- packages/core/script/build.js | 2 +- packages/core/src/emnapi/index.d.ts | 6 + packages/core/src/load.ts | 20 +- packages/core/src/worker.ts | 133 ++--------- packages/core/tsconfig.json | 1 + packages/emnapi/script/build.js | 1 + packages/emnapi/src/core/async-work.ts | 2 +- packages/emnapi/src/core/init.ts | 252 ++------------------ packages/emnapi/src/core/scope.d.ts | 5 + packages/wasi-threads/script/build.js | 1 + packages/wasi-threads/src/thread-manager.ts | 9 +- packages/wasi-threads/src/wasi-threads.ts | 31 ++- packages/wasi-threads/src/worker.ts | 19 +- 13 files changed, 94 insertions(+), 388 deletions(-) diff --git a/packages/core/script/build.js b/packages/core/script/build.js index 75955c6f..a429e06f 100644 --- a/packages/core/script/build.js +++ b/packages/core/script/build.js @@ -14,7 +14,7 @@ const dist = path.join(__dirname, '../dist') function build () { compile(path.join(__dirname, '../tsconfig.json'), { optionsToExtend: { - target: require('typescript').ScriptTarget.ES2019, + target: ts.ScriptTarget.ES2019, emitDeclarationOnly: true, declaration: true, declarationDir: path.join(__dirname, '../lib/typings') diff --git a/packages/core/src/emnapi/index.d.ts b/packages/core/src/emnapi/index.d.ts index e9c5b174..14d42103 100644 --- a/packages/core/src/emnapi/index.d.ts +++ b/packages/core/src/emnapi/index.d.ts @@ -1,4 +1,5 @@ import type { Context } from '@emnapi/runtime' +import type { ThreadManager } from '@emnapi/wasi-threads' /** @public */ export declare interface PointerInfo { @@ -34,6 +35,7 @@ export declare interface NapiModule { len?: number ): T getMemoryAddress (arrayBufferOrView: ArrayBuffer | ArrayBufferView): PointerInfo + addSendListener (worker: any): boolean } init (options: InitOptions): any @@ -42,6 +44,10 @@ export declare interface NapiModule { initWorker (arg: number): void executeAsyncWork (work: number): void postMessage?: (msg: any) => any + + waitThreadStart: boolean + /** @internal */ + PThread: ThreadManager } /** @public */ diff --git a/packages/core/src/load.ts b/packages/core/src/load.ts index 56bb7550..0fe3ea61 100644 --- a/packages/core/src/load.ts +++ b/packages/core/src/load.ts @@ -1,3 +1,4 @@ +import { WASIThreads } from '@emnapi/wasi-threads' import { type InputType, load, loadSync } from './util' import { createNapiModule } from './emnapi/index' import type { CreateOptions, NapiModule } from './emnapi/index' @@ -67,16 +68,18 @@ function loadNapiModuleImpl (loadFn: Function, userNapiModule: NapiModule | unde } const wasi = options!.wasi + const wasiThreads = new WASIThreads( + napiModule.childThread + ? { postMessage: napiModule.postMessage! } + : { + threadManager: napiModule.PThread, + waitThreadStart: napiModule.waitThreadStart + } + ) let importObject: WebAssembly.Imports = { env: napiModule.imports.env, napi: napiModule.imports.napi, - emnapi: napiModule.imports.emnapi, - wasi: { - // eslint-disable-next-line camelcase - 'thread-spawn': function __imported_wasi_thread_spawn (startArg: number, errorOrTid: number) { - return napiModule.spawnThread(startArg, errorOrTid) - } - } + emnapi: napiModule.imports.emnapi } if (wasi) { @@ -88,6 +91,8 @@ function loadNapiModuleImpl (loadFn: Function, userNapiModule: NapiModule | unde ) } + Object.assign(importObject, wasiThreads.getImportObject()) + const overwriteImports = options!.overwriteImports if (typeof overwriteImports === 'function') { const newImportObject = overwriteImports(importObject) @@ -177,6 +182,7 @@ function loadNapiModuleImpl (loadFn: Function, userNapiModule: NapiModule | unde } wasi.initialize(instance) } + wasiThreads.setup(instance, module, memory) if (beforeInit) { beforeInit({ diff --git a/packages/core/src/worker.ts b/packages/core/src/worker.ts index 2def93ef..0fa7a842 100644 --- a/packages/core/src/worker.ts +++ b/packages/core/src/worker.ts @@ -1,144 +1,41 @@ +import { + MessageHandler as WASIThreadsMessageHandler, + type HandleOptions, + type OnLoadData +} from '@emnapi/wasi-threads' import type { NapiModule } from './emnapi/index' import type { InstantiatedSource } from './load' -/** @public */ -export interface OnLoadData { - wasmModule: WebAssembly.Module - wasmMemory: WebAssembly.Memory -} +export type { HandleOptions, OnLoadData } /** @public */ -export interface HandleOptions { - onLoad (data: OnLoadData): InstantiatedSource | Promise -} - -/** @public */ -export class MessageHandler { - onLoad: (data: OnLoadData) => InstantiatedSource | Promise - instance: WebAssembly.Instance | undefined - // module: WebAssembly.Module | undefined +export class MessageHandler extends WASIThreadsMessageHandler { napiModule: NapiModule | undefined - messagesBeforeLoad: any[] constructor (options: HandleOptions) { - const onLoad = options.onLoad - if (typeof onLoad !== 'function') { - throw new TypeError('options.onLoad is not a function') - } - this.onLoad = onLoad - this.instance = undefined - // this.module = undefined + super(options) this.napiModule = undefined - this.messagesBeforeLoad = [] } - handle (e: any): void { + public override handle (e: any): void { + super.handle(e) if (e?.data?.__emnapi__) { const type = e.data.__emnapi__.type const payload = e.data.__emnapi__.payload - const onLoad = this.onLoad - if (type === 'load') { - if (this.instance !== undefined) return - let source: InstantiatedSource | Promise - try { - source = onLoad(payload) - } catch (err) { - onLoaded.call(this, err, null, payload) - return - } - const then = source && 'then' in source ? source.then : undefined - if (typeof then === 'function') { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - then.call( - source, - (source) => { onLoaded.call(this, null, source, payload) }, - (err) => { onLoaded.call(this, err, null, payload) } - ) - } else { - onLoaded.call(this, null, source as InstantiatedSource, payload) - } - } else if (type === 'start') { - handleAfterLoad.call(this, e, () => { - notifyPthreadCreateResult(payload.sab, 1) - this.napiModule!.startThread(payload.tid, payload.arg) - }) - } else if (type === 'async-worker-init') { - handleAfterLoad.call(this, e, () => { + if (type === 'async-worker-init') { + this.handleAfterLoad(e, () => { this.napiModule!.initWorker(payload.arg) }) } else if (type === 'async-work-execute') { - handleAfterLoad.call(this, e, () => { + this.handleAfterLoad(e, () => { this.napiModule!.executeAsyncWork(payload.work) }) } } } -} - -function handleAfterLoad (this: MessageHandler, e: any, f: (e: any) => void): void { - if (this.instance !== undefined) { - f.call(this, e) - } else { - this.messagesBeforeLoad.push(e.data) - } -} - -interface LoadPayload { - wasmModule: WebAssembly.Module - wasmMemory: WebAssembly.Memory - sab?: Int32Array -} - -function notifyPthreadCreateResult (sab: Int32Array | undefined, result: number): void { - if (sab) { - Atomics.store(sab, 0, result) - Atomics.notify(sab, 0) - } -} - -function onLoaded (this: MessageHandler, err: Error | null, source: InstantiatedSource | null, payload: LoadPayload): void { - if (err) { - notifyPthreadCreateResult(payload.sab, 2) - throw err - } - - if (source == null) { - notifyPthreadCreateResult(payload.sab, 2) - throw new TypeError('onLoad should return an object') - } - - const instance = source.instance - const napiModule = source.napiModule - - if (!instance) { - notifyPthreadCreateResult(payload.sab, 2) - throw new TypeError('onLoad should return an object which includes "instance"') - } - if (!napiModule) { - notifyPthreadCreateResult(payload.sab, 2) - throw new TypeError('onLoad should return an object which includes "napiModule"') - } - if (!napiModule.childThread) { - notifyPthreadCreateResult(payload.sab, 2) - throw new Error('napiModule should be created with `childThread: true`') - } - - this.instance = instance - this.napiModule = napiModule - - const postMessage = napiModule.postMessage! - postMessage({ - __emnapi__: { - type: 'loaded', - payload: {} - } - }) - const messages = this.messagesBeforeLoad - this.messagesBeforeLoad = [] - for (let i = 0; i < messages.length; i++) { - const data = messages[i] - this.handle({ data }) + protected override onLoadSuccess (source: InstantiatedSource): void { + this.napiModule = source.napiModule } } diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 577085da..12f574f3 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -10,6 +10,7 @@ "outDir": "lib", "paths": { "tslib" : ["../../node_modules/tslib/tslib.d.ts"], + "@emnapi/wasi-threads": ["../wasi-threads/lib/typings/index.d.ts"], "@/*": ["./src/*"], }, "lib": [ diff --git a/packages/emnapi/script/build.js b/packages/emnapi/script/build.js index 916bfeb4..358e8c3e 100644 --- a/packages/emnapi/script/build.js +++ b/packages/emnapi/script/build.js @@ -129,6 +129,7 @@ async function build () { }) const parsedCode = compiler.parseCode(code) return `import { _WebAssembly as WebAssembly } from '@/util' +import * as wasiThreads from '@emnapi/wasi-threads' export function createNapiModule (options) { ${parsedCode} diff --git a/packages/emnapi/src/core/async-work.ts b/packages/emnapi/src/core/async-work.ts index c3ee2929..1cb77f2a 100644 --- a/packages/emnapi/src/core/async-work.ts +++ b/packages/emnapi/src/core/async-work.ts @@ -86,7 +86,7 @@ var emnapiAWMT = { } try { for (let i = 0; i < n; ++i) { - const worker = onCreateWorker({ type: 'async-work' }) + const worker = onCreateWorker({ type: 'async-work', name: 'emnapi-async-worker' }) const p = PThread.loadWasmModuleToWorker(worker) emnapiAWMT.addListener(worker) promises.push(p.then(() => { diff --git a/packages/emnapi/src/core/init.ts b/packages/emnapi/src/core/init.ts index ac35ecca..77e946fc 100644 --- a/packages/emnapi/src/core/init.ts +++ b/packages/emnapi/src/core/init.ts @@ -3,8 +3,6 @@ import { makeDynCall, to64 } from 'emscripten:parse-tools' -type SharedInt32Array = Int32Array - export interface InitOptions { instance: WebAssembly.Instance module: WebAssembly.Module @@ -31,6 +29,9 @@ export interface INapiModule { initWorker (arg: number): void executeAsyncWork (work: number): void postMessage?: (msg: any) => any + + waitThreadStart: boolean + PThread: ThreadManager } declare const process: any @@ -77,6 +78,9 @@ export var napiModule: INapiModule = { initWorker: undefined!, executeAsyncWork: undefined!, + waitThreadStart, + PThread: undefined!, + init (options: InitOptions) { if (napiModule.loaded) return napiModule.exports if (!options) throw new TypeError('Invalid napi init options') @@ -136,7 +140,7 @@ export var napiModule: INapiModule = { export var emnapiCtx: Context export var emnapiNodeBinding: NodeBinding -export var onCreateWorker: (info: { type: 'thread' | 'async-work' }) => any +export var onCreateWorker: (info: { type: 'thread' | 'async-work'; name: string }) => any = undefined! export var out: (str: string) => void export var err: (str: string) => void @@ -277,237 +281,13 @@ function checkSharedWasmMemory (): void { } } -function spawnThread (startArg: number, errorOrTid: number): number { - checkSharedWasmMemory() - - const isNewABI = errorOrTid !== undefined - if (!isNewABI) { - errorOrTid = _malloc(to64('8')) - if (!errorOrTid) { - return -48 /* ENOMEM */ - } - } - const struct = new Int32Array(wasmMemory.buffer, errorOrTid, 2) - Atomics.store(struct, 0, 0) - Atomics.store(struct, 1, 0) - - if (ENVIRONMENT_IS_PTHREAD) { - const postMessage = napiModule.postMessage! - postMessage({ - __emnapi__: { - type: 'spawn-thread', - payload: { - startArg, - errorOrTid - } - } - }) - Atomics.wait(struct, 1, 0) - const isError = Atomics.load(struct, 0) - const result = Atomics.load(struct, 1) - if (isNewABI) { - return isError - } - _free(to64('errorOrTid')) - return isError ? -result : result - } - - let sab: Int32Array | undefined - if (waitThreadStart) { - sab = new Int32Array(new SharedArrayBuffer(4)) - Atomics.store(sab, 0, 0) - } - - let worker: any - const tid = PThread.nextWorkerID + 43 - try { - worker = PThread.getNewWorker(sab) - if (!worker) { - throw new Error('failed to get new worker') - } - - const WASI_THREADS_MAX_TID = 0x1FFFFFFF - PThread.nextWorkerID = (PThread.nextWorkerID + 1) % (WASI_THREADS_MAX_TID - 42) - PThread.pthreads[tid] = worker - worker.__emnapi_tid = tid - if (ENVIRONMENT_IS_NODE) { - worker.ref() - } - worker.postMessage({ - __emnapi__: { - type: 'start', - payload: { - tid, - arg: startArg, - sab - } - } - }) - if (waitThreadStart) { - Atomics.wait(sab!, 0, 0) - const r = Atomics.load(sab!, 0) - if (r === 2) { - throw new Error('failed to start pthread') - } - } - } catch (e) { - const EAGAIN = 6 - - Atomics.store(struct, 0, 1) - Atomics.store(struct, 1, EAGAIN) - Atomics.notify(struct, 1) - - err(e.message) - if (isNewABI) { - return 1 - } - _free(to64('errorOrTid')) - return -EAGAIN - } - - Atomics.store(struct, 0, 0) - Atomics.store(struct, 1, tid) - Atomics.notify(struct, 1) - - PThread.runningWorkers.push(worker) - if (!waitThreadStart) { - worker.whenLoaded.catch((err: any) => { - delete worker.whenLoaded - cleanThread(worker, tid, true) - throw err - }) - } - - if (isNewABI) { - return 0 - } - _free(to64('errorOrTid')) - return tid -} - -function startThread (tid: number, startArg: number): void { - if (napiModule.childThread) { - if (typeof wasmInstance.exports.wasi_thread_start !== 'function') { - throw new TypeError('wasi_thread_start is not exported') - } - const postMessage = napiModule.postMessage! - ;(wasmInstance.exports.wasi_thread_start as Function)(tid, startArg) - postMessage({ - __emnapi__: { - type: 'cleanup-thread', - payload: { - tid - } - } - }) - } else { - throw new Error('startThread is only available in child threads') - } -} - -napiModule.spawnThread = spawnThread -napiModule.startThread = startThread - -export var PThread = { - unusedWorkers: [] as any[], - runningWorkers: [] as any[], - pthreads: Object.create(null), - nextWorkerID: 0, - init () {}, - returnWorkerToPool (worker: any) { - var tid = worker.__emnapi_tid - delete PThread.pthreads[tid] - PThread.unusedWorkers.push(worker) - PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker), 1) - delete worker.__emnapi_tid - if (ENVIRONMENT_IS_NODE) { - worker.unref() - } +export var PThread = new wasiThreads.ThreadManager({ + printErr: err, + beforeLoad: (worker) => { + emnapiAddSendListener(worker) }, - loadWasmModuleToWorker: (worker: any, sab?: SharedInt32Array) => { - if (worker.whenLoaded) return worker.whenLoaded - worker.whenLoaded = new Promise((resolve, reject) => { - worker.onmessage = function (e: any) { - if (e.data.__emnapi__) { - const type = e.data.__emnapi__.type - const payload = e.data.__emnapi__.payload - if (type === 'loaded') { - worker.loaded = true - if (ENVIRONMENT_IS_NODE && !worker.__emnapi_tid) { - worker.unref() - } - resolve(worker) - // if (payload.err) { - // err('failed to load in child thread: ' + (payload.err.message || payload.err)) - // } - } else if (type === 'spawn-thread') { - spawnThread(payload.startArg, payload.errorOrTid) - } else if (type === 'cleanup-thread') { - cleanThread(worker, payload.tid) - } - } - } - worker.onerror = (e: any) => { - const message = 'worker sent an error!' - // if (worker.pthread_ptr) { - // message = 'Pthread ' + ptrToString(worker.pthread_ptr) + ' sent an error!' - // } - err(message + ' ' + e.message) - reject(e) - throw e - } - if (ENVIRONMENT_IS_NODE) { - worker.on('message', function (data: any) { - worker.onmessage({ - data - }) - }) - worker.on('error', function (e: any) { - worker.onerror(e) - }) - worker.on('detachedExit', function () {}) - } - // napiModule.emnapi.addSendListener(worker) - emnapiAddSendListener(worker) - // if (typeof emnapiTSFN !== 'undefined') { - // emnapiTSFN.addListener(worker) - // } - try { - worker.postMessage({ - __emnapi__: { - type: 'load', - payload: { - wasmModule, - wasmMemory, - sab - } - } - }) - } catch (err) { - checkSharedWasmMemory() - throw err - } - }) - return worker.whenLoaded - }, - allocateUnusedWorker () { - if (typeof onCreateWorker !== 'function') { - throw new TypeError('`options.onCreateWorker` is not provided') - } - const worker = onCreateWorker({ type: 'thread' }) - PThread.unusedWorkers.push(worker) - return worker - }, - getNewWorker (sab?: SharedInt32Array) { - if (reuseWorker) { - if (PThread.unusedWorkers.length === 0) { - const worker = PThread.allocateUnusedWorker() - PThread.loadWasmModuleToWorker(worker, sab) - } - return PThread.unusedWorkers.pop() - } - const worker = PThread.allocateUnusedWorker() - PThread.loadWasmModuleToWorker(worker, sab) - return PThread.unusedWorkers.pop() - } -} + reuseWorker, + onCreateWorker: onCreateWorker as ThreadManagerOptions['onCreateWorker'] +}) + +napiModule.PThread = PThread diff --git a/packages/emnapi/src/core/scope.d.ts b/packages/emnapi/src/core/scope.d.ts index dca5563c..344c0e37 100644 --- a/packages/emnapi/src/core/scope.d.ts +++ b/packages/emnapi/src/core/scope.d.ts @@ -14,3 +14,8 @@ declare interface CreateOptions { // factory parameter declare const options: CreateOptions + +declare const wasiThreads: typeof import('../../../wasi-threads/lib/typings/index') + +declare type ThreadManagerOptions = import('../../../wasi-threads/lib/typings/index').ThreadManagerOptions +declare type ThreadManager = import('../../../wasi-threads/lib/typings/index').ThreadManager diff --git a/packages/wasi-threads/script/build.js b/packages/wasi-threads/script/build.js index 3aab2b44..b568cb38 100644 --- a/packages/wasi-threads/script/build.js +++ b/packages/wasi-threads/script/build.js @@ -16,6 +16,7 @@ function build () { target: require('typescript').ScriptTarget.ES2019, emitDeclarationOnly: true, declaration: true, + declarationMap: true, declarationDir: path.join(__dirname, '../lib/typings') } }) diff --git a/packages/wasi-threads/src/thread-manager.ts b/packages/wasi-threads/src/thread-manager.ts index d0f9710f..b99e45ae 100644 --- a/packages/wasi-threads/src/thread-manager.ts +++ b/packages/wasi-threads/src/thread-manager.ts @@ -16,7 +16,7 @@ export interface WorkerMessageEvent { /** @public */ export interface ThreadManagerOptions { - err?: (message: string) => void + printErr?: (message: string) => void beforeLoad?: (worker: WorkerLike) => any reuseWorker?: boolean onCreateWorker?: (ctx: { type: string; name: string }) => WorkerLike @@ -80,7 +80,7 @@ export class ThreadManager { public loadWasmModuleToWorker (worker: WorkerLike, sab?: Int32Array): Promise { if (worker.whenLoaded) return worker.whenLoaded - const err = this._options.err + const err = this._options.printErr const beforeLoad = this._options.beforeLoad worker.whenLoaded = new Promise((resolve, reject) => { const handleError = function (e: { message: string }): void { @@ -190,12 +190,13 @@ export class ThreadManager { public terminateWorker (worker: WorkerLike): void { const tid = worker.__emnapi_tid + // eslint-disable-next-line @typescript-eslint/no-floating-promises worker.terminate() this.messageEvents.get(worker)?.clear() this.messageEvents.delete(worker); (worker as Worker).onmessage = (e: any) => { if (e.data.__emnapi__) { - this._options.err?.('received "' + e.data.__emnapi__.type + '" command from terminated worker: ' + tid) + this._options.printErr?.('received "' + e.data.__emnapi__.type + '" command from terminated worker: ' + tid) } } } @@ -215,7 +216,7 @@ export class ThreadManager { public fireMessageEvent (worker: WorkerLike, e: WorkerMessageEvent): void { const listeners = this.messageEvents.get(worker) if (!listeners) return - const err = this._options.err + const err = this._options.printErr listeners.forEach((listener) => { try { listener(e) diff --git a/packages/wasi-threads/src/wasi-threads.ts b/packages/wasi-threads/src/wasi-threads.ts index 52d1cc02..b25ee500 100644 --- a/packages/wasi-threads/src/wasi-threads.ts +++ b/packages/wasi-threads/src/wasi-threads.ts @@ -4,6 +4,7 @@ import type { ThreadManager, WorkerMessageEvent } from './thread-manager' /** @public */ export interface BaseOptions { version?: 'preview1' + wasm64?: boolean } /** @public */ @@ -31,8 +32,6 @@ export class WASIThreads { private wasmMemory!: WebAssembly.Memory private wasmInstance!: WebAssembly.Instance - private readonly waitThreadStart: boolean - private readonly postMessage: ((data: any) => void) | undefined private readonly threadSpawn: (startArg: number, errorOrTid?: number) => number public constructor (options: WASIThreadsOptions) { @@ -44,21 +43,21 @@ export class WASIThreads { } } + let waitThreadStart = false if ('waitThreadStart' in options) { - this.waitThreadStart = Boolean(options.waitThreadStart) - } else { - this.waitThreadStart = false + waitThreadStart = Boolean(options.waitThreadStart) } + let postMessage: ((data: any) => void) | undefined if ('postMessage' in options) { if (typeof options.postMessage !== 'function') { throw new TypeError('options.postMessage is not a function') } - this.postMessage = postMessage - } else { - this.postMessage = undefined + postMessage = options.postMessage } + const wasm64 = Boolean(options.wasm64) + const onSpawn = (e: WorkerMessageEvent): void => { if (e.data.__emnapi__) { const type = e.data.__emnapi__.type @@ -75,18 +74,18 @@ export class WASIThreads { const isNewABI = errorOrTid !== undefined if (!isNewABI) { const malloc = this.wasmInstance.exports.malloc as Function - errorOrTid = malloc(8) + errorOrTid = wasm64 ? Number(malloc(BigInt(8))) : malloc(8) if (!errorOrTid) { return -48 /* ENOMEM */ } } - const free = this.wasmInstance.exports.free as Function + const _free = this.wasmInstance.exports.free as Function + const free = wasm64 ? (ptr: number) => { _free(BigInt(ptr)) } : _free const struct = new Int32Array(this.wasmMemory.buffer, errorOrTid!, 2) Atomics.store(struct, 0, 0) Atomics.store(struct, 1, 0) - if (this.postMessage) { - const postMessage = this.postMessage + if (postMessage) { postMessage({ __emnapi__: { type: 'spawn-thread', @@ -107,7 +106,7 @@ export class WASIThreads { } let sab: Int32Array | undefined - if (this.waitThreadStart) { + if (waitThreadStart) { sab = new Int32Array(new SharedArrayBuffer(4)) Atomics.store(sab, 0, 0) } @@ -136,7 +135,7 @@ export class WASIThreads { } } }) - if (this.waitThreadStart) { + if (waitThreadStart) { Atomics.wait(sab!, 0, 0) const r = Atomics.load(sab!, 0) if (r === 2) { @@ -150,7 +149,7 @@ export class WASIThreads { Atomics.store(struct, 1, EAGAIN) Atomics.notify(struct, 1) - PThread?._options.err?.(e.message) + PThread?._options.printErr?.(e.message) if (isNewABI) { return 1 } @@ -163,7 +162,7 @@ export class WASIThreads { Atomics.notify(struct, 1) PThread!.runningWorkers.push(worker) - if (!this.waitThreadStart) { + if (!waitThreadStart) { worker.whenLoaded.catch((err: any) => { delete worker.whenLoaded PThread!.cleanThread(worker, tid, true) diff --git a/packages/wasi-threads/src/worker.ts b/packages/wasi-threads/src/worker.ts index bd53ac92..920df7dd 100644 --- a/packages/wasi-threads/src/worker.ts +++ b/packages/wasi-threads/src/worker.ts @@ -16,7 +16,7 @@ export interface OnStartData { /** @public */ export interface HandleOptions { onLoad (data: OnLoadData): WebAssembly.WebAssemblyInstantiatedSource | PromiseLike - postMessage: (message: any) => void + postMessage?: (message: any) => void } /** @public */ @@ -28,15 +28,19 @@ export class MessageHandler { public constructor (options: HandleOptions) { const onLoad = options.onLoad - const postMessage = options.postMessage + const postMsg = typeof options.postMessage === 'function' + ? options.postMessage + : typeof postMessage === 'function' + ? postMessage + : undefined if (typeof onLoad !== 'function') { throw new TypeError('options.onLoad is not a function') } - if (typeof postMessage !== 'function') { + if (typeof postMsg !== 'function') { throw new TypeError('options.postMessage is not a function') } this.onLoad = onLoad - this.postMessage = postMessage + this.postMessage = postMsg this.instance = undefined // this.module = undefined this.messagesBeforeLoad = [] @@ -100,7 +104,10 @@ export class MessageHandler { }) } - private _loaded (err: Error | null, source: WebAssembly.WebAssemblyInstantiatedSource | null, payload: OnLoadData): void { + /** @virtual */ + protected onLoadSuccess (_source: WebAssembly.WebAssemblyInstantiatedSource): void {} + + protected _loaded (err: Error | null, source: WebAssembly.WebAssemblyInstantiatedSource | null, payload: OnLoadData): void { if (err) { notifyPthreadCreateResult(payload.sab, 2) throw err @@ -120,6 +127,8 @@ export class MessageHandler { this.instance = instance + this.onLoadSuccess(source) + const postMessage = this.postMessage! postMessage({ __emnapi__: {