Skip to content

Commit e7665d6

Browse files
committed
new abi of thread-spawn(WebAssembly/wasi-libc#385)
1 parent c6d6968 commit e7665d6

File tree

4 files changed

+73
-46
lines changed

4 files changed

+73
-46
lines changed

packages/core/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export declare interface NapiModule {
6464
}
6565

6666
init (options: InitOptions): any
67-
spawnThread (startArg: number): number
67+
spawnThread (startArg: number, errorOrTid?: number): number
6868
postMessage?: (msg: any) => any
6969
}
7070

packages/core/src/load.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ function loadNapiModuleImpl (loadFn, userNapiModule, wasmInput, options) {
2222
emnapi: napiModule.imports.emnapi,
2323
wasi: {
2424
// eslint-disable-next-line camelcase
25-
'thread-spawn': function __imported_wasi_thread_spawn (startArg) {
26-
return napiModule.spawnThread(startArg, undefined)
25+
'thread-spawn': function __imported_wasi_thread_spawn (startArg, errorOrTid) {
26+
return napiModule.spawnThread(startArg, errorOrTid)
2727
}
2828
}
2929
}
@@ -116,6 +116,13 @@ function loadNapiModuleImpl (loadFn, userNapiModule, wasmInput, options) {
116116
wasi.initialize(instance)
117117
}
118118

119+
napiModule.init({
120+
instance,
121+
module,
122+
memory,
123+
table: instance.exports.__indirect_function_table
124+
})
125+
119126
if (napiModule.childThread) {
120127
const postMessage = napiModule.postMessage
121128
postMessage({
@@ -128,13 +135,6 @@ function loadNapiModuleImpl (loadFn, userNapiModule, wasmInput, options) {
128135
}
129136
})
130137
instance.exports.wasi_thread_start(tid, arg)
131-
} else {
132-
napiModule.init({
133-
instance,
134-
module,
135-
memory,
136-
table: instance.exports.__indirect_function_table
137-
})
138138
}
139139

140140
const ret = { instance, module }

packages/emnapi/src/core/async.ts

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,32 @@ function ptrToString (ptr: number): string {
6969
}
7070

7171
let nextTid = 1
72-
function spawnThread (startArg: number, threadId?: Int32Array): number {
72+
function spawnThread (startArg: number, errorOrTid: number, threadId?: Int32Array): number {
73+
errorOrTid = errorOrTid || 0
7374
if (ENVIRONMENT_IS_PTHREAD) {
74-
const threadIdBuffer = new SharedArrayBuffer(4)
75+
const threadIdBuffer = new SharedArrayBuffer(8)
7576
const id = new Int32Array(threadIdBuffer)
7677
const postMessage = napiModule.postMessage!
7778
postMessage({
7879
__emnapi__: {
7980
type: 'thread-spawn',
8081
payload: {
8182
startArg,
83+
errorOrTid,
8284
threadId: id
8385
}
8486
}
8587
})
86-
Atomics.wait(id, 0, 0)
87-
const tid = Atomics.load(id, 0)
88-
return tid
88+
Atomics.wait(id, 1, 0)
89+
if (errorOrTid) {
90+
const HEAPU32 = new Uint32Array(wasmMemory.buffer, errorOrTid, 2)
91+
const isError = Atomics.load(id, 0)
92+
const result = Atomics.load(id, 1)
93+
Atomics.store(HEAPU32, 0, isError)
94+
Atomics.store(HEAPU32, 1, result < 0 ? -result : result)
95+
return isError
96+
}
97+
return Atomics.load(id, 1)
8998
}
9099

91100
let worker: any
@@ -98,10 +107,17 @@ function spawnThread (startArg: number, threadId?: Int32Array): number {
98107
const EAGAIN = 6
99108
const ret = -EAGAIN
100109
if (threadId) {
101-
Atomics.store(threadId, 0, ret)
102-
Atomics.notify(threadId, 0)
110+
Atomics.store(threadId, 0, 1)
111+
Atomics.store(threadId, 1, ret)
112+
Atomics.notify(threadId, 1)
103113
}
104114
err(err.message)
115+
if (errorOrTid) {
116+
const HEAPU32 = new Uint32Array(wasmMemory.buffer, errorOrTid, 2)
117+
Atomics.store(HEAPU32, 0, 1)
118+
Atomics.store(HEAPU32, 1, EAGAIN)
119+
return 1
120+
}
105121
return ret
106122
}
107123

@@ -117,7 +133,7 @@ function spawnThread (startArg: number, threadId?: Int32Array): number {
117133
err('failed to load in child thread: ' + (payload.err.message || payload.err))
118134
}
119135
} else if (type === 'thread-spawn') {
120-
spawnThread(payload.startArg, payload.threadId)
136+
spawnThread(payload.startArg, payload.errorOrTid, payload.threadId)
121137
}
122138
}
123139
}
@@ -158,10 +174,17 @@ function spawnThread (startArg: number, threadId?: Int32Array): number {
158174
}
159175
}
160176
if (threadId) {
161-
Atomics.store(threadId, 0, tid)
162-
Atomics.notify(threadId, 0)
177+
Atomics.store(threadId, 0, 0)
178+
Atomics.store(threadId, 1, tid)
179+
Atomics.notify(threadId, 1)
163180
}
164181
worker.postMessage(msg)
182+
if (errorOrTid) {
183+
const HEAPU32 = new Uint32Array(wasmMemory.buffer, errorOrTid, 2)
184+
Atomics.store(HEAPU32, 0, 0)
185+
Atomics.store(HEAPU32, 1, tid)
186+
return 0
187+
}
165188
return tid
166189
}
167190
napiModule.spawnThread = spawnThread

