Skip to content

Commit

Permalink
feat: reuseWorker strict mode (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
toyobayashi authored May 19, 2024
1 parent 7dfde22 commit 696f3e8
Show file tree
Hide file tree
Showing 12 changed files with 272 additions and 83 deletions.
7 changes: 4 additions & 3 deletions packages/core/src/emnapi/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Context } from '@emnapi/runtime'
import type { ThreadManager } from '@emnapi/wasi-threads'
import type { ThreadManager, ThreadManagerOptionsMain, MainThreadBaseOptions } from '@emnapi/wasi-threads'

/** @public */
export declare interface PointerInfo {
Expand Down Expand Up @@ -65,15 +65,16 @@ export declare interface NodeBinding {
/** @public */
export declare interface CreateWorkerInfo {
type: 'thread' | 'async-work'
name: string
}

/** @public */
export declare type BaseCreateOptions = {
filename?: string
nodeBinding?: NodeBinding
reuseWorker?: boolean
reuseWorker?: ThreadManagerOptionsMain['reuseWorker']
asyncWorkPoolSize?: number
waitThreadStart?: boolean | number
waitThreadStart?: MainThreadBaseOptions['waitThreadStart']
onCreateWorker?: (info: CreateWorkerInfo) => any
print?: (str: string) => void
printErr?: (str: string) => void
Expand Down
50 changes: 31 additions & 19 deletions packages/core/src/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,29 +142,41 @@ function loadNapiModuleImpl (loadFn: Function, userNapiModule: NapiModule | unde
napiModule.PThread.setup(module, memory)
}

if (beforeInit) {
beforeInit({
instance: originalInstance,
module
const emnapiInit = (): LoadedSource | InstantiatedSource => {
if (beforeInit) {
beforeInit({
instance: originalInstance,
module
})
}
napiModule.init({
instance,
module,
memory,
table
})
}

napiModule.init({
instance,
module,
memory,
table
})

const ret: any = {
instance: originalInstance,
module,
usedInstance: instance
const ret: LoadedSource | InstantiatedSource = {
instance: originalInstance,
module,
usedInstance: instance
}
if (!isLoad) {
(ret as InstantiatedSource).napiModule = napiModule
}
return ret
}
if (!isLoad) {
ret.napiModule = napiModule

if (napiModule.PThread.shouldPreloadWorkers()) {
const poolReady = napiModule.PThread.loadWasmModuleToAllWorkers()
if (loadFn === loadCallback) {
return poolReady.then(emnapiInit)
} else {
throw new Error('Synchronous loading is not supported with worker pool (reuseWorker.size > 0)')
}
}
return ret

return emnapiInit()
})
}

Expand Down
12 changes: 11 additions & 1 deletion packages/emnapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,17 @@ instantiateNapiModule(input, {
* Reuse the thread worker after thread exit to avoid re-creatation
* @defaultValue false
*/
reuseWorker: true,
reuseWorker: {
/**
* @see {@link https://emscripten.org/docs/tools_reference/settings_reference.html#pthread-pool-size | PTHREAD_POOL_SIZE}
*/
size: 0,

/**
* @see {@link https://emscripten.org/docs/tools_reference/settings_reference.html#pthread-pool-size-strict | PTHREAD_POOL_SIZE_STRICT}
*/
strict: false
},

onCreateWorker () {
return new Worker('./worker.js')
Expand Down
3 changes: 2 additions & 1 deletion packages/emnapi/src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { napiModule } from 'emnapi:shared'
import { napiModule, PThread } from 'emnapi:shared'

import * as asyncMod from './async'
import * as memoryMod from './memory'
Expand Down Expand Up @@ -41,6 +41,7 @@ emnapiAWST.init()
emnapiExternalMemory.init()
emnapiString.init()
emnapiTSFN.init()
PThread.init()

napiModule.emnapi.syncMemory = emnapiMod.$emnapiSyncMemory
napiModule.emnapi.getMemoryAddress = emnapiMod.$emnapiGetMemoryAddress
Expand Down
28 changes: 16 additions & 12 deletions packages/emnapi/src/core/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ declare const process: any

export var ENVIRONMENT_IS_NODE = typeof process === 'object' && process !== null && typeof process.versions === 'object' && process.versions !== null && typeof process.versions.node === 'string'
export var ENVIRONMENT_IS_PTHREAD = Boolean(options.childThread)
export var reuseWorker = Boolean(options.reuseWorker)
export var waitThreadStart = typeof options.waitThreadStart === 'number' ? options.waitThreadStart : Boolean(options.waitThreadStart)

export var wasmInstance: WebAssembly.Instance
Expand Down Expand Up @@ -69,7 +68,7 @@ export var napiModule: INapiModule = {
emnapi: {},
loaded: false,
filename: '',
childThread: Boolean(options.childThread),
childThread: ENVIRONMENT_IS_PTHREAD,

initWorker: undefined!,
executeAsyncWork: undefined!,
Expand Down Expand Up @@ -244,15 +243,20 @@ function emnapiAddSendListener (worker: any): boolean {

napiModule.emnapi.addSendListener = emnapiAddSendListener

export var PThread = new ThreadManager({
printErr: err,
beforeLoad: (worker) => {
emnapiAddSendListener(worker)
},
reuseWorker,
onCreateWorker: onCreateWorker as ThreadManagerOptions['onCreateWorker'] ?? (() => {
throw new Error('options.onCreateWorker` is not provided')
})
})
export var PThread = new ThreadManager(
ENVIRONMENT_IS_PTHREAD
? {
printErr: err,
childThread: true
}
: {
printErr: err,
beforeLoad: (worker) => {
emnapiAddSendListener(worker)
},
reuseWorker: options.reuseWorker,
onCreateWorker: onCreateWorker as ThreadManagerOptionsMain['onCreateWorker']
}
)

napiModule.PThread = PThread
7 changes: 4 additions & 3 deletions packages/emnapi/src/core/scope.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ declare interface CreateOptions {
filename?: string
nodeBinding?: NodeBinding
childThread?: boolean
reuseWorker?: boolean
reuseWorker?: ThreadManagerOptionsMain['reuseWorker']
asyncWorkPoolSize?: number
waitThreadStart?: boolean | number
waitThreadStart?: MainThreadBaseOptions['waitThreadStart']
onCreateWorker?: () => any
print?: (str: string) => void
printErr?: (str: string) => void
Expand All @@ -15,7 +15,8 @@ declare interface CreateOptions {
// factory parameter
declare const options: CreateOptions

declare type ThreadManagerOptions = import('../../../wasi-threads/lib/typings/index').ThreadManagerOptions
declare type MainThreadBaseOptions = import('../../../wasi-threads/lib/typings/index').MainThreadBaseOptions
declare type ThreadManagerOptionsMain = import('../../../wasi-threads/lib/typings/index').ThreadManagerOptionsMain
declare const ThreadManager: typeof import('../../../wasi-threads/lib/typings/index').ThreadManager
// eslint-disable-next-line @typescript-eslint/no-redeclare
declare type ThreadManager = import('../../../wasi-threads/lib/typings/index').ThreadManager
26 changes: 17 additions & 9 deletions packages/test/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,26 @@ function loadPath (request, options) {
})
const napiModule = createNapiModule({
context,
filename: request,
asyncWorkPoolSize: process.env.EMNAPI_TEST_WASI_THREADS
? RUNTIME_UV_THREADPOOL_SIZE
: -RUNTIME_UV_THREADPOOL_SIZE,
filename: request,
reuseWorker: true,
waitThreadStart: 1000,
onCreateWorker () {
return new Worker(join(__dirname, './worker.js'), {
env: process.env,
execArgv: ['--experimental-wasi-unstable-preview1']
})
},
...(process.env.EMNAPI_TEST_WASI_THREADS
? {
reuseWorker: {
size: RUNTIME_UV_THREADPOOL_SIZE * 4,
strict: true
},
waitThreadStart: 1000,
onCreateWorker () {
return new Worker(join(__dirname, './worker.js'), {
env: process.env,
execArgv: ['--experimental-wasi-unstable-preview1']
})
}
}
: {}
),
...(options || {})
})

Expand Down
12 changes: 7 additions & 5 deletions packages/wasi-threads/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ This package makes [wasi-threads proposal](https://github.com/WebAssembly/wasi-t
const { WASI } = require('wasi')
const Worker = require('worker_threads')
const { WASIThreads } = require('@emnapi/wasi-threads')

const wasi = new WASI({
version: 'preview1'
})
Expand Down Expand Up @@ -70,12 +70,14 @@ This package makes [wasi-threads proposal](https://github.com/WebAssembly/wasi-t
wasi_snapshot_preview1: wasi.wasiImport,
...wasiThreads.getImportObject()
})


wasiThreads.setup(instance, module, memory)
await wasiThreads.preloadWorkers()

if (typeof instance.exports._start === 'function') {
const { exitCode } = wasiThreads.start(instance, module, memory)
return exitCode
return wasi.start(instance)
} else {
instance = wasiThreads.initialize(instance, module, memory)
wasi.initialize(instance)
// instance.exports.exported_wasm_function()
}
})
Expand Down
13 changes: 11 additions & 2 deletions packages/wasi-threads/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
export type { ThreadManagerOptions, WorkerLike, WorkerMessageEvent, WorkerFactory } from './thread-manager'
export type {
ReuseWorkerOptions,
ThreadManagerOptionsBase,
ThreadManagerOptionsMain,
ThreadManagerOptionsChild,
ThreadManagerOptions,
WorkerLike,
WorkerMessageEvent,
WorkerFactory
} from './thread-manager'
export { ThreadManager } from './thread-manager'

export type {
Expand All @@ -20,6 +29,6 @@ export type { ThreadMessageHandlerOptions } from './worker'

export { createInstanceProxy } from './proxy'

export { isTrapError } from './util'
export { isTrapError, isSharedArrayBuffer } from './util'

export type { LoadPayload } from './command'
Loading

0 comments on commit 696f3e8

Please sign in to comment.