Skip to content

Commit

Permalink
new abi of thread-spawn(WebAssembly/wasi-libc#385)
Browse files Browse the repository at this point in the history
  • Loading branch information
toyobayashi committed Mar 1, 2023
1 parent c6d6968 commit e7665d6
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 46 deletions.
2 changes: 1 addition & 1 deletion packages/core/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export declare interface NapiModule {
}

init (options: InitOptions): any
spawnThread (startArg: number): number
spawnThread (startArg: number, errorOrTid?: number): number
postMessage?: (msg: any) => any
}

Expand Down
18 changes: 9 additions & 9 deletions packages/core/src/load.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ function loadNapiModuleImpl (loadFn, userNapiModule, wasmInput, options) {
emnapi: napiModule.imports.emnapi,
wasi: {
// eslint-disable-next-line camelcase
'thread-spawn': function __imported_wasi_thread_spawn (startArg) {
return napiModule.spawnThread(startArg, undefined)
'thread-spawn': function __imported_wasi_thread_spawn (startArg, errorOrTid) {
return napiModule.spawnThread(startArg, errorOrTid)
}
}
}
Expand Down Expand Up @@ -116,6 +116,13 @@ function loadNapiModuleImpl (loadFn, userNapiModule, wasmInput, options) {
wasi.initialize(instance)
}

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

if (napiModule.childThread) {
const postMessage = napiModule.postMessage
postMessage({
Expand All @@ -128,13 +135,6 @@ function loadNapiModuleImpl (loadFn, userNapiModule, wasmInput, options) {
}
})
instance.exports.wasi_thread_start(tid, arg)
} else {
napiModule.init({
instance,
module,
memory,
table: instance.exports.__indirect_function_table
})
}

const ret = { instance, module }
Expand Down
43 changes: 33 additions & 10 deletions packages/emnapi/src/core/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,23 +69,32 @@ function ptrToString (ptr: number): string {
}

let nextTid = 1
function spawnThread (startArg: number, threadId?: Int32Array): number {
function spawnThread (startArg: number, errorOrTid: number, threadId?: Int32Array): number {
errorOrTid = errorOrTid || 0
if (ENVIRONMENT_IS_PTHREAD) {
const threadIdBuffer = new SharedArrayBuffer(4)
const threadIdBuffer = new SharedArrayBuffer(8)
const id = new Int32Array(threadIdBuffer)
const postMessage = napiModule.postMessage!
postMessage({
__emnapi__: {
type: 'thread-spawn',
payload: {
startArg,
errorOrTid,
threadId: id
}
}
})
Atomics.wait(id, 0, 0)
const tid = Atomics.load(id, 0)
return tid
Atomics.wait(id, 1, 0)
if (errorOrTid) {
const HEAPU32 = new Uint32Array(wasmMemory.buffer, errorOrTid, 2)
const isError = Atomics.load(id, 0)
const result = Atomics.load(id, 1)
Atomics.store(HEAPU32, 0, isError)
Atomics.store(HEAPU32, 1, result < 0 ? -result : result)
return isError
}
return Atomics.load(id, 1)
}

let worker: any
Expand All @@ -98,10 +107,17 @@ function spawnThread (startArg: number, threadId?: Int32Array): number {
const EAGAIN = 6
const ret = -EAGAIN
if (threadId) {
Atomics.store(threadId, 0, ret)
Atomics.notify(threadId, 0)
Atomics.store(threadId, 0, 1)
Atomics.store(threadId, 1, ret)
Atomics.notify(threadId, 1)
}
err(err.message)
if (errorOrTid) {
const HEAPU32 = new Uint32Array(wasmMemory.buffer, errorOrTid, 2)
Atomics.store(HEAPU32, 0, 1)
Atomics.store(HEAPU32, 1, EAGAIN)
return 1
}
return ret
}

Expand All @@ -117,7 +133,7 @@ function spawnThread (startArg: number, threadId?: Int32Array): number {
err('failed to load in child thread: ' + (payload.err.message || payload.err))
}
} else if (type === 'thread-spawn') {
spawnThread(payload.startArg, payload.threadId)
spawnThread(payload.startArg, payload.errorOrTid, payload.threadId)
}
}
}
Expand Down Expand Up @@ -158,10 +174,17 @@ function spawnThread (startArg: number, threadId?: Int32Array): number {
}
}
if (threadId) {
Atomics.store(threadId, 0, tid)
Atomics.notify(threadId, 0)
Atomics.store(threadId, 0, 0)
Atomics.store(threadId, 1, tid)
Atomics.notify(threadId, 1)
}
worker.postMessage(msg)
if (errorOrTid) {
const HEAPU32 = new Uint32Array(wasmMemory.buffer, errorOrTid, 2)
Atomics.store(HEAPU32, 0, 0)
Atomics.store(HEAPU32, 1, tid)
return 0
}
return tid
}
napiModule.spawnThread = spawnThread
Expand Down
56 changes: 30 additions & 26 deletions packages/emnapi/src/core/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ declare interface INapiModule {
envObject?: Env

init (options: InitOptions): any
spawnThread (startArg: number): number
spawnThread (startArg: number, errorOrTid?: number): number
postMessage?: (msg: any) => any
}

Expand Down Expand Up @@ -92,32 +92,36 @@ var napiModule: INapiModule = {
wasmModule = module
wasmMemory = memory
wasmTable = table
if (typeof exports.malloc !== 'function') throw new TypeError('malloc is not exported')
if (typeof exports.free !== 'function') throw new TypeError('free is not exported')
_malloc = exports.malloc
_free = exports.free

// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const envObject = napiModule.envObject || (napiModule.envObject = emnapiCtx.createEnv(
(cb: Ptr) => $makeDynCall('vppp', 'cb'),
(cb: Ptr) => $makeDynCall('vp', 'cb')
))

const scope = emnapiCtx.openScope(envObject)
try {
envObject.callIntoModule((_envObject) => {
const exports = napiModule.exports
const exportsHandle = scope.add(exports)
const napi_register_wasm_v1 = instance.exports.napi_register_wasm_v1 as Function
const napiValue = napi_register_wasm_v1($to64('_envObject.id'), $to64('exportsHandle.id'))
napiModule.exports = (!napiValue) ? exports : emnapiCtx.handleStore.get(napiValue)!.value
})
} finally {
emnapiCtx.closeScope(envObject, scope)

if (!napiModule.childThread) {
// main thread only
if (typeof exports.malloc !== 'function') throw new TypeError('malloc is not exported')
if (typeof exports.free !== 'function') throw new TypeError('free is not exported')
_malloc = exports.malloc
_free = exports.free

// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const envObject = napiModule.envObject || (napiModule.envObject = emnapiCtx.createEnv(
(cb: Ptr) => $makeDynCall('vppp', 'cb'),
(cb: Ptr) => $makeDynCall('vp', 'cb')
))

const scope = emnapiCtx.openScope(envObject)
try {
envObject.callIntoModule((_envObject) => {
const exports = napiModule.exports
const exportsHandle = scope.add(exports)
const napi_register_wasm_v1 = instance.exports.napi_register_wasm_v1 as Function
const napiValue = napi_register_wasm_v1($to64('_envObject.id'), $to64('exportsHandle.id'))
napiModule.exports = (!napiValue) ? exports : emnapiCtx.handleStore.get(napiValue)!.value
})
} finally {
emnapiCtx.closeScope(envObject, scope)
}
napiModule.loaded = true
delete napiModule.envObject
return napiModule.exports
}
napiModule.loaded = true
delete napiModule.envObject
return napiModule.exports
}
}

Expand Down

0 comments on commit e7665d6

Please sign in to comment.