Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: separate @emnapi/wasi-threads package #117

Merged
merged 26 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ module.exports = {
'@typescript-eslint/no-unnecessary-type-assertion': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/unbound-method': 'off',
'@typescript-eslint/no-empty-interface': 'off',
'@typescript-eslint/member-delimiter-style': ['error', {
multiline: {
delimiter: 'none',
Expand Down
40 changes: 39 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- test-*
tags:
- v*
- wasi-threads-v*
pull_request:
paths-ignore:
- '**/*.md'
Expand All @@ -25,6 +26,7 @@ env:

jobs:
build:
timeout-minutes: 15
name: Build
runs-on: ubuntu-latest
strategy:
Expand Down Expand Up @@ -93,6 +95,14 @@ jobs:
shell: bash
run: npm run test -w packages/ts-transform-emscripten-esm-library

- name: Test @emnapi/wasi-threads
if: ${{ matrix.target == 'wasm32-wasi-threads' }}
shell: bash
run: |
node ./packages/wasi-threads/test/build.js
npm run test -w packages/wasi-threads
timeout-minutes: 1

# - name: Lint
# run: npm run lint

Expand Down Expand Up @@ -133,7 +143,7 @@ jobs:

release:
name: Release
if: ${{ startsWith(github.event.ref, 'refs/tags') }}
if: ${{ startsWith(github.event.ref, 'refs/tags/v') }}
needs: build
runs-on: ubuntu-latest

Expand Down Expand Up @@ -187,3 +197,31 @@ jobs:
prerelease: false
generate_release_notes: true
files: ./script/emnapi.zip

release-wasi-threads:
name: Release
if: ${{ startsWith(github.event.ref, 'refs/tags/wasi-threads-v') }}
needs: build
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: '20.9.0'
registry-url: 'https://registry.npmjs.org'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: NPM Install
shell: bash
run: |
npm install -g node-gyp
npm install

- name: NPM Build
shell: bash
run: npm run build -w packages/wasi-threads

- name: Publish
run: npm publish --ignore-scripts -w packages/wasi-threads
10 changes: 9 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@
"program": "${file}",
"args": [],
"preLaunchTask": "CMake: build ${input:target}"
}
},
{
"type": "node",
"request": "launch",
"name": "wasi-threads test",
"runtimeArgs": [],
"program": "${workspaceFolder}/packages/wasi-threads/test/index.js",
"args": []
},
],
"inputs": [
{
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"packages/rollup-plugin-emscripten-esm-library",
"packages/runtime",
"packages/node",
"packages/wasi-threads",
"packages/emnapi",
"packages/core",
"packages/test",
Expand Down
4 changes: 2 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
},
"./dist/emnapi-core.min.mjs": {
"types": "./dist/emnapi-core.d.mts",
"import": "./dist/emnapi-core.min.mjs",
"require": null
"default": "./dist/emnapi-core.min.mjs"
}
},
"dependencies": {
"@emnapi/wasi-threads": "1.0.0",
"tslib": "^2.4.0"
},
"scripts": {
Expand Down
12 changes: 6 additions & 6 deletions packages/core/script/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -108,7 +108,7 @@ function build () {
}
},
{
input: createInput(ts.ScriptTarget.ES2019, false, ['tslib']),
input: createInput(ts.ScriptTarget.ES2019, false, ['tslib', '@emnapi/wasi-threads']),
output: {
file: path.join(dist, 'emnapi-core.cjs.js'),
format: 'cjs',
Expand All @@ -118,7 +118,7 @@ function build () {
}
},
{
input: createInput(ts.ScriptTarget.ES2019, true, ['tslib']),
input: createInput(ts.ScriptTarget.ES2019, true, ['tslib', '@emnapi/wasi-threads']),
output: {
file: path.join(dist, 'emnapi-core.cjs.min.js'),
format: 'cjs',
Expand All @@ -128,7 +128,7 @@ function build () {
}
},
{
input: createInput(ts.ScriptTarget.ES2019, false, ['tslib']),
input: createInput(ts.ScriptTarget.ES2019, false, ['tslib', '@emnapi/wasi-threads']),
output: {
file: path.join(dist, 'emnapi-core.mjs'),
format: 'esm',
Expand All @@ -138,7 +138,7 @@ function build () {
}
},
{
input: createInput(ts.ScriptTarget.ES2019, true, ['tslib']),
input: createInput(ts.ScriptTarget.ES2019, true, ['tslib', '@emnapi/wasi-threads']),
output: {
file: path.join(dist, 'emnapi-core.min.mjs'),
format: 'esm',
Expand All @@ -148,7 +148,7 @@ function build () {
}
},
{
input: createInput(ts.ScriptTarget.ES5, false, ['tslib']),
input: createInput(ts.ScriptTarget.ES5, false, ['tslib', '@emnapi/wasi-threads']),
output: {
file: path.join(dist, 'emnapi-core.esm-bundler.js'),
format: 'esm',
Expand Down
10 changes: 7 additions & 3 deletions packages/core/src/emnapi/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Context } from '@emnapi/runtime'
import type { ThreadManager } from '@emnapi/wasi-threads'

/** @public */
export declare interface PointerInfo {
Expand Down Expand Up @@ -34,14 +35,17 @@ export declare interface NapiModule {
len?: number
): T
getMemoryAddress (arrayBufferOrView: ArrayBuffer | ArrayBufferView): PointerInfo
addSendListener (worker: any): boolean
}

init (options: InitOptions): any
spawnThread (startArg: number, errorOrTid?: number): number
startThread (tid: number, startArg: number): void
initWorker (arg: number): void
executeAsyncWork (work: number): void
postMessage?: (msg: any) => any

waitThreadStart: boolean | number
/** @internal */
PThread: ThreadManager
}

/** @public */
Expand Down Expand Up @@ -69,7 +73,7 @@ export declare type BaseCreateOptions = {
nodeBinding?: NodeBinding
reuseWorker?: boolean
asyncWorkPoolSize?: number
waitThreadStart?: boolean
waitThreadStart?: boolean | number
onCreateWorker?: (info: CreateWorkerInfo) => any
print?: (str: string) => void
printErr?: (str: string) => void
Expand Down
9 changes: 5 additions & 4 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@ export type {
export type {
LoadOptions,
InstantiateOptions,
InstantiatedSource,
ReactorWASI
LoadedSource,
InstantiatedSource
} from './load'

export type {
OnLoadData,
HandleOptions
MessageHandlerOptions
} from './worker'

export type {
InputType
} from './util'

export * from '@emnapi/wasi-threads'
104 changes: 36 additions & 68 deletions packages/core/src/load.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import { type WASIInstance, WASIThreads } from '@emnapi/wasi-threads'
import { type InputType, load, loadSync } from './util'
import { createNapiModule } from './emnapi/index'
import type { CreateOptions, NapiModule } from './emnapi/index'

/** @public */
export interface InstantiatedSource extends WebAssembly.WebAssemblyInstantiatedSource {
napiModule: NapiModule
export interface LoadedSource extends WebAssembly.WebAssemblyInstantiatedSource {
usedInstance: WebAssembly.Instance
}

/** @public */
export interface ReactorWASI {
readonly wasiImport?: Record<string, any>
initialize (instance: object): void
getImportObject? (): any
export interface InstantiatedSource extends LoadedSource {
napiModule: NapiModule
}

/** @public */
export interface LoadOptions {
wasi?: ReactorWASI
wasi?: WASIInstance
overwriteImports?: (importObject: WebAssembly.Imports) => WebAssembly.Imports
beforeInit?: (source: WebAssembly.WebAssemblyInstantiatedSource) => void
getMemory?: (exports: WebAssembly.Exports) => WebAssembly.Memory
Expand Down Expand Up @@ -67,25 +66,37 @@ function loadNapiModuleImpl (loadFn: Function, userNapiModule: NapiModule | unde
}

const wasi = options!.wasi
let wasiThreads: WASIThreads | undefined

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) {
wasiThreads = new WASIThreads(
napiModule.childThread
? {
wasi,
childThread: true,
postMessage: napiModule.postMessage!
}
: {
wasi,
threadManager: napiModule.PThread,
waitThreadStart: napiModule.waitThreadStart
}
)

Object.assign(
importObject,
typeof wasi.getImportObject === 'function'
? wasi.getImportObject()
: { wasi_snapshot_preview1: wasi.wasiImport }
)

Object.assign(importObject, wasiThreads.getImportObject())
}

const overwriteImports = options!.overwriteImports
Expand Down Expand Up @@ -124,58 +135,11 @@ function loadNapiModuleImpl (loadFn: Function, userNapiModule: NapiModule | unde
instance = { exports }
}
const module = source.module

if (wasi) {
if (napiModule.childThread) {
// https://github.com/nodejs/help/issues/4102
const createHandler = function (target: WebAssembly.Exports): ProxyHandler<WebAssembly.Exports> {
const handlers = [
'apply',
'construct',
'defineProperty',
'deleteProperty',
'get',
'getOwnPropertyDescriptor',
'getPrototypeOf',
'has',
'isExtensible',
'ownKeys',
'preventExtensions',
'set',
'setPrototypeOf'
]
const handler: ProxyHandler<WebAssembly.Exports> = {}
for (let i = 0; i < handlers.length; i++) {
const name = handlers[i] as keyof ProxyHandler<WebAssembly.Exports>
handler[name] = function () {
const args = Array.prototype.slice.call(arguments, 1)
args.unshift(target)
return (Reflect[name] as any).apply(Reflect, args)
}
}
return handler
}
const handler = createHandler(originalExports)
const noop = (): void => {}
handler.get = function (_target, p, receiver) {
if (p === 'memory') {
return memory
}
if (p === '_initialize') {
return noop
}
return Reflect.get(originalExports, p, receiver)
}
const exportsProxy = new Proxy(Object.create(null), handler)
instance = new Proxy(instance, {
get (target, p, receiver) {
if (p === 'exports') {
return exportsProxy
}
return Reflect.get(target, p, receiver)
}
})
}
wasi.initialize(instance)
instance = wasiThreads!.initialize(instance, module, memory)
} else {
napiModule.PThread.setup(module, memory)
}

if (beforeInit) {
Expand All @@ -192,7 +156,11 @@ function loadNapiModuleImpl (loadFn: Function, userNapiModule: NapiModule | unde
table
})

const ret: any = { instance: originalInstance, module }
const ret: any = {
instance: originalInstance,
module,
usedInstance: instance
}
if (!isLoad) {
ret.napiModule = napiModule
}
Expand Down Expand Up @@ -229,7 +197,7 @@ export function loadNapiModule (
/** Only support `BufferSource` or `WebAssembly.Module` on Node.js */
wasmInput: InputType | Promise<InputType>,
options?: LoadOptions
): Promise<WebAssembly.WebAssemblyInstantiatedSource> {
): Promise<LoadedSource> {
if (typeof napiModule !== 'object' || napiModule === null) {
throw new TypeError('Invalid napiModule')
}
Expand All @@ -241,7 +209,7 @@ export function loadNapiModuleSync (
napiModule: NapiModule,
wasmInput: BufferSource | WebAssembly.Module,
options?: LoadOptions
): WebAssembly.WebAssemblyInstantiatedSource {
): LoadedSource {
if (typeof napiModule !== 'object' || napiModule === null) {
throw new TypeError('Invalid napiModule')
}
Expand Down
Loading
Loading