packages/emnapi/src/core/init.ts

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ declare interface INapiModule {
3737
envObject?: Env
3838

3939
init (options: InitOptions): any
40-
spawnThread (startArg: number): number
40+
spawnThread (startArg: number, errorOrTid?: number): number
4141
postMessage?: (msg: any) => any
4242
}
4343

@@ -92,32 +92,36 @@ var napiModule: INapiModule = {
9292
wasmModule = module
9393
wasmMemory = memory
9494
wasmTable = table
95-
if (typeof exports.malloc !== 'function') throw new TypeError('malloc is not exported')
96-
if (typeof exports.free !== 'function') throw new TypeError('free is not exported')
97-
_malloc = exports.malloc
98-
_free = exports.free
99-
100-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
101-
const envObject = napiModule.envObject || (napiModule.envObject = emnapiCtx.createEnv(
102-
(cb: Ptr) => $makeDynCall('vppp', 'cb'),
103-
(cb: Ptr) => $makeDynCall('vp', 'cb')
104-
))
105-
106-
const scope = emnapiCtx.openScope(envObject)
107-
try {
108-
envObject.callIntoModule((_envObject) => {
109-
const exports = napiModule.exports
110-
const exportsHandle = scope.add(exports)
111-
const napi_register_wasm_v1 = instance.exports.napi_register_wasm_v1 as Function
112-
const napiValue = napi_register_wasm_v1($to64('_envObject.id'), $to64('exportsHandle.id'))
113-
napiModule.exports = (!napiValue) ? exports : emnapiCtx.handleStore.get(napiValue)!.value
114-
})
115-
} finally {
116-
emnapiCtx.closeScope(envObject, scope)
95+
96+
if (!napiModule.childThread) {
97+
// main thread only
98+
if (typeof exports.malloc !== 'function') throw new TypeError('malloc is not exported')
99+
if (typeof exports.free !== 'function') throw new TypeError('free is not exported')
100+
_malloc = exports.malloc
101+
_free = exports.free
102+
103+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
104+
const envObject = napiModule.envObject || (napiModule.envObject = emnapiCtx.createEnv(
105+
(cb: Ptr) => $makeDynCall('vppp', 'cb'),
106+
(cb: Ptr) => $makeDynCall('vp', 'cb')
107+
))
108+
109+
const scope = emnapiCtx.openScope(envObject)
110+
try {
111+
envObject.callIntoModule((_envObject) => {
112+
const exports = napiModule.exports
113+
const exportsHandle = scope.add(exports)
114+
const napi_register_wasm_v1 = instance.exports.napi_register_wasm_v1 as Function
115+
const napiValue = napi_register_wasm_v1($to64('_envObject.id'), $to64('exportsHandle.id'))
116+
napiModule.exports = (!napiValue) ? exports : emnapiCtx.handleStore.get(napiValue)!.value
117+
})
118+
} finally {
119+
emnapiCtx.closeScope(envObject, scope)
120+
}
121+
napiModule.loaded = true
122+
delete napiModule.envObject
123+
return napiModule.exports
117124
}
118-
napiModule.loaded = true
119-
delete napiModule.envObject
120-
return napiModule.exports
121125
}
122126
}
123127

0 commit comments

Comments
 (0)