init project
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-present Toyobayashi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+1
@@ -0,0 +1 @@
|
||||
See [https://github.com/toyobayashi/emnapi](https://github.com/toyobayashi/emnapi)
|
||||
+366
@@ -0,0 +1,366 @@
|
||||
import type { Context } from '@emnapi/runtime';
|
||||
import type { ReferenceOwnership } from '@emnapi/runtime';
|
||||
import type { Worker as Worker_2 } from 'worker_threads';
|
||||
|
||||
/** @public */
|
||||
export declare type BaseCreateOptions = {
|
||||
filename?: string
|
||||
nodeBinding?: NodeBinding
|
||||
reuseWorker?: ThreadManagerOptionsMain['reuseWorker']
|
||||
asyncWorkPoolSize?: number
|
||||
waitThreadStart?: MainThreadBaseOptions['waitThreadStart']
|
||||
onCreateWorker?: (info: CreateWorkerInfo) => any
|
||||
print?: (str: string) => void
|
||||
printErr?: (str: string) => void
|
||||
postMessage?: (msg: any) => any
|
||||
plugins?: (PluginFactory | EmnapiPlugin)[]
|
||||
}
|
||||
|
||||
export declare interface BaseOptions {
|
||||
wasi: WASIInstance;
|
||||
version?: 'preview1';
|
||||
wasm64?: boolean;
|
||||
}
|
||||
|
||||
export declare interface ChildThreadOptions extends BaseOptions {
|
||||
childThread: true;
|
||||
postMessage?: (data: any) => void;
|
||||
}
|
||||
|
||||
export declare interface CleanupThreadPayload {
|
||||
tid: number;
|
||||
}
|
||||
|
||||
export declare interface CommandInfo<T extends CommandType> {
|
||||
type: T;
|
||||
payload: CommandPayloadMap[T];
|
||||
}
|
||||
|
||||
export declare interface CommandPayloadMap {
|
||||
load: LoadPayload;
|
||||
loaded: LoadedPayload;
|
||||
start: StartPayload;
|
||||
'cleanup-thread': CleanupThreadPayload;
|
||||
'terminate-all-threads': TerminateAllThreadsPayload;
|
||||
'spawn-thread': SpawnThreadPayload;
|
||||
}
|
||||
|
||||
export declare type CommandType = keyof CommandPayloadMap;
|
||||
|
||||
export declare function createInstanceProxy(instance: WebAssembly.Instance, memory?: WebAssembly.Memory | (() => WebAssembly.Memory)): WebAssembly.Instance;
|
||||
|
||||
/** @public */
|
||||
export declare function createNapiModule (
|
||||
options: CreateOptions
|
||||
): NapiModule
|
||||
|
||||
/** @public */
|
||||
export declare type CreateOptions = BaseCreateOptions & ({
|
||||
context: Context
|
||||
childThread?: boolean
|
||||
} | {
|
||||
context?: Context
|
||||
childThread: true
|
||||
})
|
||||
|
||||
/** @public */
|
||||
export declare interface CreateWorkerInfo {
|
||||
type: 'thread' | 'async-work'
|
||||
name: string
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export declare interface EmnapiPlugin {
|
||||
importObject?: (originalImports: WebAssembly.Imports) => (WebAssembly.Imports | void)
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export declare interface InitOptions {
|
||||
instance: WebAssembly.Instance
|
||||
module: WebAssembly.Module
|
||||
memory?: WebAssembly.Memory
|
||||
table?: WebAssembly.Table
|
||||
}
|
||||
|
||||
export declare type InputType = string | URL | Response | BufferSource | WebAssembly.Module;
|
||||
|
||||
export declare interface InstantiatedSource extends LoadedSource {
|
||||
napiModule: NapiModule;
|
||||
}
|
||||
|
||||
export declare function instantiateNapiModule(wasmInput: InputType | Promise<InputType>, options: InstantiateOptions): Promise<InstantiatedSource>;
|
||||
|
||||
export declare function instantiateNapiModuleSync(wasmInput: BufferSource | WebAssembly.Module, options: InstantiateOptions): InstantiatedSource;
|
||||
|
||||
export declare type InstantiateOptions = CreateOptions & LoadOptions;
|
||||
|
||||
export declare function isSharedArrayBuffer(value: any): value is SharedArrayBuffer;
|
||||
|
||||
export declare function isTrapError(e: Error): e is WebAssembly.RuntimeError;
|
||||
|
||||
export declare interface LoadedPayload {
|
||||
}
|
||||
|
||||
export declare interface LoadedSource extends WebAssembly.WebAssemblyInstantiatedSource {
|
||||
usedInstance: WebAssembly.Instance;
|
||||
}
|
||||
|
||||
export declare function loadNapiModule(napiModule: NapiModule, wasmInput: InputType | Promise<InputType>, options?: LoadOptions): Promise<LoadedSource>;
|
||||
|
||||
export declare function loadNapiModuleSync(napiModule: NapiModule, wasmInput: BufferSource | WebAssembly.Module, options?: LoadOptions): LoadedSource;
|
||||
|
||||
export declare interface LoadOptions {
|
||||
wasi?: WASIInstance;
|
||||
overwriteImports?: (importObject: WebAssembly.Imports) => WebAssembly.Imports;
|
||||
beforeInit?: (source: WebAssembly.WebAssemblyInstantiatedSource) => void;
|
||||
getMemory?: (exports: WebAssembly.Exports) => WebAssembly.Memory;
|
||||
getTable?: (exports: WebAssembly.Exports) => WebAssembly.Table;
|
||||
}
|
||||
|
||||
export declare interface LoadPayload {
|
||||
wasmModule: WebAssembly.Module;
|
||||
wasmMemory: WebAssembly.Memory;
|
||||
sab?: Int32Array;
|
||||
}
|
||||
|
||||
export declare interface MainThreadBaseOptions extends BaseOptions {
|
||||
waitThreadStart?: boolean | number;
|
||||
}
|
||||
|
||||
export declare type MainThreadOptions = MainThreadOptionsWithThreadManager | MainThreadOptionsCreateThreadManager;
|
||||
|
||||
export declare interface MainThreadOptionsCreateThreadManager extends MainThreadBaseOptions, ThreadManagerOptionsMain {
|
||||
}
|
||||
|
||||
export declare interface MainThreadOptionsWithThreadManager extends MainThreadBaseOptions {
|
||||
threadManager?: ThreadManager | (() => ThreadManager);
|
||||
}
|
||||
|
||||
export declare interface MessageEventData<T extends CommandType> {
|
||||
__emnapi__: CommandInfo<T>;
|
||||
}
|
||||
|
||||
export declare class MessageHandler extends ThreadMessageHandler {
|
||||
napiModule: NapiModule | undefined;
|
||||
constructor(options: MessageHandlerOptions);
|
||||
instantiate(data: LoadPayload): InstantiatedSource | PromiseLike<InstantiatedSource>;
|
||||
handle(e: WorkerMessageEvent): void;
|
||||
}
|
||||
|
||||
export declare interface MessageHandlerOptions extends ThreadMessageHandlerOptions {
|
||||
onLoad: (data: LoadPayload) => InstantiatedSource | PromiseLike<InstantiatedSource>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export declare interface NapiModule {
|
||||
imports: {
|
||||
env: any
|
||||
napi: any
|
||||
emnapi: any
|
||||
}
|
||||
exports: any
|
||||
loaded: boolean
|
||||
filename: string
|
||||
childThread: boolean
|
||||
emnapi: {
|
||||
/**
|
||||
* Synchronize data between the wasm memory and an ArrayBuffer / view.
|
||||
*
|
||||
* Note: when a non-shared wasm memory has grown, a view over the
|
||||
* detached old buffer is re-created over the current buffer through the
|
||||
* intrinsic base-class constructor, so for view inputs the returned view
|
||||
* may be a base-class instance (e.g. `Uint8Array` or `DataView`)
|
||||
* instead of the subclass that was passed in.
|
||||
*/
|
||||
syncMemory<T extends ArrayBuffer | ArrayBufferView> (
|
||||
js_to_wasm: boolean,
|
||||
arrayBufferOrView: T,
|
||||
offset?: number,
|
||||
len?: number
|
||||
): T extends ArrayBufferView ? ArrayBufferView : T
|
||||
getMemoryAddress (arrayBufferOrView: ArrayBuffer | ArrayBufferView): PointerInfo
|
||||
addSendListener (worker: any): boolean
|
||||
}
|
||||
|
||||
init (options: InitOptions): any
|
||||
initWorker (arg: number, func: [number, number]): void
|
||||
postMessage?: (msg: any) => any
|
||||
|
||||
waitThreadStart: boolean | number
|
||||
/* Excluded from this release type: PThread */}
|
||||
|
||||
/** @public */
|
||||
export declare interface NodeBinding {
|
||||
node: {
|
||||
emitAsyncInit: Function
|
||||
emitAsyncDestroy: Function
|
||||
makeCallback: Function
|
||||
}
|
||||
napi: {
|
||||
asyncInit: Function
|
||||
asyncDestroy: Function
|
||||
makeCallback: Function
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export declare type PluginContext = any
|
||||
|
||||
/** @public */
|
||||
export declare type PluginFactory = (ctx: PluginContext) => EmnapiPlugin
|
||||
|
||||
/** @public */
|
||||
export declare interface PointerInfo {
|
||||
address: number
|
||||
ownership: ReferenceOwnership
|
||||
runtimeAllocated: 0 | 1
|
||||
}
|
||||
|
||||
export declare interface ReuseWorkerOptions {
|
||||
size: number;
|
||||
strict?: boolean;
|
||||
}
|
||||
|
||||
export declare interface SpawnThreadPayload {
|
||||
startArg: number;
|
||||
errorOrTid: number;
|
||||
}
|
||||
|
||||
export declare interface StartPayload {
|
||||
tid: number;
|
||||
arg: number;
|
||||
sab?: Int32Array;
|
||||
}
|
||||
|
||||
export declare interface StartResult {
|
||||
exitCode: number;
|
||||
instance: WebAssembly.Instance;
|
||||
}
|
||||
|
||||
export declare interface TerminateAllThreadsPayload {
|
||||
}
|
||||
|
||||
export declare class ThreadManager {
|
||||
unusedWorkers: WorkerLike[];
|
||||
pthreads: Record<number, WorkerLike>;
|
||||
get nextWorkerID(): number;
|
||||
wasmModule: WebAssembly.Module | null;
|
||||
wasmMemory: WebAssembly.Memory | null;
|
||||
private readonly messageEvents;
|
||||
private readonly _childThread;
|
||||
private readonly _onCreateWorker;
|
||||
private readonly _reuseWorker;
|
||||
private readonly _beforeLoad?;
|
||||
readonly printErr: (message: string) => void;
|
||||
threadSpawn?: ((startArg: number, errorOrTid?: number) => number);
|
||||
constructor(options: ThreadManagerOptions);
|
||||
init(): void;
|
||||
initMainThread(): void;
|
||||
private preparePool;
|
||||
shouldPreloadWorkers(): boolean;
|
||||
loadWasmModuleToAllWorkers(): Promise<WorkerLike[]>;
|
||||
preloadWorkers(): Promise<WorkerLike[]>;
|
||||
setup(wasmModule: WebAssembly.Module, wasmMemory: WebAssembly.Memory): void;
|
||||
markId(worker: WorkerLike): number;
|
||||
returnWorkerToPool(worker: WorkerLike): void;
|
||||
loadWasmModuleToWorker(worker: WorkerLike, sab?: Int32Array): Promise<WorkerLike>;
|
||||
allocateUnusedWorker(): WorkerLike;
|
||||
getNewWorker(sab?: Int32Array): WorkerLike | undefined;
|
||||
cleanThread(worker: WorkerLike, tid: number, force?: boolean): void;
|
||||
terminateWorker(worker: WorkerLike): void;
|
||||
terminateAllThreads(): void;
|
||||
addMessageEventListener(worker: WorkerLike, onMessage: (e: WorkerMessageEvent) => void): () => void;
|
||||
fireMessageEvent(worker: WorkerLike, e: WorkerMessageEvent): void;
|
||||
}
|
||||
|
||||
export declare type ThreadManagerOptions = ThreadManagerOptionsMain | ThreadManagerOptionsChild;
|
||||
|
||||
export declare interface ThreadManagerOptionsBase {
|
||||
printErr?: (message: string) => void;
|
||||
threadSpawn?: (startArg: number, errorOrTid?: number) => number;
|
||||
}
|
||||
|
||||
export declare interface ThreadManagerOptionsChild extends ThreadManagerOptionsBase {
|
||||
childThread: true;
|
||||
}
|
||||
|
||||
export declare interface ThreadManagerOptionsMain extends ThreadManagerOptionsBase {
|
||||
beforeLoad?: (worker: WorkerLike) => any;
|
||||
reuseWorker?: boolean | number | ReuseWorkerOptions;
|
||||
onCreateWorker: WorkerFactory;
|
||||
childThread?: false;
|
||||
}
|
||||
|
||||
export declare class ThreadMessageHandler {
|
||||
protected instance: WebAssembly.Instance | undefined;
|
||||
private messagesBeforeLoad;
|
||||
protected postMessage: (message: any) => void;
|
||||
protected onLoad?: (data: LoadPayload) => WebAssembly.WebAssemblyInstantiatedSource | PromiseLike<WebAssembly.WebAssemblyInstantiatedSource>;
|
||||
protected onError: (error: Error, type: WorkerMessageType) => void;
|
||||
constructor(options?: ThreadMessageHandlerOptions);
|
||||
instantiate(data: LoadPayload): WebAssembly.WebAssemblyInstantiatedSource | PromiseLike<WebAssembly.WebAssemblyInstantiatedSource>;
|
||||
handle(e: WorkerMessageEvent<MessageEventData<WorkerMessageType>>): void;
|
||||
private _load;
|
||||
private _start;
|
||||
protected _loaded(err: Error | null, source: WebAssembly.WebAssemblyInstantiatedSource | null, payload: LoadPayload): void;
|
||||
protected handleAfterLoad<E extends WorkerMessageEvent>(e: E, f: (e: E) => void): void;
|
||||
}
|
||||
|
||||
export declare interface ThreadMessageHandlerOptions {
|
||||
onLoad?: (data: LoadPayload) => WebAssembly.WebAssemblyInstantiatedSource | PromiseLike<WebAssembly.WebAssemblyInstantiatedSource>;
|
||||
onError?: (error: Error, type: WorkerMessageType) => void;
|
||||
postMessage?: (message: any) => void;
|
||||
}
|
||||
|
||||
export declare const version: string;
|
||||
|
||||
export declare interface WASIInstance {
|
||||
readonly wasiImport?: Record<string, any>;
|
||||
initialize(instance: object): void;
|
||||
start(instance: object): number;
|
||||
getImportObject?(): any;
|
||||
}
|
||||
|
||||
export declare class WASIThreads {
|
||||
PThread: ThreadManager | undefined;
|
||||
private wasmMemory;
|
||||
private wasmInstance;
|
||||
private readonly threadSpawn;
|
||||
readonly childThread: boolean;
|
||||
private readonly postMessage;
|
||||
readonly wasi: WASIInstance;
|
||||
constructor(options: WASIThreadsOptions);
|
||||
getImportObject(): {
|
||||
wasi: WASIThreadsImports;
|
||||
};
|
||||
setup(wasmInstance: WebAssembly.Instance, wasmModule: WebAssembly.Module, wasmMemory?: WebAssembly.Memory): void;
|
||||
preloadWorkers(): Promise<WorkerLike[]>;
|
||||
initialize(instance: WebAssembly.Instance, module: WebAssembly.Module, memory?: WebAssembly.Memory): WebAssembly.Instance;
|
||||
start(instance: WebAssembly.Instance, module: WebAssembly.Module, memory?: WebAssembly.Memory): StartResult;
|
||||
terminateAllThreads(): void;
|
||||
}
|
||||
|
||||
export declare interface WASIThreadsImports {
|
||||
'thread-spawn': (startArg: number, errorOrTid?: number) => number;
|
||||
}
|
||||
|
||||
export declare type WASIThreadsOptions = MainThreadOptions | ChildThreadOptions;
|
||||
|
||||
export declare type WorkerFactory = (ctx: {
|
||||
type: string;
|
||||
name: string;
|
||||
}) => WorkerLike;
|
||||
|
||||
export declare type WorkerLike = (Worker | Worker_2) & {
|
||||
whenLoaded?: Promise<WorkerLike>;
|
||||
loaded?: boolean;
|
||||
__emnapi_tid?: number;
|
||||
};
|
||||
|
||||
export declare interface WorkerMessageEvent<T = any> {
|
||||
data: T;
|
||||
}
|
||||
|
||||
export declare type WorkerMessageType = 'load' | 'start';
|
||||
|
||||
export { }
|
||||
+366
@@ -0,0 +1,366 @@
|
||||
import type { Context } from '@emnapi/runtime';
|
||||
import type { ReferenceOwnership } from '@emnapi/runtime';
|
||||
import type { Worker as Worker_2 } from 'worker_threads';
|
||||
|
||||
/** @public */
|
||||
export declare type BaseCreateOptions = {
|
||||
filename?: string
|
||||
nodeBinding?: NodeBinding
|
||||
reuseWorker?: ThreadManagerOptionsMain['reuseWorker']
|
||||
asyncWorkPoolSize?: number
|
||||
waitThreadStart?: MainThreadBaseOptions['waitThreadStart']
|
||||
onCreateWorker?: (info: CreateWorkerInfo) => any
|
||||
print?: (str: string) => void
|
||||
printErr?: (str: string) => void
|
||||
postMessage?: (msg: any) => any
|
||||
plugins?: (PluginFactory | EmnapiPlugin)[]
|
||||
}
|
||||
|
||||
export declare interface BaseOptions {
|
||||
wasi: WASIInstance;
|
||||
version?: 'preview1';
|
||||
wasm64?: boolean;
|
||||
}
|
||||
|
||||
export declare interface ChildThreadOptions extends BaseOptions {
|
||||
childThread: true;
|
||||
postMessage?: (data: any) => void;
|
||||
}
|
||||
|
||||
export declare interface CleanupThreadPayload {
|
||||
tid: number;
|
||||
}
|
||||
|
||||
export declare interface CommandInfo<T extends CommandType> {
|
||||
type: T;
|
||||
payload: CommandPayloadMap[T];
|
||||
}
|
||||
|
||||
export declare interface CommandPayloadMap {
|
||||
load: LoadPayload;
|
||||
loaded: LoadedPayload;
|
||||
start: StartPayload;
|
||||
'cleanup-thread': CleanupThreadPayload;
|
||||
'terminate-all-threads': TerminateAllThreadsPayload;
|
||||
'spawn-thread': SpawnThreadPayload;
|
||||
}
|
||||
|
||||
export declare type CommandType = keyof CommandPayloadMap;
|
||||
|
||||
export declare function createInstanceProxy(instance: WebAssembly.Instance, memory?: WebAssembly.Memory | (() => WebAssembly.Memory)): WebAssembly.Instance;
|
||||
|
||||
/** @public */
|
||||
export declare function createNapiModule (
|
||||
options: CreateOptions
|
||||
): NapiModule
|
||||
|
||||
/** @public */
|
||||
export declare type CreateOptions = BaseCreateOptions & ({
|
||||
context: Context
|
||||
childThread?: boolean
|
||||
} | {
|
||||
context?: Context
|
||||
childThread: true
|
||||
})
|
||||
|
||||
/** @public */
|
||||
export declare interface CreateWorkerInfo {
|
||||
type: 'thread' | 'async-work'
|
||||
name: string
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export declare interface EmnapiPlugin {
|
||||
importObject?: (originalImports: WebAssembly.Imports) => (WebAssembly.Imports | void)
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export declare interface InitOptions {
|
||||
instance: WebAssembly.Instance
|
||||
module: WebAssembly.Module
|
||||
memory?: WebAssembly.Memory
|
||||
table?: WebAssembly.Table
|
||||
}
|
||||
|
||||
export declare type InputType = string | URL | Response | BufferSource | WebAssembly.Module;
|
||||
|
||||
export declare interface InstantiatedSource extends LoadedSource {
|
||||
napiModule: NapiModule;
|
||||
}
|
||||
|
||||
export declare function instantiateNapiModule(wasmInput: InputType | Promise<InputType>, options: InstantiateOptions): Promise<InstantiatedSource>;
|
||||
|
||||
export declare function instantiateNapiModuleSync(wasmInput: BufferSource | WebAssembly.Module, options: InstantiateOptions): InstantiatedSource;
|
||||
|
||||
export declare type InstantiateOptions = CreateOptions & LoadOptions;
|
||||
|
||||
export declare function isSharedArrayBuffer(value: any): value is SharedArrayBuffer;
|
||||
|
||||
export declare function isTrapError(e: Error): e is WebAssembly.RuntimeError;
|
||||
|
||||
export declare interface LoadedPayload {
|
||||
}
|
||||
|
||||
export declare interface LoadedSource extends WebAssembly.WebAssemblyInstantiatedSource {
|
||||
usedInstance: WebAssembly.Instance;
|
||||
}
|
||||
|
||||
export declare function loadNapiModule(napiModule: NapiModule, wasmInput: InputType | Promise<InputType>, options?: LoadOptions): Promise<LoadedSource>;
|
||||
|
||||
export declare function loadNapiModuleSync(napiModule: NapiModule, wasmInput: BufferSource | WebAssembly.Module, options?: LoadOptions): LoadedSource;
|
||||
|
||||
export declare interface LoadOptions {
|
||||
wasi?: WASIInstance;
|
||||
overwriteImports?: (importObject: WebAssembly.Imports) => WebAssembly.Imports;
|
||||
beforeInit?: (source: WebAssembly.WebAssemblyInstantiatedSource) => void;
|
||||
getMemory?: (exports: WebAssembly.Exports) => WebAssembly.Memory;
|
||||
getTable?: (exports: WebAssembly.Exports) => WebAssembly.Table;
|
||||
}
|
||||
|
||||
export declare interface LoadPayload {
|
||||
wasmModule: WebAssembly.Module;
|
||||
wasmMemory: WebAssembly.Memory;
|
||||
sab?: Int32Array;
|
||||
}
|
||||
|
||||
export declare interface MainThreadBaseOptions extends BaseOptions {
|
||||
waitThreadStart?: boolean | number;
|
||||
}
|
||||
|
||||
export declare type MainThreadOptions = MainThreadOptionsWithThreadManager | MainThreadOptionsCreateThreadManager;
|
||||
|
||||
export declare interface MainThreadOptionsCreateThreadManager extends MainThreadBaseOptions, ThreadManagerOptionsMain {
|
||||
}
|
||||
|
||||
export declare interface MainThreadOptionsWithThreadManager extends MainThreadBaseOptions {
|
||||
threadManager?: ThreadManager | (() => ThreadManager);
|
||||
}
|
||||
|
||||
export declare interface MessageEventData<T extends CommandType> {
|
||||
__emnapi__: CommandInfo<T>;
|
||||
}
|
||||
|
||||
export declare class MessageHandler extends ThreadMessageHandler {
|
||||
napiModule: NapiModule | undefined;
|
||||
constructor(options: MessageHandlerOptions);
|
||||
instantiate(data: LoadPayload): InstantiatedSource | PromiseLike<InstantiatedSource>;
|
||||
handle(e: WorkerMessageEvent): void;
|
||||
}
|
||||
|
||||
export declare interface MessageHandlerOptions extends ThreadMessageHandlerOptions {
|
||||
onLoad: (data: LoadPayload) => InstantiatedSource | PromiseLike<InstantiatedSource>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export declare interface NapiModule {
|
||||
imports: {
|
||||
env: any
|
||||
napi: any
|
||||
emnapi: any
|
||||
}
|
||||
exports: any
|
||||
loaded: boolean
|
||||
filename: string
|
||||
childThread: boolean
|
||||
emnapi: {
|
||||
/**
|
||||
* Synchronize data between the wasm memory and an ArrayBuffer / view.
|
||||
*
|
||||
* Note: when a non-shared wasm memory has grown, a view over the
|
||||
* detached old buffer is re-created over the current buffer through the
|
||||
* intrinsic base-class constructor, so for view inputs the returned view
|
||||
* may be a base-class instance (e.g. `Uint8Array` or `DataView`)
|
||||
* instead of the subclass that was passed in.
|
||||
*/
|
||||
syncMemory<T extends ArrayBuffer | ArrayBufferView> (
|
||||
js_to_wasm: boolean,
|
||||
arrayBufferOrView: T,
|
||||
offset?: number,
|
||||
len?: number
|
||||
): T extends ArrayBufferView ? ArrayBufferView : T
|
||||
getMemoryAddress (arrayBufferOrView: ArrayBuffer | ArrayBufferView): PointerInfo
|
||||
addSendListener (worker: any): boolean
|
||||
}
|
||||
|
||||
init (options: InitOptions): any
|
||||
initWorker (arg: number, func: [number, number]): void
|
||||
postMessage?: (msg: any) => any
|
||||
|
||||
waitThreadStart: boolean | number
|
||||
/* Excluded from this release type: PThread */}
|
||||
|
||||
/** @public */
|
||||
export declare interface NodeBinding {
|
||||
node: {
|
||||
emitAsyncInit: Function
|
||||
emitAsyncDestroy: Function
|
||||
makeCallback: Function
|
||||
}
|
||||
napi: {
|
||||
asyncInit: Function
|
||||
asyncDestroy: Function
|
||||
makeCallback: Function
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export declare type PluginContext = any
|
||||
|
||||
/** @public */
|
||||
export declare type PluginFactory = (ctx: PluginContext) => EmnapiPlugin
|
||||
|
||||
/** @public */
|
||||
export declare interface PointerInfo {
|
||||
address: number
|
||||
ownership: ReferenceOwnership
|
||||
runtimeAllocated: 0 | 1
|
||||
}
|
||||
|
||||
export declare interface ReuseWorkerOptions {
|
||||
size: number;
|
||||
strict?: boolean;
|
||||
}
|
||||
|
||||
export declare interface SpawnThreadPayload {
|
||||
startArg: number;
|
||||
errorOrTid: number;
|
||||
}
|
||||
|
||||
export declare interface StartPayload {
|
||||
tid: number;
|
||||
arg: number;
|
||||
sab?: Int32Array;
|
||||
}
|
||||
|
||||
export declare interface StartResult {
|
||||
exitCode: number;
|
||||
instance: WebAssembly.Instance;
|
||||
}
|
||||
|
||||
export declare interface TerminateAllThreadsPayload {
|
||||
}
|
||||
|
||||
export declare class ThreadManager {
|
||||
unusedWorkers: WorkerLike[];
|
||||
pthreads: Record<number, WorkerLike>;
|
||||
get nextWorkerID(): number;
|
||||
wasmModule: WebAssembly.Module | null;
|
||||
wasmMemory: WebAssembly.Memory | null;
|
||||
private readonly messageEvents;
|
||||
private readonly _childThread;
|
||||
private readonly _onCreateWorker;
|
||||
private readonly _reuseWorker;
|
||||
private readonly _beforeLoad?;
|
||||
readonly printErr: (message: string) => void;
|
||||
threadSpawn?: ((startArg: number, errorOrTid?: number) => number);
|
||||
constructor(options: ThreadManagerOptions);
|
||||
init(): void;
|
||||
initMainThread(): void;
|
||||
private preparePool;
|
||||
shouldPreloadWorkers(): boolean;
|
||||
loadWasmModuleToAllWorkers(): Promise<WorkerLike[]>;
|
||||
preloadWorkers(): Promise<WorkerLike[]>;
|
||||
setup(wasmModule: WebAssembly.Module, wasmMemory: WebAssembly.Memory): void;
|
||||
markId(worker: WorkerLike): number;
|
||||
returnWorkerToPool(worker: WorkerLike): void;
|
||||
loadWasmModuleToWorker(worker: WorkerLike, sab?: Int32Array): Promise<WorkerLike>;
|
||||
allocateUnusedWorker(): WorkerLike;
|
||||
getNewWorker(sab?: Int32Array): WorkerLike | undefined;
|
||||
cleanThread(worker: WorkerLike, tid: number, force?: boolean): void;
|
||||
terminateWorker(worker: WorkerLike): void;
|
||||
terminateAllThreads(): void;
|
||||
addMessageEventListener(worker: WorkerLike, onMessage: (e: WorkerMessageEvent) => void): () => void;
|
||||
fireMessageEvent(worker: WorkerLike, e: WorkerMessageEvent): void;
|
||||
}
|
||||
|
||||
export declare type ThreadManagerOptions = ThreadManagerOptionsMain | ThreadManagerOptionsChild;
|
||||
|
||||
export declare interface ThreadManagerOptionsBase {
|
||||
printErr?: (message: string) => void;
|
||||
threadSpawn?: (startArg: number, errorOrTid?: number) => number;
|
||||
}
|
||||
|
||||
export declare interface ThreadManagerOptionsChild extends ThreadManagerOptionsBase {
|
||||
childThread: true;
|
||||
}
|
||||
|
||||
export declare interface ThreadManagerOptionsMain extends ThreadManagerOptionsBase {
|
||||
beforeLoad?: (worker: WorkerLike) => any;
|
||||
reuseWorker?: boolean | number | ReuseWorkerOptions;
|
||||
onCreateWorker: WorkerFactory;
|
||||
childThread?: false;
|
||||
}
|
||||
|
||||
export declare class ThreadMessageHandler {
|
||||
protected instance: WebAssembly.Instance | undefined;
|
||||
private messagesBeforeLoad;
|
||||
protected postMessage: (message: any) => void;
|
||||
protected onLoad?: (data: LoadPayload) => WebAssembly.WebAssemblyInstantiatedSource | PromiseLike<WebAssembly.WebAssemblyInstantiatedSource>;
|
||||
protected onError: (error: Error, type: WorkerMessageType) => void;
|
||||
constructor(options?: ThreadMessageHandlerOptions);
|
||||
instantiate(data: LoadPayload): WebAssembly.WebAssemblyInstantiatedSource | PromiseLike<WebAssembly.WebAssemblyInstantiatedSource>;
|
||||
handle(e: WorkerMessageEvent<MessageEventData<WorkerMessageType>>): void;
|
||||
private _load;
|
||||
private _start;
|
||||
protected _loaded(err: Error | null, source: WebAssembly.WebAssemblyInstantiatedSource | null, payload: LoadPayload): void;
|
||||
protected handleAfterLoad<E extends WorkerMessageEvent>(e: E, f: (e: E) => void): void;
|
||||
}
|
||||
|
||||
export declare interface ThreadMessageHandlerOptions {
|
||||
onLoad?: (data: LoadPayload) => WebAssembly.WebAssemblyInstantiatedSource | PromiseLike<WebAssembly.WebAssemblyInstantiatedSource>;
|
||||
onError?: (error: Error, type: WorkerMessageType) => void;
|
||||
postMessage?: (message: any) => void;
|
||||
}
|
||||
|
||||
export declare const version: string;
|
||||
|
||||
export declare interface WASIInstance {
|
||||
readonly wasiImport?: Record<string, any>;
|
||||
initialize(instance: object): void;
|
||||
start(instance: object): number;
|
||||
getImportObject?(): any;
|
||||
}
|
||||
|
||||
export declare class WASIThreads {
|
||||
PThread: ThreadManager | undefined;
|
||||
private wasmMemory;
|
||||
private wasmInstance;
|
||||
private readonly threadSpawn;
|
||||
readonly childThread: boolean;
|
||||
private readonly postMessage;
|
||||
readonly wasi: WASIInstance;
|
||||
constructor(options: WASIThreadsOptions);
|
||||
getImportObject(): {
|
||||
wasi: WASIThreadsImports;
|
||||
};
|
||||
setup(wasmInstance: WebAssembly.Instance, wasmModule: WebAssembly.Module, wasmMemory?: WebAssembly.Memory): void;
|
||||
preloadWorkers(): Promise<WorkerLike[]>;
|
||||
initialize(instance: WebAssembly.Instance, module: WebAssembly.Module, memory?: WebAssembly.Memory): WebAssembly.Instance;
|
||||
start(instance: WebAssembly.Instance, module: WebAssembly.Module, memory?: WebAssembly.Memory): StartResult;
|
||||
terminateAllThreads(): void;
|
||||
}
|
||||
|
||||
export declare interface WASIThreadsImports {
|
||||
'thread-spawn': (startArg: number, errorOrTid?: number) => number;
|
||||
}
|
||||
|
||||
export declare type WASIThreadsOptions = MainThreadOptions | ChildThreadOptions;
|
||||
|
||||
export declare type WorkerFactory = (ctx: {
|
||||
type: string;
|
||||
name: string;
|
||||
}) => WorkerLike;
|
||||
|
||||
export declare type WorkerLike = (Worker | Worker_2) & {
|
||||
whenLoaded?: Promise<WorkerLike>;
|
||||
loaded?: boolean;
|
||||
__emnapi_tid?: number;
|
||||
};
|
||||
|
||||
export declare interface WorkerMessageEvent<T = any> {
|
||||
data: T;
|
||||
}
|
||||
|
||||
export declare type WorkerMessageType = 'load' | 'start';
|
||||
|
||||
export { }
|
||||
+8142
File diff suppressed because it is too large
Load Diff
+7227
File diff suppressed because it is too large
Load Diff
+5
@@ -0,0 +1,5 @@
|
||||
import { PluginFactory } from '../emnapi-core'
|
||||
export { PluginFactory, PluginContext, EmnapiPlugin } from '../emnapi-core'
|
||||
|
||||
declare const _default: PluginFactory
|
||||
export default _default
|
||||
+818
@@ -0,0 +1,818 @@
|
||||
//#region src/emnapi/async-work.js
|
||||
function asyncWork (emnapiPluginCtx) {
|
||||
const { emnapiCtx, emnapiNodeBinding, emnapiAsyncWorkPoolSize } = emnapiPluginCtx;
|
||||
var mod = (function (exports, emnapi_shared, emscripten_runtime) {
|
||||
|
||||
//#region src/async-work.ts
|
||||
/**
|
||||
* @__deps $emnapiCtx
|
||||
* @__deps $emnapiEnv
|
||||
* @__deps $emnapiNodeBinding
|
||||
* @__deps $emnapiAsyncWorkPoolSize
|
||||
* @__postset
|
||||
* ```
|
||||
* emnapiAWST.init();
|
||||
* ```
|
||||
*/
|
||||
var emnapiAWST = {
|
||||
idGen: {},
|
||||
values: [undefined],
|
||||
queued: new Set(),
|
||||
pending: [],
|
||||
init: function () {
|
||||
const idGen = {
|
||||
nextId: 1,
|
||||
list: [],
|
||||
generate: function () {
|
||||
let id;
|
||||
if (idGen.list.length) {
|
||||
id = idGen.list.shift();
|
||||
}
|
||||
else {
|
||||
id = idGen.nextId;
|
||||
idGen.nextId++;
|
||||
}
|
||||
return id;
|
||||
},
|
||||
reuse: function (id) {
|
||||
idGen.list.push(id);
|
||||
}
|
||||
};
|
||||
emnapiAWST.idGen = idGen;
|
||||
emnapiAWST.values = [undefined];
|
||||
emnapiAWST.queued = new Set();
|
||||
emnapiAWST.pending = [];
|
||||
},
|
||||
create: function (env, resource, resourceName, execute, complete, data) {
|
||||
let asyncId = 0;
|
||||
let triggerAsyncId = 0;
|
||||
if (emnapiNodeBinding) {
|
||||
const asyncContext = emnapiNodeBinding.node.emitAsyncInit(resource, resourceName, -1);
|
||||
asyncId = asyncContext.asyncId;
|
||||
triggerAsyncId = asyncContext.triggerAsyncId;
|
||||
}
|
||||
const id = emnapiAWST.idGen.generate();
|
||||
emnapiAWST.values[id] = {
|
||||
env,
|
||||
id,
|
||||
resource,
|
||||
asyncId,
|
||||
triggerAsyncId,
|
||||
status: 0,
|
||||
execute,
|
||||
complete,
|
||||
data
|
||||
};
|
||||
return id;
|
||||
},
|
||||
callComplete: function (work, status) {
|
||||
const complete = work.complete;
|
||||
const env = work.env;
|
||||
const data = work.data;
|
||||
const callback = () => {
|
||||
if (!complete)
|
||||
return;
|
||||
const envObject = emnapi_shared.emnapiEnv;
|
||||
const scope = emnapiCtx.openScope(envObject);
|
||||
try {
|
||||
envObject.callbackIntoModule(true, () => {
|
||||
(emnapiPluginCtx.wasmTable.get(complete))(env, status, data);
|
||||
});
|
||||
}
|
||||
finally {
|
||||
emnapiCtx.closeScope(envObject, scope);
|
||||
}
|
||||
};
|
||||
if (emnapiNodeBinding) {
|
||||
emnapiNodeBinding.node.makeCallback(work.resource, callback, [], {
|
||||
asyncId: work.asyncId,
|
||||
triggerAsyncId: work.triggerAsyncId
|
||||
});
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
queue: function (id) {
|
||||
const work = emnapiAWST.values[id];
|
||||
if (!work)
|
||||
return;
|
||||
if (work.status === 0) {
|
||||
work.status = 1;
|
||||
if (emnapiAWST.queued.size >= (Math.abs(emnapiAsyncWorkPoolSize) || 4)) {
|
||||
emnapiAWST.pending.push(id);
|
||||
return;
|
||||
}
|
||||
emnapiAWST.queued.add(id);
|
||||
const env = work.env;
|
||||
const data = work.data;
|
||||
const execute = work.execute;
|
||||
work.status = 2;
|
||||
emnapiCtx.features.setImmediate(() => {
|
||||
(emnapiPluginCtx.wasmTable.get(execute))(env, data);
|
||||
emnapiAWST.queued.delete(id);
|
||||
work.status = 3;
|
||||
emnapiCtx.features.setImmediate(() => {
|
||||
emnapiAWST.callComplete(work, 0 /* napi_status.napi_ok */);
|
||||
});
|
||||
if (emnapiAWST.pending.length > 0) {
|
||||
const nextWorkId = emnapiAWST.pending.shift();
|
||||
emnapiAWST.values[nextWorkId].status = 0;
|
||||
emnapiAWST.queue(nextWorkId);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
cancel: function (id) {
|
||||
const index = emnapiAWST.pending.indexOf(id);
|
||||
if (index !== -1) {
|
||||
const work = emnapiAWST.values[id];
|
||||
if (work && (work.status === 1)) {
|
||||
work.status = 4;
|
||||
emnapiAWST.pending.splice(index, 1);
|
||||
emnapiCtx.features.setImmediate(() => {
|
||||
emnapiAWST.callComplete(work, 11 /* napi_status.napi_cancelled */);
|
||||
});
|
||||
return 0 /* napi_status.napi_ok */;
|
||||
}
|
||||
else {
|
||||
return 9 /* napi_status.napi_generic_failure */;
|
||||
}
|
||||
}
|
||||
return 9 /* napi_status.napi_generic_failure */;
|
||||
},
|
||||
remove: function (id) {
|
||||
const work = emnapiAWST.values[id];
|
||||
if (!work)
|
||||
return;
|
||||
if (emnapiNodeBinding) {
|
||||
emnapiNodeBinding.node.emitAsyncDestroy({
|
||||
asyncId: work.asyncId,
|
||||
triggerAsyncId: work.triggerAsyncId
|
||||
});
|
||||
}
|
||||
emnapiAWST.values[id] = undefined;
|
||||
emnapiAWST.idGen.reuse(id);
|
||||
}
|
||||
};
|
||||
|
||||
//#endregion src/async-work.ts
|
||||
|
||||
//#region src/macro.ts
|
||||
|
||||
//#endregion src/macro.ts
|
||||
|
||||
//#region src/core/async-work.ts
|
||||
const {
|
||||
// onCreateWorker, napiModule, singleThreadAsyncWork, _emnapi_async_work_pool_size,
|
||||
// PThread, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_PTHREAD, wasmInstance, _free, wasmMemory, _malloc,
|
||||
_emnapi_node_emit_async_init, _emnapi_node_emit_async_destroy, _emnapi_runtime_keepalive_pop, _emnapi_runtime_keepalive_push } = emnapiPluginCtx;
|
||||
var emnapiAWMT = {
|
||||
pool: [],
|
||||
workerReady: null,
|
||||
globalAddress: 0,
|
||||
globalOffset: {
|
||||
idle_threads: 0,
|
||||
q: 1 * 4,
|
||||
next: 1 * 4,
|
||||
prev: 2 * 4,
|
||||
mutex: 3 * 4,
|
||||
cond: 4 * 4,
|
||||
exit_message: 5 * 4,
|
||||
end: 7 * 4
|
||||
},
|
||||
offset: {
|
||||
/* napi_ref */ resource: 0,
|
||||
/* double */ async_id: 8,
|
||||
/* double */ trigger_async_id: 16,
|
||||
/* napi_env */ env: 24,
|
||||
/* int32_t */ status: 1 * 4 + 24, // 0 for pending, 1 for cancelled, 2 for completed
|
||||
queue: 2 * 4 + 24,
|
||||
queue_next: 2 * 4 + 24,
|
||||
queue_prev: 3 * 4 + 24,
|
||||
/* void* */ data: 4 * 4 + 24,
|
||||
/* napi_async_execute_callback */ execute: 5 * 4 + 24,
|
||||
/* napi_async_complete_callback */ complete: 6 * 4 + 24,
|
||||
end: 7 * 4 + 24
|
||||
},
|
||||
/**
|
||||
* When another thread grows the shared WebAssembly.Memory, this agent's
|
||||
* cached `wasmMemory.buffer` may still have the old shorter length
|
||||
* (V8 refreshes it lazily). If a pointer derived from shared memory lies
|
||||
* beyond the cached length, `wasmMemory.grow(0)` forces the agent to
|
||||
* observe the current memory size and refreshes the buffer.
|
||||
*/
|
||||
ensureBufferFor(end) {
|
||||
let buffer = emscripten_runtime.wasmMemory.buffer;
|
||||
if (end > buffer.byteLength) {
|
||||
emscripten_runtime.wasmMemory.grow(0);
|
||||
buffer = emscripten_runtime.wasmMemory.buffer;
|
||||
}
|
||||
return buffer;
|
||||
},
|
||||
init() {
|
||||
emnapiAWMT.pool = [];
|
||||
emnapiAWMT.workerReady = null;
|
||||
if (typeof emscripten_runtime.PThread !== 'undefined') {
|
||||
emscripten_runtime.PThread.unusedWorkers.forEach(emnapiAWMT.addListener);
|
||||
Object.values(emscripten_runtime.PThread.pthreads).forEach(emnapiAWMT.addListener);
|
||||
const __original_getNewWorker = emscripten_runtime.PThread.getNewWorker;
|
||||
emscripten_runtime.PThread.getNewWorker = function () {
|
||||
const r = __original_getNewWorker.apply(this, arguments);
|
||||
emnapiAWMT.addListener(r);
|
||||
return r;
|
||||
};
|
||||
}
|
||||
},
|
||||
addListener(worker) {
|
||||
if (!worker)
|
||||
return false;
|
||||
if (worker._emnapiAWMTListener)
|
||||
return true;
|
||||
const handler = function (e) {
|
||||
const data = emscripten_runtime.ENVIRONMENT_IS_NODE ? e : e.data;
|
||||
const __emnapi__ = data.__emnapi__;
|
||||
if (__emnapi__) {
|
||||
const type = __emnapi__.type;
|
||||
const payload = __emnapi__.payload;
|
||||
if (type === 'async-work-complete') {
|
||||
emnapiAWMT.callComplete(payload.work, 0 /* napi_status.napi_ok */);
|
||||
}
|
||||
}
|
||||
};
|
||||
const dispose = function () {
|
||||
if (emscripten_runtime.ENVIRONMENT_IS_NODE) {
|
||||
worker.off('message', handler);
|
||||
}
|
||||
else {
|
||||
worker.removeEventListener('message', handler, false);
|
||||
}
|
||||
delete worker._emnapiAWMTListener;
|
||||
};
|
||||
worker._emnapiAWMTListener = { handler, dispose };
|
||||
if (emscripten_runtime.ENVIRONMENT_IS_NODE) {
|
||||
worker.on('message', handler);
|
||||
}
|
||||
else {
|
||||
worker.addEventListener('message', handler, false);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
initGlobal() {
|
||||
if (!emnapiAWMT.globalAddress) {
|
||||
emnapiAWMT.globalAddress = emscripten_runtime._malloc(emnapiAWMT.globalOffset.end);
|
||||
emnapiAWMT.globalAddress >>>= 0;
|
||||
const size = emnapiAWMT.globalOffset.end;
|
||||
const addr = emnapiAWMT.globalAddress;
|
||||
new Uint8Array(emnapiAWMT.ensureBufferFor(addr + size), addr, size).fill(0);
|
||||
emnapiAWMT.queueInit(emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.q);
|
||||
emnapiAWMT.queueInit(emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.exit_message);
|
||||
}
|
||||
},
|
||||
terminateWorkers() {
|
||||
emnapiAWMT.pool.forEach(w => {
|
||||
w._emnapiAWMTListener?.dispose();
|
||||
w._emnapiTSFNListener?.dispose();
|
||||
w.terminate();
|
||||
});
|
||||
emnapiAWMT.pool.length = 0;
|
||||
},
|
||||
initWorkers(n) {
|
||||
if (emscripten_runtime.ENVIRONMENT_IS_PTHREAD) {
|
||||
return emnapiAWMT.workerReady || (emnapiAWMT.workerReady = Promise.resolve());
|
||||
}
|
||||
if (emnapiAWMT.workerReady)
|
||||
return emnapiAWMT.workerReady;
|
||||
if (!('emnapi_async_worker_create' in emscripten_runtime.wasmInstance.exports)) {
|
||||
throw new TypeError('`emnapi_async_worker_create` is not exported, please try to add `--export=emnapi_async_worker_create` to linker flags');
|
||||
}
|
||||
const emnapi_async_worker_create = emscripten_runtime.wasmInstance.exports.emnapi_async_worker_create;
|
||||
const args = [];
|
||||
emnapiAWMT.initGlobal();
|
||||
for (let i = 0; i < n; ++i) {
|
||||
args.push(emnapi_async_worker_create(1, emnapiAWMT.globalAddress));
|
||||
}
|
||||
const promises = args.map(index => {
|
||||
if (index === 0) {
|
||||
return Promise.reject(new Error('Failed to create async worker'));
|
||||
}
|
||||
let worker;
|
||||
if (index < 0) {
|
||||
worker = emnapiAWMT.pool[-index - 1];
|
||||
if (worker)
|
||||
return worker.whenLoaded;
|
||||
}
|
||||
index >>>= 0;
|
||||
const tidOffset = 20;
|
||||
const view = new DataView(emnapiAWMT.ensureBufferFor(index + tidOffset + 4));
|
||||
const tid = view.getInt32(index + tidOffset, true);
|
||||
worker = emscripten_runtime.PThread.pthreads[tid];
|
||||
return worker.whenLoaded;
|
||||
});
|
||||
emnapiAWMT.workerReady = Promise.all(promises);
|
||||
return emnapiAWMT.workerReady;
|
||||
},
|
||||
getResource(work) {
|
||||
emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.resource + 4);
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
return GET_HEAP_DATA_VIEW().getUint32(work + emnapiAWMT.offset.resource, true);
|
||||
},
|
||||
getExecute(work) {
|
||||
emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.execute + 4);
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
return GET_HEAP_DATA_VIEW().getUint32(work + emnapiAWMT.offset.execute, true);
|
||||
},
|
||||
getComplete(work) {
|
||||
emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.complete + 4);
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
return GET_HEAP_DATA_VIEW().getUint32(work + emnapiAWMT.offset.complete, true);
|
||||
},
|
||||
getEnv(work) {
|
||||
emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.env + 4);
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
return GET_HEAP_DATA_VIEW().getUint32(work + emnapiAWMT.offset.env, true);
|
||||
},
|
||||
getData(work) {
|
||||
emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.data + 4);
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
return GET_HEAP_DATA_VIEW().getUint32(work + emnapiAWMT.offset.data, true);
|
||||
},
|
||||
getMutex() {
|
||||
const index = emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.mutex;
|
||||
const mutex = {
|
||||
lock() {
|
||||
const isBrowserMain = typeof window !== 'undefined' && typeof document !== 'undefined' && !emscripten_runtime.ENVIRONMENT_IS_NODE;
|
||||
const i32a = new Int32Array(emnapiAWMT.ensureBufferFor(index + 4), index, 1);
|
||||
if (isBrowserMain) {
|
||||
while (true) {
|
||||
const oldValue = Atomics.compareExchange(i32a, 0, 0, 10);
|
||||
if (oldValue === 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
while (true) {
|
||||
const oldValue = Atomics.compareExchange(i32a, 0, 0, 10);
|
||||
if (oldValue === 0) {
|
||||
return;
|
||||
}
|
||||
Atomics.wait(i32a, 0, 10);
|
||||
}
|
||||
}
|
||||
},
|
||||
unlock() {
|
||||
const i32a = new Int32Array(emnapiAWMT.ensureBufferFor(index + 4), index, 1);
|
||||
const oldValue = Atomics.compareExchange(i32a, 0, 10, 0);
|
||||
if (oldValue !== 10) {
|
||||
throw new Error('Tried to unlock while not holding the mutex');
|
||||
}
|
||||
Atomics.notify(i32a, 0, 1);
|
||||
},
|
||||
execute(fn) {
|
||||
mutex.lock();
|
||||
try {
|
||||
return fn();
|
||||
}
|
||||
finally {
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
};
|
||||
return mutex;
|
||||
},
|
||||
getCond() {
|
||||
const index = emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.cond;
|
||||
const mutex = emnapiAWMT.getMutex();
|
||||
const cond = {
|
||||
wait() {
|
||||
const i32a = new Int32Array(emnapiAWMT.ensureBufferFor(index + 4), index, 1);
|
||||
const value = Atomics.load(i32a, 0);
|
||||
mutex.unlock();
|
||||
Atomics.wait(i32a, 0, value);
|
||||
mutex.lock();
|
||||
},
|
||||
signal() {
|
||||
const i32a = new Int32Array(emnapiAWMT.ensureBufferFor(index + 4), index, 1);
|
||||
Atomics.add(i32a, 0, 1);
|
||||
Atomics.notify(i32a, 0, 1);
|
||||
}
|
||||
};
|
||||
return cond;
|
||||
},
|
||||
queueInit(q) {
|
||||
emnapiAWMT.ensureBufferFor(q + 4 + 4);
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
GET_HEAP_DATA_VIEW().setUint32(q, q, true);
|
||||
GET_HEAP_DATA_VIEW().setUint32(q + 4, q, true);
|
||||
},
|
||||
queueInsertTail(h, q) {
|
||||
emnapiAWMT.ensureBufferFor(h + 4 + 4);
|
||||
emnapiAWMT.ensureBufferFor(q + 4 + 4);
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
GET_HEAP_DATA_VIEW().setUint32(q, h, true);
|
||||
const tempValue = GET_HEAP_DATA_VIEW().getUint32(h + 4, true);
|
||||
GET_HEAP_DATA_VIEW().setUint32(q + 4, tempValue, true);
|
||||
const qprev = GET_HEAP_DATA_VIEW().getUint32(q + 4, true);
|
||||
GET_HEAP_DATA_VIEW().setUint32(qprev, q, true);
|
||||
GET_HEAP_DATA_VIEW().setUint32(h + 4, q, true);
|
||||
},
|
||||
queueRemove(q) {
|
||||
emnapiAWMT.ensureBufferFor(q + 4 + 4);
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
const qprev = GET_HEAP_DATA_VIEW().getUint32(q + 4, true);
|
||||
const qnext = GET_HEAP_DATA_VIEW().getUint32(q, true);
|
||||
GET_HEAP_DATA_VIEW().setUint32(qprev, qnext, true);
|
||||
GET_HEAP_DATA_VIEW().setUint32(qnext + 4, qprev, true);
|
||||
},
|
||||
queueEmpty(q) {
|
||||
emnapiAWMT.ensureBufferFor(q + 4);
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
// eslint-disable-next-line eqeqeq
|
||||
return q == GET_HEAP_DATA_VIEW().getUint32(q, true);
|
||||
},
|
||||
scheduleWork: function (work) {
|
||||
if (!emnapiAWMT.workerReady?.ready) {
|
||||
emnapiAWMT.initWorkers(emnapi_shared._emnapi_async_work_pool_size()).then(() => {
|
||||
emnapiAWMT.workerReady.ready = true;
|
||||
}).catch((err) => {
|
||||
emnapiAWMT.workerReady = null;
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
_emnapi_runtime_keepalive_push();
|
||||
emnapiCtx.increaseWaitingRequestCounter();
|
||||
const statusBuffer = new Int32Array(emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.status + 4), work + emnapiAWMT.offset.status, 1);
|
||||
Atomics.store(statusBuffer, 0, 0 /* AsyncWorkStatus.Pending */);
|
||||
const mutex = emnapiAWMT.getMutex();
|
||||
const cond = emnapiAWMT.getCond();
|
||||
mutex.lock();
|
||||
try {
|
||||
emnapiAWMT.queueInsertTail(emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.q, work + emnapiAWMT.offset.queue);
|
||||
}
|
||||
catch (err) {
|
||||
_emnapi_runtime_keepalive_pop();
|
||||
emnapiCtx.decreaseWaitingRequestCounter();
|
||||
mutex.unlock();
|
||||
throw err;
|
||||
}
|
||||
emnapiAWMT.ensureBufferFor(emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.idle_threads + 4);
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
if (GET_HEAP_DATA_VIEW().getUint32(emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.idle_threads, true) > 0) {
|
||||
cond.signal();
|
||||
}
|
||||
mutex.unlock();
|
||||
},
|
||||
cancelWork(work) {
|
||||
let cancelled = false;
|
||||
emnapiAWMT.getMutex().execute(() => {
|
||||
emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.status + 4);
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
cancelled = !emnapiAWMT.queueEmpty(work + emnapiAWMT.offset.queue) && GET_HEAP_DATA_VIEW().getInt32(work + emnapiAWMT.offset.status, true) !== 2 /* AsyncWorkStatus.Completed */;
|
||||
if (cancelled) {
|
||||
emnapiAWMT.queueRemove(work + emnapiAWMT.offset.queue);
|
||||
}
|
||||
});
|
||||
if (!cancelled) {
|
||||
return 9 /* napi_status.napi_generic_failure */;
|
||||
}
|
||||
if (Atomics.compareExchange(new Int32Array(emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.status + 4), work + emnapiAWMT.offset.status, 1), 0, 0 /* AsyncWorkStatus.Pending */, 1 /* AsyncWorkStatus.Cancelled */) !== 0 /* AsyncWorkStatus.Pending */) {
|
||||
return 9 /* napi_status.napi_generic_failure */;
|
||||
}
|
||||
emnapiCtx.features.setImmediate(() => {
|
||||
emnapiAWMT.callComplete(work, 11 /* napi_status.napi_cancelled */);
|
||||
});
|
||||
return 0 /* napi_status.napi_ok */;
|
||||
},
|
||||
callComplete: function (work, status) {
|
||||
_emnapi_runtime_keepalive_pop();
|
||||
emnapiCtx.decreaseWaitingRequestCounter();
|
||||
const complete = emnapiAWMT.getComplete(work);
|
||||
const env = emnapiAWMT.getEnv(work);
|
||||
const data = emnapiAWMT.getData(work);
|
||||
const envObject = emnapi_shared.emnapiEnv;
|
||||
const scope = emnapiCtx.openScope(envObject);
|
||||
const callback = () => {
|
||||
if (!complete)
|
||||
return;
|
||||
envObject.callbackIntoModule(true, () => {
|
||||
(emnapiPluginCtx.wasmTable.get(complete))(env, status, data);
|
||||
});
|
||||
};
|
||||
try {
|
||||
if (emnapiNodeBinding) {
|
||||
const resource = emnapiAWMT.getResource(work);
|
||||
const resource_value = emnapiCtx.getRef(resource).get();
|
||||
const resourceObject = emnapiCtx.jsValueFromNapiValue(resource_value);
|
||||
const view = new DataView(emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.trigger_async_id + 8));
|
||||
const asyncId = view.getFloat64(work + emnapiAWMT.offset.async_id, true);
|
||||
const triggerAsyncId = view.getFloat64(work + emnapiAWMT.offset.trigger_async_id, true);
|
||||
emnapiNodeBinding.node.makeCallback(resourceObject, callback, [], {
|
||||
asyncId,
|
||||
triggerAsyncId
|
||||
});
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
emnapiCtx.closeScope(envObject, scope);
|
||||
}
|
||||
}
|
||||
};
|
||||
emnapiAWST.init();
|
||||
emnapiAWMT.init();
|
||||
/** @__sig ippppppp */
|
||||
var napi_create_async_work = emnapi_shared.singleThreadAsyncWork
|
||||
? function (env, resource, resource_name, execute, complete, data, result) {
|
||||
if (!env)
|
||||
return 1 /* napi_status.napi_invalid_arg */;
|
||||
// @ts-expect-error
|
||||
const envObject = emnapi_shared.emnapiEnv;
|
||||
envObject.checkGCAccess();
|
||||
if (!execute)
|
||||
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
||||
if (!result)
|
||||
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
||||
let resourceObject;
|
||||
if (resource) {
|
||||
resourceObject = Object(emnapiCtx.jsValueFromNapiValue(resource));
|
||||
}
|
||||
else {
|
||||
resourceObject = {};
|
||||
}
|
||||
if (!resource_name)
|
||||
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
||||
const resourceName = String(emnapiCtx.jsValueFromNapiValue(resource_name));
|
||||
const id = emnapiAWST.create(env, resourceObject, resourceName, execute, complete, data);
|
||||
result >>>= 0;
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
GET_HEAP_DATA_VIEW().setUint32(result, id, true);
|
||||
return envObject.clearLastError();
|
||||
}
|
||||
: function (env, resource, resource_name, execute, complete, data, result) {
|
||||
if (!env)
|
||||
return 1 /* napi_status.napi_invalid_arg */;
|
||||
// @ts-expect-error
|
||||
const envObject = emnapi_shared.emnapiEnv;
|
||||
envObject.checkGCAccess();
|
||||
if (!execute)
|
||||
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
||||
if (!result)
|
||||
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
||||
let resourceObject;
|
||||
if (resource) {
|
||||
resourceObject = Object(emnapiCtx.jsValueFromNapiValue(resource));
|
||||
}
|
||||
else {
|
||||
resourceObject = {};
|
||||
}
|
||||
if (!resource_name)
|
||||
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
||||
const sizeofAW = emnapiAWMT.offset.end;
|
||||
let aw = emscripten_runtime._malloc(sizeofAW);
|
||||
if (!aw)
|
||||
return envObject.setLastError(9 /* napi_status.napi_generic_failure */);
|
||||
aw >>>= 0;
|
||||
new Uint8Array(emnapiAWMT.ensureBufferFor(aw + sizeofAW)).subarray(aw, aw + sizeofAW).fill(0);
|
||||
const s = emnapiCtx.napiValueFromJsValue(resourceObject);
|
||||
const resourceRef = emnapiCtx.createReference(envObject, s, 1, 1 /* ReferenceOwnership.kUserland */);
|
||||
const resource_ = resourceRef.id;
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
GET_HEAP_DATA_VIEW().setUint32(aw, resource_, true);
|
||||
_emnapi_node_emit_async_init(s, resource_name, -1, aw + emnapiAWMT.offset.async_id);
|
||||
GET_HEAP_DATA_VIEW().setUint32(aw + emnapiAWMT.offset.env, env, true);
|
||||
GET_HEAP_DATA_VIEW().setUint32(aw + emnapiAWMT.offset.execute, execute, true);
|
||||
GET_HEAP_DATA_VIEW().setUint32(aw + emnapiAWMT.offset.complete, complete, true);
|
||||
GET_HEAP_DATA_VIEW().setUint32(aw + emnapiAWMT.offset.data, data, true);
|
||||
emnapiAWMT.queueInit(aw + emnapiAWMT.offset.queue);
|
||||
result >>>= 0;
|
||||
GET_HEAP_DATA_VIEW().setUint32(result, aw, true);
|
||||
return envObject.clearLastError();
|
||||
};
|
||||
/** @__sig ipp */
|
||||
var napi_delete_async_work = emnapi_shared.singleThreadAsyncWork
|
||||
? function (env, work) {
|
||||
if (!env)
|
||||
return 1 /* napi_status.napi_invalid_arg */;
|
||||
// @ts-expect-error
|
||||
const envObject = emnapi_shared.emnapiEnv;
|
||||
envObject.checkGCAccess();
|
||||
if (!work)
|
||||
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
||||
work >>>= 0;
|
||||
emnapiAWST.remove(work);
|
||||
return envObject.clearLastError();
|
||||
}
|
||||
: function (env, work) {
|
||||
if (!env)
|
||||
return 1 /* napi_status.napi_invalid_arg */;
|
||||
// @ts-expect-error
|
||||
const envObject = emnapi_shared.emnapiEnv;
|
||||
envObject.checkGCAccess();
|
||||
if (!work)
|
||||
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
||||
work >>>= 0;
|
||||
const resource = emnapiAWMT.getResource(work);
|
||||
emnapiCtx.getRef(resource).dispose();
|
||||
if (emnapiNodeBinding) {
|
||||
const view = new DataView(emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.trigger_async_id + 8));
|
||||
const asyncId = view.getFloat64(work + emnapiAWMT.offset.async_id, true);
|
||||
const triggerAsyncId = view.getFloat64(work + emnapiAWMT.offset.trigger_async_id, true);
|
||||
_emnapi_node_emit_async_destroy(asyncId, triggerAsyncId);
|
||||
}
|
||||
emscripten_runtime._free(work);
|
||||
return envObject.clearLastError();
|
||||
};
|
||||
/** @__sig ipp */
|
||||
var napi_queue_async_work = emnapi_shared.singleThreadAsyncWork
|
||||
? function (env, work) {
|
||||
if (!env)
|
||||
return 1 /* napi_status.napi_invalid_arg */;
|
||||
const envObject = emnapi_shared.emnapiEnv;
|
||||
if (!work)
|
||||
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
||||
work >>>= 0;
|
||||
emnapiAWST.queue(work);
|
||||
return envObject.clearLastError();
|
||||
}
|
||||
: function (env, work) {
|
||||
if (!env)
|
||||
return 1 /* napi_status.napi_invalid_arg */;
|
||||
const envObject = emnapi_shared.emnapiEnv;
|
||||
if (!work)
|
||||
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
||||
work >>>= 0;
|
||||
emnapiAWMT.scheduleWork(work);
|
||||
return envObject.clearLastError();
|
||||
};
|
||||
/** @__sig ipp */
|
||||
var napi_cancel_async_work = emnapi_shared.singleThreadAsyncWork
|
||||
? function (env, work) {
|
||||
if (!env)
|
||||
return 1 /* napi_status.napi_invalid_arg */;
|
||||
const envObject = emnapi_shared.emnapiEnv;
|
||||
if (!work)
|
||||
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
||||
work >>>= 0;
|
||||
const status = emnapiAWST.cancel(work);
|
||||
if (status === 0 /* napi_status.napi_ok */)
|
||||
return envObject.clearLastError();
|
||||
return envObject.setLastError(status);
|
||||
}
|
||||
: function (env, work) {
|
||||
if (!env)
|
||||
return 1 /* napi_status.napi_invalid_arg */;
|
||||
const envObject = emnapi_shared.emnapiEnv;
|
||||
if (!work)
|
||||
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
||||
work >>>= 0;
|
||||
const status = emnapiAWMT.cancelWork(work);
|
||||
if (status === 0 /* napi_status.napi_ok */)
|
||||
return envObject.clearLastError();
|
||||
return envObject.setLastError(status);
|
||||
};
|
||||
/** @__sig pp */
|
||||
function _emnapi_async_worker(globalAddress) {
|
||||
globalAddress >>>= 0;
|
||||
emnapiAWMT.globalAddress = globalAddress;
|
||||
const mutex = emnapiAWMT.getMutex();
|
||||
const cond = emnapiAWMT.getCond();
|
||||
mutex.lock();
|
||||
const exitMessageAddr = globalAddress + emnapiAWMT.globalOffset.exit_message;
|
||||
const idleThreadsAddr = globalAddress + emnapiAWMT.globalOffset.idle_threads;
|
||||
const workerQueueAddr = globalAddress + emnapiAWMT.globalOffset.q;
|
||||
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
||||
for (;;) {
|
||||
emnapiAWMT.ensureBufferFor(workerQueueAddr + 4);
|
||||
while (emnapiAWMT.queueEmpty(workerQueueAddr)) {
|
||||
Atomics.add(new Int32Array(emnapiAWMT.ensureBufferFor(idleThreadsAddr + 4), idleThreadsAddr, 1), 0, 1);
|
||||
cond.wait();
|
||||
Atomics.sub(new Int32Array(emnapiAWMT.ensureBufferFor(idleThreadsAddr + 4), idleThreadsAddr, 1), 0, 1);
|
||||
}
|
||||
const q = GET_HEAP_DATA_VIEW().getUint32(workerQueueAddr, true);
|
||||
if (q === exitMessageAddr) {
|
||||
cond.signal();
|
||||
mutex.unlock();
|
||||
break;
|
||||
}
|
||||
const work = q - emnapiAWMT.offset.queue;
|
||||
emnapiAWMT.queueRemove(q);
|
||||
emnapiAWMT.queueInit(q);
|
||||
mutex.unlock();
|
||||
const statusBuffer = new Int32Array(emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.status + 4), work + emnapiAWMT.offset.status, 1);
|
||||
if (Atomics.load(statusBuffer, 0) === 1 /* AsyncWorkStatus.Cancelled */) {
|
||||
emscripten_runtime.abort('unreachable');
|
||||
}
|
||||
const execute = emnapiAWMT.getExecute(work);
|
||||
const env = emnapiAWMT.getEnv(work);
|
||||
const data = emnapiAWMT.getData(work);
|
||||
(emnapiPluginCtx.wasmTable.get(execute))(env, data);
|
||||
Atomics.store(statusBuffer, 0, 2 /* AsyncWorkStatus.Completed */);
|
||||
const postMessage = emnapi_shared.napiModule.postMessage;
|
||||
postMessage({
|
||||
__emnapi__: {
|
||||
type: 'async-work-complete',
|
||||
payload: { work }
|
||||
}
|
||||
});
|
||||
mutex.lock();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/** @__sig ipp */
|
||||
function _emnapi_spawn_worker(f, globalAddress) {
|
||||
if (typeof emnapi_shared.onCreateWorker !== 'function') {
|
||||
throw new TypeError('`options.onCreateWorker` is not a function');
|
||||
}
|
||||
const promises = [];
|
||||
const args = [];
|
||||
if (!('emnapi_async_worker_create' in emscripten_runtime.wasmInstance.exports)) {
|
||||
throw new TypeError('`emnapi_async_worker_create` is not exported, please try to add `--export=emnapi_async_worker_create` to linker flags');
|
||||
}
|
||||
args.push(emscripten_runtime.wasmInstance.exports.emnapi_async_worker_create(0, 0));
|
||||
const handleError = (e) => {
|
||||
if ('message' in e && (e.message.indexOf('RuntimeError') !== -1 || e.message.indexOf('unreachable') !== -1)) {
|
||||
emnapiAWMT.terminateWorkers();
|
||||
}
|
||||
};
|
||||
let ret;
|
||||
try {
|
||||
const worker = emnapi_shared.onCreateWorker({ type: 'async-work', name: 'emnapi-async-worker' });
|
||||
const p = emscripten_runtime.PThread.loadWasmModuleToWorker(worker);
|
||||
if (emscripten_runtime.ENVIRONMENT_IS_NODE) {
|
||||
worker.on('error', handleError);
|
||||
}
|
||||
else {
|
||||
worker.addEventListener('error', handleError, false);
|
||||
}
|
||||
emnapiAWMT.addListener(worker);
|
||||
if (typeof emnapiPluginCtx.emnapiTSFN !== 'undefined') {
|
||||
emnapiPluginCtx.emnapiTSFN.addListener(worker);
|
||||
}
|
||||
promises.push(p.then(() => {
|
||||
if (typeof worker.unref === 'function') {
|
||||
worker.unref();
|
||||
}
|
||||
}));
|
||||
ret = emnapiAWMT.pool.push(worker) - 1;
|
||||
const arg = args[0];
|
||||
worker.threadBlockBase = arg;
|
||||
worker.postMessage({
|
||||
__emnapi__: {
|
||||
type: 'async-worker-init',
|
||||
payload: { arg, func: [f, globalAddress] }
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
const arg = args[0];
|
||||
emscripten_runtime._free(arg);
|
||||
throw err;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
function initWorker(startArg, func) {
|
||||
if (emnapi_shared.napiModule.childThread) {
|
||||
if (typeof emscripten_runtime.wasmInstance.exports.emnapi_async_worker_init !== 'function') {
|
||||
throw new TypeError('`emnapi_async_worker_init` is not exported, please try to add `--export=emnapi_async_worker_init` to linker flags');
|
||||
}
|
||||
emscripten_runtime.wasmInstance.exports.emnapi_async_worker_init(startArg);
|
||||
(emnapiPluginCtx.wasmTable.get(func[0]))(func[1]);
|
||||
}
|
||||
else {
|
||||
throw new Error('startThread is only available in child threads');
|
||||
}
|
||||
}
|
||||
emnapi_shared.napiModule.initWorker = initWorker;
|
||||
|
||||
//#endregion src/core/async-work.ts
|
||||
|
||||
exports._emnapi_async_worker = _emnapi_async_worker;
|
||||
exports._emnapi_spawn_worker = _emnapi_spawn_worker;
|
||||
exports.napi_cancel_async_work = napi_cancel_async_work;
|
||||
exports.napi_create_async_work = napi_create_async_work;
|
||||
exports.napi_delete_async_work = napi_delete_async_work;
|
||||
exports.napi_queue_async_work = napi_queue_async_work;
|
||||
|
||||
return exports;
|
||||
|
||||
})({}, emnapiPluginCtx, emnapiPluginCtx);
|
||||
return {
|
||||
importObject: (original) => {
|
||||
|
||||
Object.keys(mod).forEach(key => {
|
||||
if (key.startsWith('napi_') || key.startsWith('node_api_')) {
|
||||
original.napi[key] = mod[key];
|
||||
} else {
|
||||
original.env[key] = mod[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//#endregion src/emnapi/async-work.js
|
||||
|
||||
export { asyncWork as default };
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export { PluginFactory, PluginContext, EmnapiPlugin } from '../emnapi-core'
|
||||
export { default as v8 } from './v8'
|
||||
export { default as asyncWork } from './async-work'
|
||||
export { default as tsfn } from './threadsafe-function'
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export { default as v8 } from './v8.js'
|
||||
export { default as asyncWork } from './async-work.js'
|
||||
export { default as tsfn } from './threadsafe-function.js'
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import { PluginFactory } from '../emnapi-core'
|
||||
export { PluginFactory, PluginContext, EmnapiPlugin } from '../emnapi-core'
|
||||
|
||||
declare const _default: PluginFactory
|
||||
export default _default
|
||||
+1010
File diff suppressed because it is too large
Load Diff
+5
@@ -0,0 +1,5 @@
|
||||
import { PluginFactory } from '../emnapi-core'
|
||||
export { PluginFactory, PluginContext, EmnapiPlugin } from '../emnapi-core'
|
||||
|
||||
declare const _default: PluginFactory
|
||||
export default _default
|
||||
+1446
File diff suppressed because it is too large
Load Diff
+39
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "@emnapi/core",
|
||||
"version": "2.0.0-alpha.3",
|
||||
"description": "emnapi core",
|
||||
"type": "module",
|
||||
"main": "./dist/emnapi-core.js",
|
||||
"module": "./dist/emnapi-core.js",
|
||||
"types": "./dist/emnapi-core.d.ts",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": "./dist/emnapi-core.js",
|
||||
"./plugins": "./dist/plugins/index.js",
|
||||
"./plugins/index": "./dist/plugins/index.js",
|
||||
"./plugins/v8": "./dist/plugins/v8.js",
|
||||
"./plugins/async-work": "./dist/plugins/async-work.js",
|
||||
"./plugins/threadsafe-function": "./dist/plugins/threadsafe-function.js",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "2.0.1",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node ./script/build.js && rollup -c"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/toyobayashi/emnapi.git"
|
||||
},
|
||||
"author": "toyobayashi",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/toyobayashi/emnapi/issues"
|
||||
},
|
||||
"homepage": "https://github.com/toyobayashi/emnapi#readme",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import { defineConfig } from '@emnapi/shared'
|
||||
import pkg from './package.json' with { type: 'json' }
|
||||
import { join, dirname } from 'path'
|
||||
import fs from 'fs'
|
||||
|
||||
const plugins = [
|
||||
['v8', 'v8', 'emnapiCorePluginsV8'],
|
||||
['asyncWork', 'async-work', 'emnapiCorePluginsAsyncWork'],
|
||||
['tsfn', 'threadsafe-function', 'emnapiCorePluginsThreadSafeFunction']
|
||||
]
|
||||
|
||||
export default [
|
||||
...defineConfig({
|
||||
outputName: 'emnapiCore',
|
||||
outputFile: 'emnapi-core',
|
||||
defines: {
|
||||
__VERSION__: JSON.stringify(pkg.version)
|
||||
},
|
||||
external: ['@emnapi/wasi-threads'],
|
||||
plugins: [
|
||||
{
|
||||
name: 'rollup-plugin-create-plugin-entry',
|
||||
writeBundle (outputOptions) {
|
||||
const distDir = dirname(outputOptions.file)
|
||||
fs.mkdirSync(join(distDir, 'plugins'), { recursive: true })
|
||||
const entryFile = join(distDir, 'plugins', 'index.js')
|
||||
const dtsEntryFile = join(distDir, 'plugins', 'index.d.ts')
|
||||
let content = ''
|
||||
for (const [name, file] of plugins) {
|
||||
content += `export { default as ${name} } from './${file}.js'\n`
|
||||
}
|
||||
let dtsContent = "export { PluginFactory, PluginContext, EmnapiPlugin } from '../emnapi-core'\n"
|
||||
for (const [name, file] of plugins) {
|
||||
dtsContent += `export { default as ${name} } from './${file}'\n`
|
||||
}
|
||||
fs.writeFileSync(entryFile, content, 'utf8')
|
||||
fs.writeFileSync(dtsEntryFile, dtsContent, 'utf8')
|
||||
}
|
||||
}
|
||||
]
|
||||
}),
|
||||
...defineConfig({
|
||||
outputFile: 'emnapi-core.full',
|
||||
defines: {
|
||||
__VERSION__: JSON.stringify(pkg.version)
|
||||
},
|
||||
}),
|
||||
...plugins.flatMap(([_, name, outputName]) => defineConfig({
|
||||
outputName,
|
||||
outputFile: `plugins/${name}`,
|
||||
dtsEntry: `dist/types/emnapi/${name}.d.ts`,
|
||||
staticDtsContent: (code) => code.replace(/\.\/index/g, '../emnapi-core'),
|
||||
input: join(import.meta.dirname, `src/emnapi/${name}.js`),
|
||||
compilerOptions: {
|
||||
declaration: false,
|
||||
declarationMap: false,
|
||||
declarationDir: undefined
|
||||
}
|
||||
}))
|
||||
]
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-present Toyobayashi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+1
@@ -0,0 +1 @@
|
||||
See [https://github.com/toyobayashi/emnapi](https://github.com/toyobayashi/emnapi)
|
||||
+828
@@ -0,0 +1,828 @@
|
||||
/// <reference lib="esnext.disposable" />
|
||||
|
||||
export declare type Ptr = number | bigint
|
||||
|
||||
export declare interface IBuffer extends Uint8Array {
|
||||
toString (encoding?: string, start?: number, end?: number): string
|
||||
}
|
||||
|
||||
export declare interface BufferCtor {
|
||||
readonly prototype: IBuffer
|
||||
/** @deprecated */
|
||||
new (...args: any[]): IBuffer
|
||||
from: {
|
||||
(buffer: ArrayBufferLike): IBuffer
|
||||
(buffer: ArrayBufferLike, byteOffset: number, length: number): IBuffer
|
||||
}
|
||||
alloc: (size: number) => IBuffer
|
||||
isBuffer: (obj: unknown) => obj is IBuffer
|
||||
}
|
||||
|
||||
export declare const enum Constant {
|
||||
HOLE,
|
||||
EMPTY,
|
||||
UNDEFINED,
|
||||
NULL,
|
||||
FALSE,
|
||||
TRUE,
|
||||
GLOBAL,
|
||||
EMPTY_STRING,
|
||||
}
|
||||
|
||||
export declare const enum Version {
|
||||
NODE_API_SUPPORTED_VERSION_MIN = 1,
|
||||
NODE_API_DEFAULT_MODULE_API_VERSION = 8,
|
||||
NODE_API_SUPPORTED_VERSION_MAX = 10,
|
||||
NODE_MODULE_VERSION = 127,
|
||||
NAPI_VERSION_EXPERIMENTAL = 2147483647 // INT_MAX
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export declare type Pointer<T> = number | bigint
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export declare type PointerPointer<T> = number
|
||||
export declare type FunctionPointer<T extends (...args: any[]) => any> = Pointer<T>
|
||||
export declare type Const<T> = T
|
||||
|
||||
export declare type void_p = Pointer<void>
|
||||
export declare type void_pp = Pointer<void_p>
|
||||
export declare type bool = number
|
||||
export declare type char = number
|
||||
export declare type char_p = Pointer<char>
|
||||
export declare type unsigned_char = number
|
||||
export declare type const_char = Const<char>
|
||||
export declare type const_char_p = Pointer<const_char>
|
||||
export declare type char16_t = number
|
||||
export declare type const_char16_t = number
|
||||
export declare type char16_t_p = Pointer<char16_t>
|
||||
export declare type const_char16_t_p = Pointer<const_char16_t>
|
||||
|
||||
export declare type short = number
|
||||
export declare type unsigned_short = number
|
||||
export declare type int = number
|
||||
export declare type unsigned_int = number
|
||||
export declare type long = number
|
||||
export declare type unsigned_long = number
|
||||
export declare type long_long = bigint
|
||||
export declare type unsigned_long_long = bigint
|
||||
export declare type float = number
|
||||
export declare type double = number
|
||||
export declare type long_double = number
|
||||
export declare type size_t = number
|
||||
|
||||
export declare type int8_t = number
|
||||
export declare type uint8_t = number
|
||||
export declare type int16_t = number
|
||||
export declare type uint16_t = number
|
||||
export declare type int32_t = number
|
||||
export declare type uint32_t = number
|
||||
export declare type int64_t = bigint
|
||||
export declare type uint64_t = bigint
|
||||
export declare type napi_env = Pointer<unknown>
|
||||
|
||||
export declare type napi_value = Pointer<unknown>
|
||||
export declare type napi_ref = Pointer<unknown>
|
||||
export declare type napi_deferred = Pointer<unknown>
|
||||
export declare type napi_handle_scope = Pointer<unknown>
|
||||
export declare type napi_escapable_handle_scope = Pointer<unknown>
|
||||
|
||||
export declare type napi_addon_register_func = FunctionPointer<(env: napi_env, exports: napi_value) => napi_value>
|
||||
|
||||
export declare type napi_callback_info = Pointer<unknown>
|
||||
export declare type napi_callback = FunctionPointer<(env: napi_env, info: napi_callback_info) => napi_value>
|
||||
|
||||
export declare interface napi_extended_error_info {
|
||||
error_message: const_char_p
|
||||
engine_reserved: void_p
|
||||
engine_error_code: uint32_t
|
||||
error_code: napi_status
|
||||
}
|
||||
|
||||
export declare interface napi_property_descriptor {
|
||||
// One of utf8name or name should be NULL.
|
||||
utf8name: const_char_p
|
||||
name: napi_value
|
||||
|
||||
method: napi_callback
|
||||
getter: napi_callback
|
||||
setter: napi_callback
|
||||
value: napi_value
|
||||
/* napi_property_attributes */
|
||||
attributes: number
|
||||
data: void_p
|
||||
}
|
||||
|
||||
export declare type napi_finalize = FunctionPointer<(
|
||||
env: napi_env,
|
||||
finalize_data: void_p,
|
||||
finalize_hint: void_p
|
||||
) => void>
|
||||
|
||||
export declare interface node_module {
|
||||
nm_version: int32_t
|
||||
nm_flags: uint32_t
|
||||
nm_filename: Pointer<const_char>
|
||||
nm_register_func: napi_addon_register_func
|
||||
nm_modname: Pointer<const_char>
|
||||
nm_priv: Pointer<void>
|
||||
reserved: PointerPointer<void>
|
||||
}
|
||||
|
||||
export declare interface napi_node_version {
|
||||
major: uint32_t
|
||||
minor: uint32_t
|
||||
patch: uint32_t
|
||||
release: const_char_p
|
||||
}
|
||||
|
||||
export declare interface emnapi_emscripten_version {
|
||||
major: uint32_t
|
||||
minor: uint32_t
|
||||
patch: uint32_t
|
||||
}
|
||||
|
||||
export declare const enum napi_status {
|
||||
napi_ok,
|
||||
napi_invalid_arg,
|
||||
napi_object_expected,
|
||||
napi_string_expected,
|
||||
napi_name_expected,
|
||||
napi_function_expected,
|
||||
napi_number_expected,
|
||||
napi_boolean_expected,
|
||||
napi_array_expected,
|
||||
napi_generic_failure,
|
||||
napi_pending_exception,
|
||||
napi_cancelled,
|
||||
napi_escape_called_twice,
|
||||
napi_handle_scope_mismatch,
|
||||
napi_callback_scope_mismatch,
|
||||
napi_queue_full,
|
||||
napi_closing,
|
||||
napi_bigint_expected,
|
||||
napi_date_expected,
|
||||
napi_arraybuffer_expected,
|
||||
napi_detachable_arraybuffer_expected,
|
||||
napi_would_deadlock, // unused
|
||||
napi_no_external_buffers_allowed,
|
||||
napi_cannot_run_js
|
||||
}
|
||||
|
||||
export declare const enum napi_property_attributes {
|
||||
napi_default = 0,
|
||||
napi_writable = 1 << 0,
|
||||
napi_enumerable = 1 << 1,
|
||||
napi_configurable = 1 << 2,
|
||||
|
||||
// Used with napi_define_class to distinguish static properties
|
||||
// from instance properties. Ignored by napi_define_properties.
|
||||
napi_static = 1 << 10,
|
||||
|
||||
/// #ifdef NAPI_EXPERIMENTAL
|
||||
// Default for class methods.
|
||||
napi_default_method = napi_writable | napi_configurable,
|
||||
|
||||
// Default for object properties, like in JS obj[prop].
|
||||
napi_default_jsproperty = napi_writable | napi_enumerable | napi_configurable
|
||||
/// #endif // NAPI_EXPERIMENTAL
|
||||
}
|
||||
|
||||
export declare const enum napi_valuetype {
|
||||
napi_undefined,
|
||||
napi_null,
|
||||
napi_boolean,
|
||||
napi_number,
|
||||
napi_string,
|
||||
napi_symbol,
|
||||
napi_object,
|
||||
napi_function,
|
||||
napi_external,
|
||||
napi_bigint
|
||||
}
|
||||
|
||||
export declare const enum napi_typedarray_type {
|
||||
napi_int8_array,
|
||||
napi_uint8_array,
|
||||
napi_uint8_clamped_array,
|
||||
napi_int16_array,
|
||||
napi_uint16_array,
|
||||
napi_int32_array,
|
||||
napi_uint32_array,
|
||||
napi_float32_array,
|
||||
napi_float64_array,
|
||||
napi_bigint64_array,
|
||||
napi_biguint64_array,
|
||||
napi_float16_array,
|
||||
}
|
||||
|
||||
export declare const enum napi_key_collection_mode {
|
||||
napi_key_include_prototypes,
|
||||
napi_key_own_only
|
||||
}
|
||||
|
||||
export declare const enum napi_key_filter {
|
||||
napi_key_all_properties = 0,
|
||||
napi_key_writable = 1,
|
||||
napi_key_enumerable = 1 << 1,
|
||||
napi_key_configurable = 1 << 2,
|
||||
napi_key_skip_strings = 1 << 3,
|
||||
napi_key_skip_symbols = 1 << 4
|
||||
}
|
||||
|
||||
export declare const enum napi_key_conversion {
|
||||
napi_key_keep_numbers,
|
||||
napi_key_numbers_to_strings
|
||||
}
|
||||
|
||||
export declare const enum emnapi_memory_view_type {
|
||||
emnapi_int8_array,
|
||||
emnapi_uint8_array,
|
||||
emnapi_uint8_clamped_array,
|
||||
emnapi_int16_array,
|
||||
emnapi_uint16_array,
|
||||
emnapi_int32_array,
|
||||
emnapi_uint32_array,
|
||||
emnapi_float32_array,
|
||||
emnapi_float64_array,
|
||||
emnapi_bigint64_array,
|
||||
emnapi_biguint64_array,
|
||||
emnapi_float16_array,
|
||||
emnapi_data_view = -1,
|
||||
emnapi_buffer = -2
|
||||
}
|
||||
|
||||
export declare const enum napi_threadsafe_function_call_mode {
|
||||
napi_tsfn_nonblocking,
|
||||
napi_tsfn_blocking
|
||||
}
|
||||
|
||||
export declare const enum napi_threadsafe_function_release_mode {
|
||||
napi_tsfn_release,
|
||||
napi_tsfn_abort
|
||||
}
|
||||
export declare class ArrayStore<T extends {
|
||||
id: number | bigint;
|
||||
}> extends BaseArrayStore<T> {
|
||||
protected _allocator: IdAllocator;
|
||||
constructor(initialCapacity?: number);
|
||||
insert(value: T): void;
|
||||
alloc<P extends any[]>(factory: (...args: P) => T, ...args: P): T;
|
||||
dealloc(id: number | bigint): void;
|
||||
}
|
||||
|
||||
export declare class BaseArrayStore<T> extends Disposable_2 implements ObjectAllocator<T> {
|
||||
protected _values: [undefined, ...(T | undefined)[]];
|
||||
constructor(initialCapacity?: number);
|
||||
assign(id: number | bigint, value: T): T;
|
||||
deref<R extends T = T>(id: number | bigint): R | undefined;
|
||||
alloc<P extends any[]>(factory: (...args: P) => T, ...args: P): T;
|
||||
dealloc(id: number | bigint): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export declare type CleanupHookCallbackFunction = number | ((arg: number) => void);
|
||||
|
||||
export declare class Context {
|
||||
private _isStopping;
|
||||
private _canCallIntoJs;
|
||||
private _suppressDestroy;
|
||||
private envStore;
|
||||
refStore: Map<number, Reference>;
|
||||
private readonly refCounter?;
|
||||
private readonly cleanupQueue;
|
||||
readonly features: Features;
|
||||
readonly isolate: Isolate;
|
||||
constructor(options?: ContextOptions);
|
||||
getIsolate(): Isolate;
|
||||
suppressDestroy(): void;
|
||||
getRuntimeVersions(): {
|
||||
version: string;
|
||||
NODE_API_SUPPORTED_VERSION_MAX: Version;
|
||||
NAPI_VERSION_EXPERIMENTAL: Version;
|
||||
NODE_API_DEFAULT_MODULE_API_VERSION: Version;
|
||||
NODE_MODULE_VERSION: Version;
|
||||
};
|
||||
createNotSupportWeakRefError(api: string, message: string): NotSupportWeakRefError;
|
||||
createNotSupportBufferError(api: string, message: string): NotSupportBufferError;
|
||||
createReference(envObject: Env, handle_id: napi_value, initialRefcount: uint32_t, ownership: ReferenceOwnership): Reference;
|
||||
createReferenceWithData(envObject: Env, handle_id: napi_value, initialRefcount: uint32_t, ownership: ReferenceOwnership, data: void_p): Reference;
|
||||
createReferenceWithFinalizer(envObject: Env, handle_id: napi_value, initialRefcount: uint32_t, ownership: ReferenceOwnership, finalize_callback?: napi_finalize, finalize_data?: void_p, finalize_hint?: void_p): Reference;
|
||||
createResolver<T>(): Resolver<T>;
|
||||
adjustAmountOfExternalAllocatedMemory(changeInBytes: number | bigint): bigint;
|
||||
createEnv(filename: string, moduleApiVersion: number, bridge: EnvNativeBridge, nodeBinding?: any): Env;
|
||||
createFunction(envObject: Env, napiCallback: (env: napi_env, info: napi_callback_info) => void_p, data: number | bigint, name: string, dynamicExecution: boolean): Function;
|
||||
createTrackedFinalizer(envObject: Env, finalize_callback: napi_finalize, finalize_data: void_p, finalize_hint: void_p): TrackedFinalizer;
|
||||
getCurrentScope(): HandleScope;
|
||||
openScope(envObject: Env): HandleScope;
|
||||
closeScope(envObject: Env, scope?: HandleScope): void;
|
||||
getEnv(env: napi_env): Env | undefined;
|
||||
getRef(ref: napi_ref): Reference | undefined;
|
||||
getHandleScope(scope: napi_handle_scope): HandleScope | undefined;
|
||||
getCallbackInfo(info: napi_callback_info): ICallbackInfo;
|
||||
napiValueFromJsValue(value: unknown): number | bigint;
|
||||
jsValueFromNapiValue<T = any>(napiValue: number | bigint): T | undefined;
|
||||
createExternal(data: number | bigint): External_2;
|
||||
getExternalValue(external: External_2): number | bigint;
|
||||
isExternal(value: unknown): boolean;
|
||||
addCleanupHook(envObject: Env, fn: CleanupHookCallbackFunction, arg: number): void;
|
||||
removeCleanupHook(envObject: Env, fn: CleanupHookCallbackFunction, arg: number): void;
|
||||
runCleanup(): void;
|
||||
increaseWaitingRequestCounter(): void;
|
||||
decreaseWaitingRequestCounter(): void;
|
||||
setCanCallIntoJs(value: boolean): void;
|
||||
setStopping(value: boolean): void;
|
||||
canCallIntoJs(): boolean;
|
||||
getDylinkMetadata(binary: WebAssembly.Module | Uint8Array): DylinkMetadata;
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
export declare interface ContextOptions extends IsolateOptions {
|
||||
}
|
||||
|
||||
export declare class CountIdAllocator extends Disposable_2 implements IdAllocator {
|
||||
next: number;
|
||||
constructor(initialNext?: number);
|
||||
aquire(): number;
|
||||
release(_: number): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export declare class CountIdReuseAllocator extends CountIdAllocator implements IdAllocator {
|
||||
private _freeList;
|
||||
aquire(): number;
|
||||
release(id: number): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export declare function createContext(options?: ContextOptions): Context;
|
||||
|
||||
export declare function deletePrivate(obj: any, key: Private): boolean;
|
||||
|
||||
declare abstract class Disposable_2 implements globalThis.Disposable {
|
||||
abstract dispose(): void;
|
||||
[Symbol.dispose](): void;
|
||||
}
|
||||
export { Disposable_2 as Disposable }
|
||||
|
||||
export declare interface DylinkMetadata {
|
||||
neededDynlibs: string[];
|
||||
tlsExports: Set<string>;
|
||||
weakImports: Set<string>;
|
||||
runtimePaths: string[];
|
||||
memorySize: number;
|
||||
memoryAlign: number;
|
||||
tableSize: number;
|
||||
tableAlign: number;
|
||||
}
|
||||
|
||||
export declare class EmnapiError extends Error {
|
||||
constructor(message?: string);
|
||||
}
|
||||
|
||||
export declare abstract class Env extends Disposable_2 {
|
||||
id: number | bigint;
|
||||
openHandleScopes: number;
|
||||
instanceData: TrackedFinalizer | null;
|
||||
lastException: Persistent<any>;
|
||||
refs: number;
|
||||
reflist: RefTracker;
|
||||
finalizing_reflist: RefTracker;
|
||||
pendingFinalizers: RefTracker[];
|
||||
moduleApiVersion: number;
|
||||
filename: string;
|
||||
nodeBinding?: any;
|
||||
inGcFinalizer: boolean;
|
||||
bridge: EnvNativeBridge;
|
||||
readonly ctx: Context;
|
||||
private store;
|
||||
constructor(ctx: Context, store: ArrayStore<Env>, bridge: EnvNativeBridge);
|
||||
canCallIntoJs(): boolean;
|
||||
terminatedOrTerminating(): boolean;
|
||||
ref(): void;
|
||||
unref(): void;
|
||||
clearLastError(): napi_status;
|
||||
setLastError(error_code: napi_status, engine_error_code?: uint32_t, engine_reserved?: number): napi_status;
|
||||
callIntoModule<T>(fn: (env: Env) => T, handleException?: (envObject: Env, value: any) => void): T;
|
||||
abstract callFinalizer(cb: napi_finalize, data: void_p, hint: void_p): void;
|
||||
invokeFinalizerFromGC(finalizer: RefTracker): void;
|
||||
checkGCAccess(): void;
|
||||
enqueueFinalizer(finalizer: RefTracker): void;
|
||||
dequeueFinalizer(finalizer: RefTracker): void;
|
||||
deleteMe(): void;
|
||||
dispose(): void;
|
||||
private readonly _bindingMap;
|
||||
initObjectBinding<S extends object>(value: S): IReferenceBinding;
|
||||
getObjectBinding<S extends object>(value: S): IReferenceBinding;
|
||||
setInstanceData(data: number, finalize_cb: number, finalize_hint: number): void;
|
||||
getInstanceData(): number | bigint;
|
||||
}
|
||||
|
||||
export declare interface EnvNativeBridge {
|
||||
address: number;
|
||||
deleteEnv: (ptr: number) => void;
|
||||
setLastError: (env: napi_env, error_code: napi_status, engine_error_code: uint32_t, engine_reserved: number) => void;
|
||||
makeDynCall_vppp: (cb: Ptr) => (a: Ptr, b: Ptr, c: Ptr) => void;
|
||||
makeDynCall_vp: (cb: Ptr) => (a: Ptr) => void;
|
||||
abort: (msg?: string) => never;
|
||||
wasm64?: boolean;
|
||||
}
|
||||
|
||||
declare interface External_2 extends Record<any, any> {
|
||||
}
|
||||
|
||||
declare const External_2: {
|
||||
new (value: number | bigint): External_2;
|
||||
prototype: null;
|
||||
};
|
||||
export { External_2 as External }
|
||||
|
||||
export declare interface Features {
|
||||
makeDynamicFunction: ((...args: any[]) => Function) | undefined;
|
||||
getGlobalThis: () => typeof globalThis;
|
||||
setFunctionName: ((fn: Function, name: string) => void) | undefined;
|
||||
Reflect: typeof Reflect | undefined;
|
||||
finalizer: boolean;
|
||||
weakSymbol: boolean;
|
||||
BigInt: typeof BigInt | undefined;
|
||||
MessageChannel: typeof MessageChannel | undefined;
|
||||
Buffer: BufferCtor | undefined;
|
||||
setImmediate: (callback: () => void) => any;
|
||||
withResolvers: <T>(this: PromiseConstructor) => Resolver<T>;
|
||||
}
|
||||
|
||||
export declare class Finalizer {
|
||||
envObject: Env;
|
||||
private _finalizeCallback;
|
||||
private _finalizeData;
|
||||
private _finalizeHint;
|
||||
private _makeDynCall_vppp;
|
||||
constructor(envObject: Env, _finalizeCallback?: napi_finalize, _finalizeData?: void_p, _finalizeHint?: void_p);
|
||||
copy(): Finalizer;
|
||||
move(target: Finalizer): void;
|
||||
callback(): napi_finalize;
|
||||
data(): void_p;
|
||||
hint(): void_p;
|
||||
resetEnv(): void;
|
||||
resetFinalizer(): void;
|
||||
callFinalizer(): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export declare class FunctionTemplate extends Template {
|
||||
callback: (info: napi_callback_info, v8FunctionCallback: Ptr) => Ptr;
|
||||
v8FunctionCallback: Ptr;
|
||||
data: any;
|
||||
className: string | undefined;
|
||||
signature: Signature | undefined;
|
||||
private _instanceTemplate;
|
||||
private _prototypeTemplate;
|
||||
private _cached;
|
||||
constructor(ctx: Isolate, callback: (info: napi_callback_info, v8FunctionCallback: Ptr) => Ptr, v8FunctionCallback: Ptr, data: any, signature?: Signature);
|
||||
setClassName(name: string): void;
|
||||
instanceTemplate(): ObjectTemplate;
|
||||
prototypeTemplate(): ObjectTemplate;
|
||||
getFunction(): (...args: any[]) => any;
|
||||
}
|
||||
|
||||
export declare function getDefaultContext(): Context;
|
||||
|
||||
export declare function getDylinkMetadata(binary: WebAssembly.Module | Uint8Array): DylinkMetadata;
|
||||
|
||||
export declare function getExternalValue(external: External_2): number | bigint;
|
||||
|
||||
export declare function getInternalField(instance: object, index: number): any;
|
||||
|
||||
export declare function getPrivate(obj: any, key: Private): any;
|
||||
|
||||
export declare class HandleScope extends Disposable_2 {
|
||||
handleStore: HandleStore;
|
||||
id: number | bigint;
|
||||
parent: HandleScope | null;
|
||||
child: HandleScope | null;
|
||||
start: number;
|
||||
end: number;
|
||||
private _escapeCalled;
|
||||
callbackInfo: ICallbackInfo;
|
||||
static create(parentScope: HandleScope | null, handleStore: HandleStore, start?: number, end?: number): HandleScope;
|
||||
constructor(parentScope: HandleScope | null, handleStore: HandleStore, start?: number, end?: number);
|
||||
reuse(parentScope: HandleScope): void;
|
||||
add<V>(value: V): number;
|
||||
addExternal(data: number | bigint): number;
|
||||
dispose(): void;
|
||||
escape(handle: number): number;
|
||||
escapeCalled(): boolean;
|
||||
}
|
||||
|
||||
export declare class HandleStore extends BaseArrayStore<any> {
|
||||
static MIN_ID: 8;
|
||||
private _allocator;
|
||||
private _features;
|
||||
readonly refValues: Map<number, PersistentValueType<any>>;
|
||||
private _erase;
|
||||
private _deref;
|
||||
constructor(features: Features);
|
||||
isOutOfScope(id: number): boolean;
|
||||
deepDeref<R = any>(id: number | bigint): R | undefined;
|
||||
push<S>(value: S): number;
|
||||
erase(start: number, end: number, weak: boolean): void;
|
||||
swap(a: number, b: number): void;
|
||||
}
|
||||
|
||||
export declare function hasPrivate(obj: any, key: Private): boolean;
|
||||
|
||||
export declare interface ICallbackInfo {
|
||||
thiz: any;
|
||||
holder: any;
|
||||
data: void_p;
|
||||
args: ArrayLike<any>;
|
||||
fn: Function;
|
||||
}
|
||||
|
||||
export declare interface IdAllocator extends Disposable_2 {
|
||||
aquire(): number;
|
||||
release(id: number): void;
|
||||
}
|
||||
|
||||
export declare interface IReferenceBinding {
|
||||
wrapped: number;
|
||||
tag: Uint32Array | null;
|
||||
}
|
||||
|
||||
export declare function isExternal(object: unknown): object is External_2;
|
||||
|
||||
export declare class Isolate {
|
||||
private _lastException;
|
||||
private _globalThis;
|
||||
private _scopeStore;
|
||||
private _handleStore;
|
||||
private _externalMemory;
|
||||
globalHandleStore: PersistentStore;
|
||||
readonly features: Features;
|
||||
constructor(options?: IsolateOptions);
|
||||
napiValueFromJsValue(value: unknown): number | bigint;
|
||||
jsValueFromNapiValue<T = any>(napiValue: number | bigint): T | undefined;
|
||||
deleteRefSlotValue(id: number | bigint): void;
|
||||
getRefSlotValue(id: number | bigint): PersistentValueType<any>;
|
||||
setRefSlotValue(id: number | bigint, ref: PersistentValueType<any>): void;
|
||||
createReference<T>(...args: [T] | []): Persistent<T>;
|
||||
insertRef<T>(persistent: Persistent<T>): void;
|
||||
getRef<T = any>(ref: napi_ref): Persistent<T> | undefined;
|
||||
removeRef(ref: napi_ref, force?: boolean): void;
|
||||
getTryCatch(address: number | bigint): TryCatch | undefined;
|
||||
pushTryCatch(address: number | bigint): TryCatch;
|
||||
popTryCatch(address: number | bigint): void;
|
||||
setLastException(err: any): void;
|
||||
throwException(err: any): any;
|
||||
hasPendingException(): boolean;
|
||||
getAndClearLastException(): any;
|
||||
adjustAmountOfExternalAllocatedMemory(changeInBytes: number | bigint): bigint;
|
||||
createSignature(template: FunctionTemplate): Signature;
|
||||
createObjectTemplate(constructor: any): ObjectTemplate;
|
||||
setInternalField(obj: any, index: number, value: any): void;
|
||||
getInternalField(obj: any, index: number): any;
|
||||
getInternalFieldCount(obj: any): number;
|
||||
createFunctionTemplate(callback: (info: napi_callback_info, v8FunctionCallback: Ptr) => Ptr, v8FunctionCallback: Ptr, data: any, signature?: Signature): FunctionTemplate;
|
||||
isScopeEmpty(): boolean;
|
||||
getCurrentScope(): HandleScope;
|
||||
openScope(): HandleScope;
|
||||
closeScope(_scope?: HandleScope): void;
|
||||
getHandleScope(scope: napi_handle_scope): HandleScope | undefined;
|
||||
getCallbackInfo(info: napi_callback_info): ICallbackInfo;
|
||||
createExternal(data: number | bigint): External_2;
|
||||
getExternalValue(external: External_2): number | bigint;
|
||||
isExternal(value: unknown): boolean;
|
||||
createResolver<T>(): Resolver<T>;
|
||||
createPrivate(name: string): Private;
|
||||
getOrCreateGlobalPrivate(name: string): Private;
|
||||
setPrivate(obj: any, key: Private, value: any): void;
|
||||
getPrivate(obj: any, key: Private): any;
|
||||
hasPrivate(obj: any, key: Private): boolean;
|
||||
deletePrivate(obj: any, key: Private): boolean;
|
||||
}
|
||||
|
||||
export declare interface IsolateOptions {
|
||||
features?: Partial<Features>;
|
||||
onExternalMemoryChange?: (current: bigint, old: bigint, delta: bigint) => any;
|
||||
}
|
||||
|
||||
export declare function isReferenceType(v: any): v is object;
|
||||
|
||||
export declare const NAPI_VERSION_EXPERIMENTAL = Version.NAPI_VERSION_EXPERIMENTAL;
|
||||
|
||||
export declare const NODE_API_DEFAULT_MODULE_API_VERSION = Version.NODE_API_DEFAULT_MODULE_API_VERSION;
|
||||
|
||||
export declare const NODE_API_SUPPORTED_VERSION_MAX = Version.NODE_API_SUPPORTED_VERSION_MAX;
|
||||
|
||||
export declare const NODE_API_SUPPORTED_VERSION_MIN = Version.NODE_API_SUPPORTED_VERSION_MIN;
|
||||
|
||||
export declare class NodeEnv extends Env {
|
||||
destructing: boolean;
|
||||
finalizationScheduled: boolean;
|
||||
constructor(ctx: Context, store: ArrayStore<Env>, bridge: EnvNativeBridge);
|
||||
deleteMe(): void;
|
||||
canCallIntoJs(): boolean;
|
||||
triggerFatalException(err: any): void;
|
||||
callbackIntoModule<T>(enforceUncaughtExceptionPolicy: boolean, fn: (env: Env) => T): T;
|
||||
callFinalizer(cb: napi_finalize, data: void_p, hint: void_p): void;
|
||||
callFinalizerInternal(forceUncaught: int, cb: napi_finalize, data: void_p, hint: void_p): void;
|
||||
enqueueFinalizer(finalizer: RefTracker): void;
|
||||
drainFinalizerQueue(): void;
|
||||
}
|
||||
|
||||
export declare class NotSupportBufferError extends EmnapiError {
|
||||
constructor(api: string, message: string);
|
||||
}
|
||||
|
||||
export declare class NotSupportWeakRefError extends EmnapiError {
|
||||
constructor(api: string, message: string);
|
||||
}
|
||||
|
||||
export declare interface ObjectAllocator<T> extends Disposable_2 {
|
||||
deref<R extends T = T>(id: number | bigint): R | undefined;
|
||||
alloc<P extends any[]>(factory: (...args: P) => T, ...args: P): T;
|
||||
dealloc(id: number | bigint): void;
|
||||
}
|
||||
|
||||
export declare class ObjectTemplate extends Template {
|
||||
Ctor: any;
|
||||
internalFieldCount: number;
|
||||
private _accessors;
|
||||
constructor(ctx: Isolate, Ctor?: any);
|
||||
setAccessor(name: string | symbol, getterWrap: (property: Ptr, info: Ptr, getter: Ptr) => Ptr, setterWrap: (property: Ptr, value: Ptr, info: Ptr, setter: Ptr) => Ptr, getter: Ptr, setter: Ptr, data: any, attribute: number, getterSideEffectType: number, setterSideEffectType: number): void;
|
||||
setInternalFieldCount(value: number): void;
|
||||
applyToInstance(instance: any): void;
|
||||
newInstance(_context: any): any;
|
||||
}
|
||||
|
||||
export declare class Persistent<T> extends Disposable_2 {
|
||||
private _param;
|
||||
private _callback;
|
||||
private _isolate;
|
||||
id: number;
|
||||
private static readonly _registry;
|
||||
constructor(isolate: Isolate, ...args: [T] | []);
|
||||
dispose(): void;
|
||||
copy(): Persistent<T>;
|
||||
move(to: Persistent<T>): void;
|
||||
slot(): number;
|
||||
getSlot(): PersistentValueType<T>;
|
||||
setSlot(ref: PersistentValueType<T>): void;
|
||||
deleteSlot(): void;
|
||||
setWeak<P>(param: P, callback: (param: P) => void): void;
|
||||
clearWeak(): void;
|
||||
reset(): void;
|
||||
resetTo(value: T): void;
|
||||
isEmpty(): boolean;
|
||||
deref(): T | undefined;
|
||||
}
|
||||
|
||||
export declare class PersistentStore extends ArrayStore<Persistent<any>> {
|
||||
private _recyleList;
|
||||
constructor(initialCapacity?: number);
|
||||
markUnused(id: number | bigint): void;
|
||||
recycle(): void;
|
||||
}
|
||||
|
||||
export declare type PersistentValueType<T> = StrongRef<T> | WeakRef<T extends object ? T : never> | undefined;
|
||||
|
||||
export declare class Private {
|
||||
private _name;
|
||||
constructor(name: string);
|
||||
name(): string;
|
||||
static forApi(name: string): Private;
|
||||
}
|
||||
|
||||
export declare class Reference extends RefTracker {
|
||||
private static weakCallback;
|
||||
id: number;
|
||||
envObject: Env | undefined;
|
||||
private ctx;
|
||||
private canBeWeak;
|
||||
private _refcount;
|
||||
private _ownership;
|
||||
static create(ctx: Context, envObject: Env, handle_id: napi_value, initialRefcount: uint32_t, ownership: ReferenceOwnership, _unused1?: void_p, _unused2?: void_p, _unused3?: void_p): Reference;
|
||||
constructor(ctx: Context, envObject: Env, handle_id: napi_value, initialRefcount: uint32_t, ownership: ReferenceOwnership);
|
||||
getPersistent(): Persistent<any>;
|
||||
ref(): number;
|
||||
unref(): number;
|
||||
deref(): any;
|
||||
get(): napi_value;
|
||||
resetFinalizer(): void;
|
||||
data(): void_p;
|
||||
refcount(): number;
|
||||
ownership(): ReferenceOwnership;
|
||||
protected callUserFinalizer(): void;
|
||||
protected invokeFinalizerFromGC(): void;
|
||||
private _setWeak;
|
||||
setWeakWithData<T>(data: T, callback: (data: T) => void): void;
|
||||
clearWeak(): void;
|
||||
finalize(): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export declare enum ReferenceOwnership {
|
||||
kRuntime = 0,
|
||||
kUserland = 1
|
||||
}
|
||||
|
||||
export declare class ReferenceWithData extends Reference {
|
||||
private _data;
|
||||
static create(ctx: Context, envObject: Env, value: napi_value, initialRefcount: uint32_t, ownership: ReferenceOwnership, data: void_p): ReferenceWithData;
|
||||
constructor(ctx: Context, envObject: Env, value: napi_value, initialRefcount: uint32_t, ownership: ReferenceOwnership, data: void_p);
|
||||
data(): void_p;
|
||||
}
|
||||
|
||||
export declare class ReferenceWithFinalizer extends Reference {
|
||||
private _finalizer;
|
||||
static create(ctx: Context, envObject: Env, value: napi_value, initialRefcount: uint32_t, ownership: ReferenceOwnership, finalize_callback: napi_finalize, finalize_data: void_p, finalize_hint: void_p): ReferenceWithFinalizer;
|
||||
constructor(ctx: Context, envObject: Env, value: napi_value, initialRefcount: uint32_t, ownership: ReferenceOwnership, finalize_callback: napi_finalize, finalize_data: void_p, finalize_hint: void_p);
|
||||
resetFinalizer(): void;
|
||||
data(): void_p;
|
||||
protected callUserFinalizer(): void;
|
||||
protected invokeFinalizerFromGC(): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export declare class RefTracker extends Disposable_2 {
|
||||
dispose(): void;
|
||||
finalize(): void;
|
||||
protected _next: RefTracker | null;
|
||||
protected _prev: RefTracker | null;
|
||||
link(list: RefTracker): void;
|
||||
unlink(): void;
|
||||
static finalizeAll(list: RefTracker): void;
|
||||
}
|
||||
|
||||
export declare interface Resolver<T> {
|
||||
promise: Promise<T>;
|
||||
resolve: (value: T | PromiseLike<T>) => void;
|
||||
reject: (reason?: any) => void;
|
||||
}
|
||||
|
||||
export declare interface ReusableStoreValue extends Disposable_2 {
|
||||
reuse(...args: any[]): void;
|
||||
}
|
||||
|
||||
export declare class ScopeStore extends BaseArrayStore<HandleScope> {
|
||||
private readonly _rootScope;
|
||||
currentScope: HandleScope;
|
||||
constructor();
|
||||
openScope(handleStore: HandleStore): HandleScope;
|
||||
closeScope(): void;
|
||||
isEmpty(): boolean;
|
||||
}
|
||||
|
||||
export declare function setInternalField(instance: object, index: number, value: any): void;
|
||||
|
||||
export declare function setPrivate(obj: any, key: Private, value: any): void;
|
||||
|
||||
export declare class Signature {
|
||||
receiver: FunctionTemplate | undefined;
|
||||
constructor(receiver: FunctionTemplate | undefined);
|
||||
}
|
||||
|
||||
export declare class StrongRef<T> extends Disposable_2 {
|
||||
private _value;
|
||||
constructor(value: T);
|
||||
deref(): T;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export declare class Template {
|
||||
protected ctx: Isolate;
|
||||
protected _properties: Map<string | symbol, [any, number]>;
|
||||
constructor(ctx: Isolate);
|
||||
set(name: string | symbol, value: any, attr: number): void;
|
||||
protected _addPropertiesToInstance(instance: any): void;
|
||||
}
|
||||
|
||||
export declare class TrackedFinalizer extends RefTracker {
|
||||
private _finalizer;
|
||||
static create(envObject: Env, finalize_callback: napi_finalize, finalize_data: void_p, finalize_hint: void_p): TrackedFinalizer;
|
||||
private constructor();
|
||||
data(): void_p;
|
||||
dispose(): void;
|
||||
finalize(): void;
|
||||
}
|
||||
|
||||
export declare class TryCatch {
|
||||
static top: TryCatch | null;
|
||||
private static _map;
|
||||
id: number | bigint;
|
||||
private _exception;
|
||||
private _caught;
|
||||
private _next;
|
||||
constructor(id: number | bigint);
|
||||
static deref(id: number | bigint): TryCatch | undefined;
|
||||
static pop(): void;
|
||||
isEmpty(): boolean;
|
||||
hasCaught(): boolean;
|
||||
exception(): any;
|
||||
rethrow(ctx: Isolate): any;
|
||||
setError(err: any): void;
|
||||
reset(): void;
|
||||
extractException(): any;
|
||||
}
|
||||
|
||||
export declare const version: string;
|
||||
|
||||
export { }
|
||||
+2225
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+63
@@ -0,0 +1,63 @@
|
||||
'use strict'
|
||||
// Mutable CommonJS facade over the ESM build. The v1 package shipped a real
|
||||
// CJS entry whose exports object was mutable; generated WASI loaders and
|
||||
// @napi-rs/wasm-runtime's runtime.cjs rely on sharing that one mutable
|
||||
// exports object (e.g. test harnesses intercept `createContext` around a
|
||||
// fresh loader require). The v2 package is ESM-only, and `require()` of an
|
||||
// ES module returns a frozen namespace, so restore the contract here. Each
|
||||
// export is re-assigned individually (instead of spreading the namespace
|
||||
// into `module.exports`) so cjs-module-lexer can statically detect the named
|
||||
// exports -- `import()` of this CJS entry must still expose them as module
|
||||
// namespace properties. Module-level state (the default-context memoization)
|
||||
// lives inside the ESM module itself, so both entries observe the same
|
||||
// runtime state.
|
||||
const __emnapiRuntime = require('./dist/emnapi.js')
|
||||
module.exports.ArrayStore = __emnapiRuntime.ArrayStore
|
||||
module.exports.BaseArrayStore = __emnapiRuntime.BaseArrayStore
|
||||
module.exports.Context = __emnapiRuntime.Context
|
||||
module.exports.CountIdAllocator = __emnapiRuntime.CountIdAllocator
|
||||
module.exports.CountIdReuseAllocator = __emnapiRuntime.CountIdReuseAllocator
|
||||
module.exports.Disposable = __emnapiRuntime.Disposable
|
||||
module.exports.EmnapiError = __emnapiRuntime.EmnapiError
|
||||
module.exports.Env = __emnapiRuntime.Env
|
||||
module.exports.External = __emnapiRuntime.External
|
||||
module.exports.Finalizer = __emnapiRuntime.Finalizer
|
||||
module.exports.FunctionTemplate = __emnapiRuntime.FunctionTemplate
|
||||
module.exports.HandleScope = __emnapiRuntime.HandleScope
|
||||
module.exports.HandleStore = __emnapiRuntime.HandleStore
|
||||
module.exports.Isolate = __emnapiRuntime.Isolate
|
||||
module.exports.NAPI_VERSION_EXPERIMENTAL = __emnapiRuntime.NAPI_VERSION_EXPERIMENTAL
|
||||
module.exports.NODE_API_DEFAULT_MODULE_API_VERSION = __emnapiRuntime.NODE_API_DEFAULT_MODULE_API_VERSION
|
||||
module.exports.NODE_API_SUPPORTED_VERSION_MAX = __emnapiRuntime.NODE_API_SUPPORTED_VERSION_MAX
|
||||
module.exports.NODE_API_SUPPORTED_VERSION_MIN = __emnapiRuntime.NODE_API_SUPPORTED_VERSION_MIN
|
||||
module.exports.NodeEnv = __emnapiRuntime.NodeEnv
|
||||
module.exports.NotSupportBufferError = __emnapiRuntime.NotSupportBufferError
|
||||
module.exports.NotSupportWeakRefError = __emnapiRuntime.NotSupportWeakRefError
|
||||
module.exports.ObjectTemplate = __emnapiRuntime.ObjectTemplate
|
||||
module.exports.Persistent = __emnapiRuntime.Persistent
|
||||
module.exports.PersistentStore = __emnapiRuntime.PersistentStore
|
||||
module.exports.Private = __emnapiRuntime.Private
|
||||
module.exports.RefTracker = __emnapiRuntime.RefTracker
|
||||
module.exports.Reference = __emnapiRuntime.Reference
|
||||
module.exports.ReferenceOwnership = __emnapiRuntime.ReferenceOwnership
|
||||
module.exports.ReferenceWithData = __emnapiRuntime.ReferenceWithData
|
||||
module.exports.ReferenceWithFinalizer = __emnapiRuntime.ReferenceWithFinalizer
|
||||
module.exports.ScopeStore = __emnapiRuntime.ScopeStore
|
||||
module.exports.Signature = __emnapiRuntime.Signature
|
||||
module.exports.StrongRef = __emnapiRuntime.StrongRef
|
||||
module.exports.Template = __emnapiRuntime.Template
|
||||
module.exports.TrackedFinalizer = __emnapiRuntime.TrackedFinalizer
|
||||
module.exports.TryCatch = __emnapiRuntime.TryCatch
|
||||
module.exports.createContext = __emnapiRuntime.createContext
|
||||
module.exports.deletePrivate = __emnapiRuntime.deletePrivate
|
||||
module.exports.getDefaultContext = __emnapiRuntime.getDefaultContext
|
||||
module.exports.getDylinkMetadata = __emnapiRuntime.getDylinkMetadata
|
||||
module.exports.getExternalValue = __emnapiRuntime.getExternalValue
|
||||
module.exports.getInternalField = __emnapiRuntime.getInternalField
|
||||
module.exports.getPrivate = __emnapiRuntime.getPrivate
|
||||
module.exports.hasPrivate = __emnapiRuntime.hasPrivate
|
||||
module.exports.isExternal = __emnapiRuntime.isExternal
|
||||
module.exports.isReferenceType = __emnapiRuntime.isReferenceType
|
||||
module.exports.setInternalField = __emnapiRuntime.setInternalField
|
||||
module.exports.setPrivate = __emnapiRuntime.setPrivate
|
||||
module.exports.version = __emnapiRuntime.version
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "@emnapi/runtime",
|
||||
"version": "2.0.0-alpha.3",
|
||||
"description": "emnapi runtime",
|
||||
"type": "module",
|
||||
"main": "./dist/emnapi.js",
|
||||
"module": "./dist/emnapi.js",
|
||||
"types": "./dist/emnapi.d.ts",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/emnapi.d.ts",
|
||||
"import": "./dist/emnapi.js",
|
||||
"require": "./index.cjs",
|
||||
"default": "./dist/emnapi.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"test": "node ./test/index.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/toyobayashi/emnapi.git"
|
||||
},
|
||||
"author": "toyobayashi",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/toyobayashi/emnapi/issues"
|
||||
},
|
||||
"homepage": "https://github.com/toyobayashi/emnapi#readme",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { defineConfig } from '@emnapi/shared'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { EOL } from 'os'
|
||||
import pkg from './package.json' with { type: 'json' }
|
||||
|
||||
export default defineConfig({
|
||||
outputName: 'emnapi',
|
||||
outputFile: 'emnapi',
|
||||
sourcemap: true,
|
||||
defines: {
|
||||
__VERSION__: JSON.stringify(pkg.version)
|
||||
},
|
||||
apiExtractorCallback: (result) => {
|
||||
if (result.succeeded) {
|
||||
let dts = ''
|
||||
dts += '/// <reference lib="esnext.disposable" />' + EOL + EOL
|
||||
dts += fs.readFileSync(path.join(import.meta.dirname, 'src/typings/common.d.ts'), 'utf8').replace(/declare/g, 'export declare')
|
||||
dts += fs.readFileSync(path.join(import.meta.dirname, 'src/typings/ctype.d.ts'), 'utf8').replace(/declare/g, 'export declare')
|
||||
dts += fs.readFileSync(path.join(import.meta.dirname, 'src/typings/napi.d.ts'), 'utf8').replace(/declare/g, 'export declare')
|
||||
dts += fs.readFileSync(result.extractorConfig.publicTrimmedFilePath, 'utf8')
|
||||
fs.writeFileSync(result.extractorConfig.publicTrimmedFilePath, dts, 'utf8')
|
||||
}
|
||||
}
|
||||
})
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-present Toyobayashi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
# @emnapi/wasi-threads
|
||||
|
||||
This package makes [wasi-threads proposal](https://github.com/WebAssembly/wasi-threads) based WASI modules work in Node.js and browser.
|
||||
|
||||
## Quick Start
|
||||
|
||||
`index.html`
|
||||
|
||||
```html
|
||||
<script src="./node_modules/@tybys/wasm-util/dist/wasm-util.js"></script>
|
||||
<script src="./node_modules/@emnapi/wasi-threads/dist/wasi-threads.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
```
|
||||
|
||||
If your application will block browser main thread (for example `pthread_join`), please run it in worker instead.
|
||||
|
||||
```html
|
||||
<script>
|
||||
// pthread_join (Atomics.wait) cannot be called in browser main thread
|
||||
new Worker('./index.js')
|
||||
</script>
|
||||
```
|
||||
|
||||
`index.js`
|
||||
|
||||
```js
|
||||
const ENVIRONMENT_IS_NODE =
|
||||
typeof process === 'object' && process !== null &&
|
||||
typeof process.versions === 'object' && process.versions !== null &&
|
||||
typeof process.versions.node === 'string';
|
||||
|
||||
(function (main) {
|
||||
if (ENVIRONMENT_IS_NODE) {
|
||||
main(require)
|
||||
} else {
|
||||
if (typeof importScripts === 'function') {
|
||||
importScripts('./node_modules/@tybys/wasm-util/dist/wasm-util.js')
|
||||
importScripts('./node_modules/@emnapi/wasi-threads/dist/wasi-threads.js')
|
||||
}
|
||||
const nodeWasi = { WASI: globalThis.wasmUtil.WASI }
|
||||
const nodeWorkerThreads = {
|
||||
Worker: globalThis.Worker
|
||||
}
|
||||
const _require = function (request) {
|
||||
if (request === 'node:wasi' || request === 'wasi') return nodeWasi
|
||||
if (request === 'node:worker_threads' || request === 'worker_threads') return nodeWorkerThreads
|
||||
if (request === '@emnapi/wasi-threads') return globalThis.wasiThreads
|
||||
throw new Error('Can not find module: ' + request)
|
||||
}
|
||||
main(_require)
|
||||
}
|
||||
})(async function (require) {
|
||||
const { WASI } = require('wasi')
|
||||
const { Worker } = require('worker_threads')
|
||||
const { WASIThreads } = require('@emnapi/wasi-threads')
|
||||
|
||||
const wasi = new WASI({
|
||||
version: 'preview1'
|
||||
})
|
||||
const wasiThreads = new WASIThreads({
|
||||
wasi,
|
||||
|
||||
/**
|
||||
* avoid Atomics.wait() deadlock during thread creation in browser
|
||||
* see https://emscripten.org/docs/tools_reference/settings_reference.html#pthread-pool-size
|
||||
*/
|
||||
reuseWorker: ENVIRONMENT_IS_NODE
|
||||
? false
|
||||
: {
|
||||
size: 4 /** greater than actual needs (2) */,
|
||||
strict: true
|
||||
},
|
||||
|
||||
/**
|
||||
* Synchronous thread creation
|
||||
* pthread_create will not return until thread worker actually starts
|
||||
*/
|
||||
waitThreadStart: typeof window === 'undefined' ? 1000 : false,
|
||||
|
||||
onCreateWorker: () => {
|
||||
return new Worker('./worker.js', {
|
||||
execArgv: ['--experimental-wasi-unstable-preview1']
|
||||
})
|
||||
}
|
||||
})
|
||||
const memory = new WebAssembly.Memory({
|
||||
initial: 16777216 / 65536,
|
||||
maximum: 2147483648 / 65536,
|
||||
shared: true
|
||||
})
|
||||
let input
|
||||
const file = 'path/to/your/wasi-module.wasm'
|
||||
try {
|
||||
input = require('fs').readFileSync(require('path').join(__dirname, file))
|
||||
} catch (err) {
|
||||
const response = await fetch(file)
|
||||
input = await response.arrayBuffer()
|
||||
}
|
||||
let { module, instance } = await WebAssembly.instantiate(input, {
|
||||
env: { memory },
|
||||
wasi_snapshot_preview1: wasi.wasiImport,
|
||||
...wasiThreads.getImportObject()
|
||||
})
|
||||
|
||||
wasiThreads.setup(instance, module, memory)
|
||||
await wasiThreads.preloadWorkers()
|
||||
|
||||
if (typeof instance.exports._start === 'function') {
|
||||
return wasi.start(instance)
|
||||
} else {
|
||||
wasi.initialize(instance)
|
||||
// instance.exports.exported_wasm_function()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
`worker.js`
|
||||
|
||||
```js
|
||||
(function (main) {
|
||||
const ENVIRONMENT_IS_NODE =
|
||||
typeof process === 'object' && process !== null &&
|
||||
typeof process.versions === 'object' && process.versions !== null &&
|
||||
typeof process.versions.node === 'string'
|
||||
|
||||
if (ENVIRONMENT_IS_NODE) {
|
||||
const _require = function (request) {
|
||||
return require(request)
|
||||
}
|
||||
|
||||
const _init = function () {
|
||||
const nodeWorkerThreads = require('worker_threads')
|
||||
const parentPort = nodeWorkerThreads.parentPort
|
||||
|
||||
parentPort.on('message', (data) => {
|
||||
globalThis.onmessage({ data })
|
||||
})
|
||||
|
||||
Object.assign(globalThis, {
|
||||
self: globalThis,
|
||||
require,
|
||||
Worker: nodeWorkerThreads.Worker,
|
||||
importScripts: function (f) {
|
||||
(0, eval)(require('fs').readFileSync(f, 'utf8') + '//# sourceURL=' + f)
|
||||
},
|
||||
postMessage: function (msg) {
|
||||
parentPort.postMessage(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
main(_require, _init)
|
||||
} else {
|
||||
importScripts('./node_modules/@tybys/wasm-util/dist/wasm-util.js')
|
||||
importScripts('./node_modules/@emnapi/wasi-threads/dist/wasi-threads.js')
|
||||
|
||||
const nodeWasi = { WASI: globalThis.wasmUtil.WASI }
|
||||
const _require = function (request) {
|
||||
if (request === '@emnapi/wasi-threads') return globalThis.wasiThreads
|
||||
if (request === 'node:wasi' || request === 'wasi') return nodeWasi
|
||||
throw new Error('Can not find module: ' + request)
|
||||
}
|
||||
const _init = function () {}
|
||||
main(_require, _init)
|
||||
}
|
||||
})(function main (require, init) {
|
||||
init()
|
||||
|
||||
const { WASI } = require('wasi')
|
||||
const { ThreadMessageHandler, WASIThreads } = require('@emnapi/wasi-threads')
|
||||
|
||||
const handler = new ThreadMessageHandler({
|
||||
async onLoad ({ wasmModule, wasmMemory }) {
|
||||
const wasi = new WASI({
|
||||
version: 'preview1'
|
||||
})
|
||||
|
||||
const wasiThreads = new WASIThreads({
|
||||
wasi,
|
||||
childThread: true
|
||||
})
|
||||
|
||||
const originalInstance = await WebAssembly.instantiate(wasmModule, {
|
||||
env: {
|
||||
memory: wasmMemory,
|
||||
},
|
||||
wasi_snapshot_preview1: wasi.wasiImport,
|
||||
...wasiThreads.getImportObject()
|
||||
})
|
||||
|
||||
// must call `initialize` instead of `start` in child thread
|
||||
const instance = wasiThreads.initialize(originalInstance, wasmModule, wasmMemory)
|
||||
|
||||
return { module: wasmModule, instance }
|
||||
}
|
||||
})
|
||||
|
||||
globalThis.onmessage = function (e) {
|
||||
handler.handle(e)
|
||||
// handle other messages
|
||||
}
|
||||
})
|
||||
```
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
import type { Worker as Worker_2 } from 'worker_threads';
|
||||
|
||||
export declare interface BaseOptions {
|
||||
wasi: WASIInstance;
|
||||
version?: 'preview1';
|
||||
wasm64?: boolean;
|
||||
}
|
||||
|
||||
export declare interface ChildThreadOptions extends BaseOptions {
|
||||
childThread: true;
|
||||
postMessage?: (data: any) => void;
|
||||
}
|
||||
|
||||
export declare interface CleanupThreadPayload {
|
||||
tid: number;
|
||||
}
|
||||
|
||||
export declare interface CommandInfo<T extends CommandType> {
|
||||
type: T;
|
||||
payload: CommandPayloadMap[T];
|
||||
}
|
||||
|
||||
export declare interface CommandPayloadMap {
|
||||
load: LoadPayload;
|
||||
loaded: LoadedPayload;
|
||||
start: StartPayload;
|
||||
'cleanup-thread': CleanupThreadPayload;
|
||||
'terminate-all-threads': TerminateAllThreadsPayload;
|
||||
'spawn-thread': SpawnThreadPayload;
|
||||
}
|
||||
|
||||
export declare type CommandType = keyof CommandPayloadMap;
|
||||
|
||||
export declare function createInstanceProxy(instance: WebAssembly.Instance, memory?: WebAssembly.Memory | (() => WebAssembly.Memory)): WebAssembly.Instance;
|
||||
|
||||
export declare function isSharedArrayBuffer(value: any): value is SharedArrayBuffer;
|
||||
|
||||
export declare function isTrapError(e: Error): e is WebAssembly.RuntimeError;
|
||||
|
||||
export declare interface LoadedPayload {
|
||||
}
|
||||
|
||||
export declare interface LoadPayload {
|
||||
wasmModule: WebAssembly.Module;
|
||||
wasmMemory: WebAssembly.Memory;
|
||||
sab?: Int32Array;
|
||||
}
|
||||
|
||||
export declare interface MainThreadBaseOptions extends BaseOptions {
|
||||
waitThreadStart?: boolean | number;
|
||||
}
|
||||
|
||||
export declare type MainThreadOptions = MainThreadOptionsWithThreadManager | MainThreadOptionsCreateThreadManager;
|
||||
|
||||
export declare interface MainThreadOptionsCreateThreadManager extends MainThreadBaseOptions, ThreadManagerOptionsMain {
|
||||
}
|
||||
|
||||
export declare interface MainThreadOptionsWithThreadManager extends MainThreadBaseOptions {
|
||||
threadManager?: ThreadManager | (() => ThreadManager);
|
||||
}
|
||||
|
||||
export declare interface MessageEventData<T extends CommandType> {
|
||||
__emnapi__: CommandInfo<T>;
|
||||
}
|
||||
|
||||
export declare interface ReuseWorkerOptions {
|
||||
size: number;
|
||||
strict?: boolean;
|
||||
}
|
||||
|
||||
export declare interface SpawnThreadPayload {
|
||||
startArg: number;
|
||||
errorOrTid: number;
|
||||
}
|
||||
|
||||
export declare interface StartPayload {
|
||||
tid: number;
|
||||
arg: number;
|
||||
sab?: Int32Array;
|
||||
}
|
||||
|
||||
export declare interface StartResult {
|
||||
exitCode: number;
|
||||
instance: WebAssembly.Instance;
|
||||
}
|
||||
|
||||
export declare interface TerminateAllThreadsPayload {
|
||||
}
|
||||
|
||||
export declare class ThreadManager {
|
||||
unusedWorkers: WorkerLike[];
|
||||
pthreads: Record<number, WorkerLike>;
|
||||
get nextWorkerID(): number;
|
||||
wasmModule: WebAssembly.Module | null;
|
||||
wasmMemory: WebAssembly.Memory | null;
|
||||
private readonly messageEvents;
|
||||
private readonly _childThread;
|
||||
private readonly _onCreateWorker;
|
||||
private readonly _reuseWorker;
|
||||
private readonly _beforeLoad?;
|
||||
readonly printErr: (message: string) => void;
|
||||
threadSpawn?: ((startArg: number, errorOrTid?: number) => number);
|
||||
constructor(options: ThreadManagerOptions);
|
||||
init(): void;
|
||||
initMainThread(): void;
|
||||
private preparePool;
|
||||
shouldPreloadWorkers(): boolean;
|
||||
loadWasmModuleToAllWorkers(): Promise<WorkerLike[]>;
|
||||
preloadWorkers(): Promise<WorkerLike[]>;
|
||||
setup(wasmModule: WebAssembly.Module, wasmMemory: WebAssembly.Memory): void;
|
||||
markId(worker: WorkerLike): number;
|
||||
returnWorkerToPool(worker: WorkerLike): void;
|
||||
loadWasmModuleToWorker(worker: WorkerLike, sab?: Int32Array): Promise<WorkerLike>;
|
||||
allocateUnusedWorker(): WorkerLike;
|
||||
getNewWorker(sab?: Int32Array): WorkerLike | undefined;
|
||||
cleanThread(worker: WorkerLike, tid: number, force?: boolean): void;
|
||||
terminateWorker(worker: WorkerLike): void;
|
||||
terminateAllThreads(): void;
|
||||
addMessageEventListener(worker: WorkerLike, onMessage: (e: WorkerMessageEvent) => void): () => void;
|
||||
fireMessageEvent(worker: WorkerLike, e: WorkerMessageEvent): void;
|
||||
}
|
||||
|
||||
export declare type ThreadManagerOptions = ThreadManagerOptionsMain | ThreadManagerOptionsChild;
|
||||
|
||||
export declare interface ThreadManagerOptionsBase {
|
||||
printErr?: (message: string) => void;
|
||||
threadSpawn?: (startArg: number, errorOrTid?: number) => number;
|
||||
}
|
||||
|
||||
export declare interface ThreadManagerOptionsChild extends ThreadManagerOptionsBase {
|
||||
childThread: true;
|
||||
}
|
||||
|
||||
export declare interface ThreadManagerOptionsMain extends ThreadManagerOptionsBase {
|
||||
beforeLoad?: (worker: WorkerLike) => any;
|
||||
reuseWorker?: boolean | number | ReuseWorkerOptions;
|
||||
onCreateWorker: WorkerFactory;
|
||||
childThread?: false;
|
||||
}
|
||||
|
||||
export declare class ThreadMessageHandler {
|
||||
protected instance: WebAssembly.Instance | undefined;
|
||||
private messagesBeforeLoad;
|
||||
protected postMessage: (message: any) => void;
|
||||
protected onLoad?: (data: LoadPayload) => WebAssembly.WebAssemblyInstantiatedSource | PromiseLike<WebAssembly.WebAssemblyInstantiatedSource>;
|
||||
protected onError: (error: Error, type: WorkerMessageType) => void;
|
||||
constructor(options?: ThreadMessageHandlerOptions);
|
||||
instantiate(data: LoadPayload): WebAssembly.WebAssemblyInstantiatedSource | PromiseLike<WebAssembly.WebAssemblyInstantiatedSource>;
|
||||
handle(e: WorkerMessageEvent<MessageEventData<WorkerMessageType>>): void;
|
||||
private _load;
|
||||
private _start;
|
||||
protected _loaded(err: Error | null, source: WebAssembly.WebAssemblyInstantiatedSource | null, payload: LoadPayload): void;
|
||||
protected handleAfterLoad<E extends WorkerMessageEvent>(e: E, f: (e: E) => void): void;
|
||||
}
|
||||
|
||||
export declare interface ThreadMessageHandlerOptions {
|
||||
onLoad?: (data: LoadPayload) => WebAssembly.WebAssemblyInstantiatedSource | PromiseLike<WebAssembly.WebAssemblyInstantiatedSource>;
|
||||
onError?: (error: Error, type: WorkerMessageType) => void;
|
||||
postMessage?: (message: any) => void;
|
||||
}
|
||||
|
||||
export declare interface WASIInstance {
|
||||
readonly wasiImport?: Record<string, any>;
|
||||
initialize(instance: object): void;
|
||||
start(instance: object): number;
|
||||
getImportObject?(): any;
|
||||
}
|
||||
|
||||
export declare class WASIThreads {
|
||||
PThread: ThreadManager | undefined;
|
||||
private wasmMemory;
|
||||
private wasmInstance;
|
||||
private readonly threadSpawn;
|
||||
readonly childThread: boolean;
|
||||
private readonly postMessage;
|
||||
readonly wasi: WASIInstance;
|
||||
constructor(options: WASIThreadsOptions);
|
||||
getImportObject(): {
|
||||
wasi: WASIThreadsImports;
|
||||
};
|
||||
setup(wasmInstance: WebAssembly.Instance, wasmModule: WebAssembly.Module, wasmMemory?: WebAssembly.Memory): void;
|
||||
preloadWorkers(): Promise<WorkerLike[]>;
|
||||
initialize(instance: WebAssembly.Instance, module: WebAssembly.Module, memory?: WebAssembly.Memory): WebAssembly.Instance;
|
||||
start(instance: WebAssembly.Instance, module: WebAssembly.Module, memory?: WebAssembly.Memory): StartResult;
|
||||
terminateAllThreads(): void;
|
||||
}
|
||||
|
||||
export declare interface WASIThreadsImports {
|
||||
'thread-spawn': (startArg: number, errorOrTid?: number) => number;
|
||||
}
|
||||
|
||||
export declare type WASIThreadsOptions = MainThreadOptions | ChildThreadOptions;
|
||||
|
||||
export declare type WorkerFactory = (ctx: {
|
||||
type: string;
|
||||
name: string;
|
||||
}) => WorkerLike;
|
||||
|
||||
export declare type WorkerLike = (Worker | Worker_2) & {
|
||||
whenLoaded?: Promise<WorkerLike>;
|
||||
loaded?: boolean;
|
||||
__emnapi_tid?: number;
|
||||
};
|
||||
|
||||
export declare interface WorkerMessageEvent<T = any> {
|
||||
data: T;
|
||||
}
|
||||
|
||||
export declare type WorkerMessageType = 'load' | 'start';
|
||||
|
||||
export { }
|
||||
+918
@@ -0,0 +1,918 @@
|
||||
//#region src/util.ts
|
||||
const _WebAssembly = typeof WebAssembly !== 'undefined'
|
||||
? WebAssembly
|
||||
: typeof WXWebAssembly !== 'undefined'
|
||||
? WXWebAssembly
|
||||
: undefined;
|
||||
const ENVIRONMENT_IS_NODE = typeof process === 'object' && process !== null &&
|
||||
typeof process.versions === 'object' && process.versions !== null &&
|
||||
typeof process.versions.node === 'string';
|
||||
function getPostMessage(options) {
|
||||
return typeof options?.postMessage === 'function'
|
||||
? options.postMessage
|
||||
: typeof postMessage === 'function'
|
||||
? postMessage
|
||||
: undefined;
|
||||
}
|
||||
function serizeErrorToBuffer(sab, code, error) {
|
||||
const i32array = new Int32Array(sab);
|
||||
Atomics.store(i32array, 0, code);
|
||||
if (code > 1 && error) {
|
||||
const name = error.name;
|
||||
const message = error.message;
|
||||
const stack = error.stack;
|
||||
const nameBuffer = new TextEncoder().encode(name);
|
||||
const messageBuffer = new TextEncoder().encode(message);
|
||||
const stackBuffer = new TextEncoder().encode(stack);
|
||||
Atomics.store(i32array, 1, nameBuffer.length);
|
||||
Atomics.store(i32array, 2, messageBuffer.length);
|
||||
Atomics.store(i32array, 3, stackBuffer.length);
|
||||
const buffer = new Uint8Array(sab);
|
||||
buffer.set(nameBuffer, 16);
|
||||
buffer.set(messageBuffer, 16 + nameBuffer.length);
|
||||
buffer.set(stackBuffer, 16 + nameBuffer.length + messageBuffer.length);
|
||||
}
|
||||
}
|
||||
function deserizeErrorFromBuffer(sab) {
|
||||
const i32array = new Int32Array(sab);
|
||||
const status = Atomics.load(i32array, 0);
|
||||
if (status <= 1) {
|
||||
return null;
|
||||
}
|
||||
const nameLength = Atomics.load(i32array, 1);
|
||||
const messageLength = Atomics.load(i32array, 2);
|
||||
const stackLength = Atomics.load(i32array, 3);
|
||||
const buffer = new Uint8Array(sab);
|
||||
const nameBuffer = buffer.slice(16, 16 + nameLength);
|
||||
const messageBuffer = buffer.slice(16 + nameLength, 16 + nameLength + messageLength);
|
||||
const stackBuffer = buffer.slice(16 + nameLength + messageLength, 16 + nameLength + messageLength + stackLength);
|
||||
const name = new TextDecoder().decode(nameBuffer);
|
||||
const message = new TextDecoder().decode(messageBuffer);
|
||||
const stack = new TextDecoder().decode(stackBuffer);
|
||||
const ErrorConstructor = globalThis[name] ?? (name === 'RuntimeError' ? (_WebAssembly.RuntimeError ?? Error) : Error);
|
||||
const error = new ErrorConstructor(message);
|
||||
Object.defineProperty(error, 'stack', {
|
||||
value: stack,
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
return error;
|
||||
}
|
||||
function isSharedArrayBuffer(value) {
|
||||
return ((typeof SharedArrayBuffer === 'function' && value instanceof SharedArrayBuffer) ||
|
||||
(Object.prototype.toString.call(value) === '[object SharedArrayBuffer]'));
|
||||
}
|
||||
function isTrapError(e) {
|
||||
try {
|
||||
return e instanceof _WebAssembly.RuntimeError;
|
||||
}
|
||||
catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion src/util.ts
|
||||
|
||||
//#region src/command.ts
|
||||
function createMessage(type, payload) {
|
||||
return {
|
||||
__emnapi__: {
|
||||
type,
|
||||
payload
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//#endregion src/command.ts
|
||||
|
||||
//#region src/thread-manager.ts
|
||||
const WASI_THREADS_MAX_TID = 0x1FFFFFFF;
|
||||
function checkSharedWasmMemory(wasmMemory) {
|
||||
if (wasmMemory) {
|
||||
if (!isSharedArrayBuffer(wasmMemory.buffer)) {
|
||||
throw new Error('Multithread features require shared wasm memory. ' +
|
||||
'Try to compile with `-matomics -mbulk-memory` and use `--import-memory --shared-memory` during linking, ' +
|
||||
'then create WebAssembly.Memory with `shared: true` option');
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (typeof SharedArrayBuffer === 'undefined') {
|
||||
throw new Error('Current environment does not support SharedArrayBuffer, threads are not available!');
|
||||
}
|
||||
}
|
||||
}
|
||||
function getReuseWorker(value) {
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? { size: 0, strict: false } : false;
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
if (!(value >= 0)) {
|
||||
throw new RangeError('reuseWorker: size must be a non-negative integer');
|
||||
}
|
||||
return { size: value, strict: false };
|
||||
}
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
const size = Number(value.size) || 0;
|
||||
const strict = Boolean(value.strict);
|
||||
if (!(size > 0) && strict) {
|
||||
throw new RangeError('reuseWorker: size must be set to positive integer if strict is set to true');
|
||||
}
|
||||
return { size, strict };
|
||||
}
|
||||
let nextWorkerID = 0;
|
||||
class ThreadManager {
|
||||
get nextWorkerID() { return nextWorkerID; }
|
||||
constructor(options) {
|
||||
this.unusedWorkers = [];
|
||||
this.pthreads = Object.create(null);
|
||||
this.wasmModule = null;
|
||||
this.wasmMemory = null;
|
||||
this.messageEvents = new WeakMap();
|
||||
if (!options) {
|
||||
throw new TypeError('ThreadManager(): options is not provided');
|
||||
}
|
||||
if ('childThread' in options) {
|
||||
this._childThread = Boolean(options.childThread);
|
||||
}
|
||||
else {
|
||||
this._childThread = false;
|
||||
}
|
||||
if (this._childThread) {
|
||||
this._onCreateWorker = undefined;
|
||||
this._reuseWorker = false;
|
||||
this._beforeLoad = undefined;
|
||||
}
|
||||
else {
|
||||
this._onCreateWorker = options.onCreateWorker;
|
||||
this._reuseWorker = getReuseWorker(options.reuseWorker);
|
||||
this._beforeLoad = options.beforeLoad;
|
||||
}
|
||||
this.printErr = options.printErr ?? console.error.bind(console);
|
||||
this.threadSpawn = options.threadSpawn;
|
||||
}
|
||||
init() {
|
||||
if (!this._childThread) {
|
||||
this.initMainThread();
|
||||
}
|
||||
}
|
||||
initMainThread() {
|
||||
this.preparePool();
|
||||
}
|
||||
preparePool() {
|
||||
if (this._reuseWorker) {
|
||||
if (this._reuseWorker.size) {
|
||||
let pthreadPoolSize = this._reuseWorker.size;
|
||||
while (pthreadPoolSize--) {
|
||||
const worker = this.allocateUnusedWorker();
|
||||
if (ENVIRONMENT_IS_NODE) {
|
||||
worker.once('message', () => { });
|
||||
worker.unref();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
shouldPreloadWorkers() {
|
||||
return !this._childThread && this._reuseWorker && this._reuseWorker.size > 0;
|
||||
}
|
||||
loadWasmModuleToAllWorkers() {
|
||||
const promises = Array(this.unusedWorkers.length);
|
||||
for (let i = 0; i < this.unusedWorkers.length; ++i) {
|
||||
const worker = this.unusedWorkers[i];
|
||||
if (ENVIRONMENT_IS_NODE)
|
||||
worker.ref();
|
||||
promises[i] = this.loadWasmModuleToWorker(worker).then((w) => {
|
||||
if (ENVIRONMENT_IS_NODE)
|
||||
worker.unref();
|
||||
return w;
|
||||
}, (e) => {
|
||||
if (ENVIRONMENT_IS_NODE)
|
||||
worker.unref();
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
return Promise.all(promises).catch((err) => {
|
||||
this.terminateAllThreads();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
preloadWorkers() {
|
||||
if (this.shouldPreloadWorkers()) {
|
||||
return this.loadWasmModuleToAllWorkers();
|
||||
}
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
setup(wasmModule, wasmMemory) {
|
||||
this.wasmModule = wasmModule;
|
||||
this.wasmMemory = wasmMemory;
|
||||
}
|
||||
markId(worker) {
|
||||
if (worker.__emnapi_tid)
|
||||
return worker.__emnapi_tid;
|
||||
const tid = nextWorkerID + 43;
|
||||
nextWorkerID = (nextWorkerID + 1) % (WASI_THREADS_MAX_TID - 42);
|
||||
this.pthreads[tid] = worker;
|
||||
worker.__emnapi_tid = tid;
|
||||
return tid;
|
||||
}
|
||||
returnWorkerToPool(worker) {
|
||||
var tid = worker.__emnapi_tid;
|
||||
if (tid !== undefined) {
|
||||
delete this.pthreads[tid];
|
||||
}
|
||||
this.unusedWorkers.push(worker);
|
||||
delete worker.__emnapi_tid;
|
||||
if (ENVIRONMENT_IS_NODE) {
|
||||
worker.unref();
|
||||
}
|
||||
}
|
||||
loadWasmModuleToWorker(worker, sab) {
|
||||
if (worker.whenLoaded)
|
||||
return worker.whenLoaded;
|
||||
const err = this.printErr;
|
||||
const beforeLoad = this._beforeLoad;
|
||||
const _this = this;
|
||||
worker.whenLoaded = new Promise((resolve, reject) => {
|
||||
const handleError = function (e) {
|
||||
let message = 'worker sent an error!';
|
||||
if (worker.__emnapi_tid !== undefined) {
|
||||
message = 'worker (tid = ' + worker.__emnapi_tid + ') sent an error!';
|
||||
}
|
||||
if ('message' in e) {
|
||||
err(message + ' ' + e.message);
|
||||
if (e.message.indexOf('RuntimeError') !== -1 || e.message.indexOf('unreachable') !== -1) {
|
||||
try {
|
||||
_this.terminateAllThreads();
|
||||
}
|
||||
catch (_) { }
|
||||
}
|
||||
}
|
||||
else {
|
||||
err(message);
|
||||
}
|
||||
reject(e);
|
||||
throw e;
|
||||
};
|
||||
const handleMessage = (data) => {
|
||||
if (data.__emnapi__) {
|
||||
const type = data.__emnapi__.type;
|
||||
const payload = data.__emnapi__.payload;
|
||||
if (type === 'loaded') {
|
||||
worker.loaded = true;
|
||||
if (ENVIRONMENT_IS_NODE && !worker.__emnapi_tid) {
|
||||
worker.unref();
|
||||
}
|
||||
resolve(worker);
|
||||
}
|
||||
else if (type === 'cleanup-thread') {
|
||||
if (payload.tid in this.pthreads) {
|
||||
this.cleanThread(worker, payload.tid);
|
||||
}
|
||||
}
|
||||
else if (type === 'spawn-thread') {
|
||||
this.threadSpawn(payload.startArg, payload.errorOrTid);
|
||||
}
|
||||
else if (type === 'terminate-all-threads') {
|
||||
this.terminateAllThreads();
|
||||
}
|
||||
}
|
||||
};
|
||||
worker.onmessage = (e) => {
|
||||
handleMessage(e.data);
|
||||
this.fireMessageEvent(worker, e);
|
||||
};
|
||||
worker.onerror = handleError;
|
||||
if (ENVIRONMENT_IS_NODE) {
|
||||
worker.on('message', function (data) {
|
||||
worker.onmessage?.({
|
||||
data
|
||||
});
|
||||
});
|
||||
worker.on('error', function (e) {
|
||||
worker.onerror?.(e);
|
||||
});
|
||||
worker.on('detachedExit', function () { });
|
||||
}
|
||||
if (typeof beforeLoad === 'function') {
|
||||
beforeLoad(worker);
|
||||
}
|
||||
try {
|
||||
worker.postMessage(createMessage('load', {
|
||||
wasmModule: this.wasmModule,
|
||||
wasmMemory: this.wasmMemory,
|
||||
sab
|
||||
}));
|
||||
}
|
||||
catch (err) {
|
||||
checkSharedWasmMemory(this.wasmMemory);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
return worker.whenLoaded;
|
||||
}
|
||||
allocateUnusedWorker() {
|
||||
const _onCreateWorker = this._onCreateWorker;
|
||||
if (typeof _onCreateWorker !== 'function') {
|
||||
throw new TypeError('`options.onCreateWorker` is not provided');
|
||||
}
|
||||
const worker = _onCreateWorker({ type: 'thread', name: 'emnapi-pthread' });
|
||||
this.unusedWorkers.push(worker);
|
||||
return worker;
|
||||
}
|
||||
getNewWorker(sab) {
|
||||
if (this._reuseWorker) {
|
||||
if (this.unusedWorkers.length === 0) {
|
||||
if (this._reuseWorker.strict) {
|
||||
if (!ENVIRONMENT_IS_NODE) {
|
||||
const err = this.printErr;
|
||||
err('Tried to spawn a new thread, but the thread pool is exhausted.\n' +
|
||||
'This might result in a deadlock unless some threads eventually exit or the code explicitly breaks out to the event loop.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
const worker = this.allocateUnusedWorker();
|
||||
this.loadWasmModuleToWorker(worker, sab);
|
||||
}
|
||||
return this.unusedWorkers.pop();
|
||||
}
|
||||
const worker = this.allocateUnusedWorker();
|
||||
this.loadWasmModuleToWorker(worker, sab);
|
||||
return this.unusedWorkers.pop();
|
||||
}
|
||||
cleanThread(worker, tid, force) {
|
||||
if (!force && this._reuseWorker) {
|
||||
this.returnWorkerToPool(worker);
|
||||
}
|
||||
else {
|
||||
delete this.pthreads[tid];
|
||||
this.terminateWorker(worker);
|
||||
delete worker.__emnapi_tid;
|
||||
}
|
||||
}
|
||||
terminateWorker(worker) {
|
||||
const tid = worker.__emnapi_tid;
|
||||
worker.terminate();
|
||||
this.messageEvents.get(worker)?.clear();
|
||||
this.messageEvents.delete(worker);
|
||||
worker.onmessage = (e) => {
|
||||
if (e.data.__emnapi__) {
|
||||
const err = this.printErr;
|
||||
err('received "' + e.data.__emnapi__.type + '" command from terminated worker: ' + tid);
|
||||
}
|
||||
};
|
||||
}
|
||||
terminateAllThreads() {
|
||||
const runningWorkers = Object.values(this.pthreads);
|
||||
for (let i = 0; i < runningWorkers.length; ++i) {
|
||||
this.terminateWorker(runningWorkers[i]);
|
||||
}
|
||||
for (let i = 0; i < this.unusedWorkers.length; ++i) {
|
||||
this.terminateWorker(this.unusedWorkers[i]);
|
||||
}
|
||||
this.unusedWorkers = [];
|
||||
this.pthreads = Object.create(null);
|
||||
this.preparePool();
|
||||
}
|
||||
addMessageEventListener(worker, onMessage) {
|
||||
let listeners = this.messageEvents.get(worker);
|
||||
if (!listeners) {
|
||||
listeners = new Set();
|
||||
this.messageEvents.set(worker, listeners);
|
||||
}
|
||||
listeners.add(onMessage);
|
||||
return () => {
|
||||
listeners?.delete(onMessage);
|
||||
};
|
||||
}
|
||||
fireMessageEvent(worker, e) {
|
||||
const listeners = this.messageEvents.get(worker);
|
||||
if (!listeners)
|
||||
return;
|
||||
const err = this.printErr;
|
||||
listeners.forEach((listener) => {
|
||||
try {
|
||||
listener(e);
|
||||
}
|
||||
catch (e) {
|
||||
err(e.stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion src/thread-manager.ts
|
||||
|
||||
//#region src/proxy.ts
|
||||
const kIsProxy = Symbol('kIsProxy');
|
||||
function createInstanceProxy(instance, memory) {
|
||||
if (instance[kIsProxy])
|
||||
return instance;
|
||||
const originalExports = instance.exports;
|
||||
const createHandler = function (target) {
|
||||
const handlers = [
|
||||
'apply',
|
||||
'construct',
|
||||
'defineProperty',
|
||||
'deleteProperty',
|
||||
'get',
|
||||
'getOwnPropertyDescriptor',
|
||||
'getPrototypeOf',
|
||||
'has',
|
||||
'isExtensible',
|
||||
'ownKeys',
|
||||
'preventExtensions',
|
||||
'set',
|
||||
'setPrototypeOf'
|
||||
];
|
||||
const handler = {};
|
||||
for (let i = 0; i < handlers.length; i++) {
|
||||
const name = handlers[i];
|
||||
handler[name] = function () {
|
||||
const args = Array.prototype.slice.call(arguments, 1);
|
||||
args.unshift(target);
|
||||
return Reflect[name].apply(Reflect, args);
|
||||
};
|
||||
}
|
||||
return handler;
|
||||
};
|
||||
const handler = createHandler(originalExports);
|
||||
const _initialize = () => { };
|
||||
const _start = () => 0;
|
||||
handler.get = function (_target, p, receiver) {
|
||||
if (p === 'memory') {
|
||||
return (typeof memory === 'function' ? memory() : memory) ?? Reflect.get(originalExports, p, receiver);
|
||||
}
|
||||
if (p === '_initialize') {
|
||||
return p in originalExports ? _initialize : undefined;
|
||||
}
|
||||
if (p === '_start') {
|
||||
return p in originalExports ? _start : undefined;
|
||||
}
|
||||
return Reflect.get(originalExports, p, receiver);
|
||||
};
|
||||
handler.has = function (_target, p) {
|
||||
if (p === 'memory')
|
||||
return true;
|
||||
return Reflect.has(originalExports, p);
|
||||
};
|
||||
const exportsProxy = new Proxy(Object.create(null), handler);
|
||||
return new Proxy(instance, {
|
||||
get(target, p, receiver) {
|
||||
if (p === 'exports') {
|
||||
return exportsProxy;
|
||||
}
|
||||
if (p === kIsProxy) {
|
||||
return true;
|
||||
}
|
||||
return Reflect.get(target, p, receiver);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//#endregion src/proxy.ts
|
||||
|
||||
//#region src/wasi-threads.ts
|
||||
const patchedWasiInstances = new WeakMap();
|
||||
const THREAD_SPAWN_RESULT_SIZE = Int32Array.BYTES_PER_ELEMENT * 2;
|
||||
const sharedArrayBufferByteLength = typeof SharedArrayBuffer === 'function'
|
||||
? Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'byteLength').get
|
||||
: undefined;
|
||||
function isConclusivelySharedArrayBuffer(buffer) {
|
||||
if (sharedArrayBufferByteLength === undefined)
|
||||
return false;
|
||||
try {
|
||||
sharedArrayBufferByteLength.call(buffer);
|
||||
return true;
|
||||
}
|
||||
catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function getThreadSpawnResultView(memory, address, wasm64) {
|
||||
const offset = typeof address === 'bigint' ? Number(address) : address >>> 0;
|
||||
let buffer = memory.buffer;
|
||||
if (offset + THREAD_SPAWN_RESULT_SIZE > buffer.byteLength &&
|
||||
isConclusivelySharedArrayBuffer(buffer)) {
|
||||
if (wasm64) {
|
||||
memory.grow(BigInt(0));
|
||||
}
|
||||
else {
|
||||
memory.grow(0);
|
||||
}
|
||||
buffer = memory.buffer;
|
||||
}
|
||||
return new Int32Array(buffer, offset, 2);
|
||||
}
|
||||
class WASIThreads {
|
||||
constructor(options) {
|
||||
if (!options) {
|
||||
throw new TypeError('WASIThreads(): options is not provided');
|
||||
}
|
||||
if (!options.wasi) {
|
||||
throw new TypeError('WASIThreads(): options.wasi is not provided');
|
||||
}
|
||||
patchedWasiInstances.set(this, new WeakSet());
|
||||
const wasi = options.wasi;
|
||||
patchWasiInstance(this, wasi);
|
||||
this.wasi = wasi;
|
||||
if ('childThread' in options) {
|
||||
this.childThread = Boolean(options.childThread);
|
||||
}
|
||||
else {
|
||||
this.childThread = false;
|
||||
}
|
||||
this.PThread = undefined;
|
||||
if ('threadManager' in options) {
|
||||
if (typeof options.threadManager === 'function') {
|
||||
this.PThread = options.threadManager();
|
||||
}
|
||||
else {
|
||||
this.PThread = options.threadManager;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!this.childThread) {
|
||||
this.PThread = new ThreadManager(options);
|
||||
this.PThread.init();
|
||||
}
|
||||
}
|
||||
let waitThreadStart = false;
|
||||
if ('waitThreadStart' in options) {
|
||||
waitThreadStart = typeof options.waitThreadStart === 'number' ? options.waitThreadStart : Boolean(options.waitThreadStart);
|
||||
}
|
||||
const postMessage = getPostMessage(options);
|
||||
if (this.childThread && typeof postMessage !== 'function') {
|
||||
throw new TypeError('options.postMessage is not a function');
|
||||
}
|
||||
this.postMessage = postMessage;
|
||||
const wasm64 = Boolean(options.wasm64);
|
||||
const threadSpawn = (startArg, errorOrTid) => {
|
||||
const EAGAIN = 6;
|
||||
const isNewABI = errorOrTid !== undefined;
|
||||
try {
|
||||
checkSharedWasmMemory(this.wasmMemory);
|
||||
}
|
||||
catch (err) {
|
||||
this.PThread?.printErr(err.stack);
|
||||
if (isNewABI) {
|
||||
const struct = getThreadSpawnResultView(this.wasmMemory, errorOrTid, wasm64);
|
||||
Atomics.store(struct, 0, 1);
|
||||
Atomics.store(struct, 1, EAGAIN);
|
||||
Atomics.notify(struct, 1);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
if (!isNewABI) {
|
||||
const malloc = this.wasmInstance.exports.malloc;
|
||||
errorOrTid = wasm64 ? Number(malloc(BigInt(8))) : (malloc(8) >>> 0);
|
||||
if (!errorOrTid) {
|
||||
return -48;
|
||||
}
|
||||
}
|
||||
const _free = this.wasmInstance.exports.free;
|
||||
const free = wasm64 ? (ptr) => { _free(BigInt(ptr)); } : _free;
|
||||
const struct = getThreadSpawnResultView(this.wasmMemory, errorOrTid, wasm64);
|
||||
Atomics.store(struct, 0, 0);
|
||||
Atomics.store(struct, 1, 0);
|
||||
if (this.childThread) {
|
||||
postMessage(createMessage('spawn-thread', {
|
||||
startArg,
|
||||
errorOrTid: errorOrTid
|
||||
}));
|
||||
Atomics.wait(struct, 1, 0);
|
||||
const isError = Atomics.load(struct, 0);
|
||||
const result = Atomics.load(struct, 1);
|
||||
if (isNewABI) {
|
||||
return isError;
|
||||
}
|
||||
free(errorOrTid);
|
||||
return isError ? -result : result;
|
||||
}
|
||||
const shouldWait = waitThreadStart || (waitThreadStart === 0);
|
||||
let sab;
|
||||
if (shouldWait) {
|
||||
sab = new Int32Array(new SharedArrayBuffer(16 + 8192));
|
||||
Atomics.store(sab, 0, 0);
|
||||
}
|
||||
let worker;
|
||||
let tid;
|
||||
const PThread = this.PThread;
|
||||
try {
|
||||
worker = PThread.getNewWorker(sab);
|
||||
if (!worker) {
|
||||
throw new Error('failed to get new worker');
|
||||
}
|
||||
tid = PThread.markId(worker);
|
||||
if (ENVIRONMENT_IS_NODE) {
|
||||
worker.unref();
|
||||
}
|
||||
worker.postMessage(createMessage('start', {
|
||||
tid,
|
||||
arg: startArg,
|
||||
sab
|
||||
}));
|
||||
if (shouldWait) {
|
||||
if (typeof waitThreadStart === 'number') {
|
||||
const waitResult = Atomics.wait(sab, 0, 0, waitThreadStart);
|
||||
if (waitResult === 'timed-out') {
|
||||
throw new Error('Spawning thread timed out. Please check if the worker is created successfully and if message is handled properly in the worker.');
|
||||
}
|
||||
}
|
||||
else {
|
||||
Atomics.wait(sab, 0, 0);
|
||||
}
|
||||
const r = Atomics.load(sab, 0);
|
||||
if (r > 1) {
|
||||
throw deserizeErrorFromBuffer(sab.buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (worker !== undefined && tid !== undefined) {
|
||||
try {
|
||||
PThread.cleanThread(worker, tid, true);
|
||||
}
|
||||
catch (_) { }
|
||||
}
|
||||
Atomics.store(struct, 0, 1);
|
||||
Atomics.store(struct, 1, EAGAIN);
|
||||
Atomics.notify(struct, 1);
|
||||
PThread?.printErr(e.stack);
|
||||
if (isNewABI) {
|
||||
return 1;
|
||||
}
|
||||
free(errorOrTid);
|
||||
return -EAGAIN;
|
||||
}
|
||||
Atomics.store(struct, 0, 0);
|
||||
Atomics.store(struct, 1, tid);
|
||||
Atomics.notify(struct, 1);
|
||||
if (!shouldWait) {
|
||||
worker.whenLoaded.catch((err) => {
|
||||
delete worker.whenLoaded;
|
||||
PThread.cleanThread(worker, tid, true);
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
if (isNewABI) {
|
||||
return 0;
|
||||
}
|
||||
free(errorOrTid);
|
||||
return tid;
|
||||
};
|
||||
this.threadSpawn = threadSpawn;
|
||||
if (this.PThread) {
|
||||
this.PThread.threadSpawn = threadSpawn;
|
||||
}
|
||||
}
|
||||
getImportObject() {
|
||||
return {
|
||||
wasi: {
|
||||
'thread-spawn': this.threadSpawn
|
||||
}
|
||||
};
|
||||
}
|
||||
setup(wasmInstance, wasmModule, wasmMemory) {
|
||||
wasmMemory ??= wasmInstance.exports.memory;
|
||||
this.wasmInstance = wasmInstance;
|
||||
this.wasmMemory = wasmMemory;
|
||||
if (this.PThread) {
|
||||
this.PThread.setup(wasmModule, wasmMemory);
|
||||
}
|
||||
}
|
||||
preloadWorkers() {
|
||||
if (this.PThread) {
|
||||
return this.PThread.preloadWorkers();
|
||||
}
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
initialize(instance, module, memory) {
|
||||
const exports = instance.exports;
|
||||
memory ??= exports.memory;
|
||||
if (this.childThread) {
|
||||
instance = createInstanceProxy(instance, memory);
|
||||
}
|
||||
this.setup(instance, module, memory);
|
||||
const wasi = this.wasi;
|
||||
if (('_start' in exports) && (typeof exports._start === 'function')) {
|
||||
if (this.childThread) {
|
||||
wasi.start(instance);
|
||||
try {
|
||||
const kStarted = getWasiSymbol(wasi, 'kStarted');
|
||||
wasi[kStarted] = false;
|
||||
}
|
||||
catch (_) { }
|
||||
}
|
||||
else {
|
||||
setupInstance(wasi, instance);
|
||||
}
|
||||
}
|
||||
else {
|
||||
wasi.initialize(instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
start(instance, module, memory) {
|
||||
const exports = instance.exports;
|
||||
memory ??= exports.memory;
|
||||
if (this.childThread) {
|
||||
instance = createInstanceProxy(instance, memory);
|
||||
}
|
||||
this.setup(instance, module, memory);
|
||||
const exitCode = this.wasi.start(instance);
|
||||
return { exitCode, instance };
|
||||
}
|
||||
terminateAllThreads() {
|
||||
if (!this.childThread) {
|
||||
this.PThread?.terminateAllThreads();
|
||||
}
|
||||
else {
|
||||
this.postMessage(createMessage('terminate-all-threads', {}));
|
||||
}
|
||||
}
|
||||
}
|
||||
function patchWasiInstance(wasiThreads, wasi) {
|
||||
const patched = patchedWasiInstances.get(wasiThreads);
|
||||
if (patched.has(wasi)) {
|
||||
return;
|
||||
}
|
||||
const _this = wasiThreads;
|
||||
const wasiImport = wasi.wasiImport;
|
||||
if (wasiImport) {
|
||||
const proc_exit = wasiImport.proc_exit;
|
||||
wasiImport.proc_exit = function (code) {
|
||||
_this.terminateAllThreads();
|
||||
return proc_exit.call(this, code);
|
||||
};
|
||||
}
|
||||
if (!_this.childThread) {
|
||||
const start = wasi.start;
|
||||
if (typeof start === 'function') {
|
||||
wasi.start = function (instance) {
|
||||
try {
|
||||
return start.call(this, instance);
|
||||
}
|
||||
catch (err) {
|
||||
if (isTrapError(err)) {
|
||||
_this.terminateAllThreads();
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
patched.add(wasi);
|
||||
}
|
||||
function getWasiSymbol(wasi, description) {
|
||||
const symbols = Object.getOwnPropertySymbols(wasi);
|
||||
const selectDescription = (description) => (s) => {
|
||||
if (s.description) {
|
||||
return s.description === description;
|
||||
}
|
||||
return s.toString() === `Symbol(${description})`;
|
||||
};
|
||||
if (Array.isArray(description)) {
|
||||
return description.map(d => symbols.filter(selectDescription(d))[0]);
|
||||
}
|
||||
return symbols.filter(selectDescription(description))[0];
|
||||
}
|
||||
function setupInstance(wasi, instance) {
|
||||
const [kInstance, kSetMemory] = getWasiSymbol(wasi, ['kInstance', 'kSetMemory']);
|
||||
wasi[kInstance] = instance;
|
||||
wasi[kSetMemory](instance.exports.memory);
|
||||
}
|
||||
|
||||
//#endregion src/wasi-threads.ts
|
||||
|
||||
//#region src/worker.ts
|
||||
class ThreadMessageHandler {
|
||||
constructor(options) {
|
||||
const postMsg = getPostMessage(options);
|
||||
if (typeof postMsg !== 'function') {
|
||||
throw new TypeError('options.postMessage is not a function');
|
||||
}
|
||||
this.postMessage = postMsg;
|
||||
this.onLoad = options?.onLoad;
|
||||
this.onError = typeof options?.onError === 'function' ? options.onError : (_type, err) => { throw err; };
|
||||
this.instance = undefined;
|
||||
this.messagesBeforeLoad = [];
|
||||
}
|
||||
instantiate(data) {
|
||||
if (typeof this.onLoad === 'function') {
|
||||
return this.onLoad(data);
|
||||
}
|
||||
throw new Error('ThreadMessageHandler.prototype.instantiate is not implemented');
|
||||
}
|
||||
handle(e) {
|
||||
if (e?.data?.__emnapi__) {
|
||||
const type = e.data.__emnapi__.type;
|
||||
const payload = e.data.__emnapi__.payload;
|
||||
try {
|
||||
if (type === 'load') {
|
||||
this._load(payload);
|
||||
}
|
||||
else if (type === 'start') {
|
||||
this.handleAfterLoad(e, () => {
|
||||
this._start(payload);
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
this.onError(err, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
_load(payload) {
|
||||
if (this.instance !== undefined)
|
||||
return;
|
||||
let source;
|
||||
try {
|
||||
source = this.instantiate(payload);
|
||||
}
|
||||
catch (err) {
|
||||
this._loaded(err, null, payload);
|
||||
return;
|
||||
}
|
||||
const then = source && 'then' in source ? source.then : undefined;
|
||||
if (typeof then === 'function') {
|
||||
then.call(source, (source) => { this._loaded(null, source, payload); }, (err) => { this._loaded(err, null, payload); });
|
||||
}
|
||||
else {
|
||||
this._loaded(null, source, payload);
|
||||
}
|
||||
}
|
||||
_start(payload) {
|
||||
const wasi_thread_start = this.instance.exports.wasi_thread_start;
|
||||
if (typeof wasi_thread_start !== 'function') {
|
||||
const err = new TypeError('wasi_thread_start is not exported');
|
||||
notifyPthreadCreateResult(payload.sab, 2, err);
|
||||
throw err;
|
||||
}
|
||||
const postMessage = this.postMessage;
|
||||
const tid = payload.tid;
|
||||
const startArg = payload.arg;
|
||||
notifyPthreadCreateResult(payload.sab, 1);
|
||||
try {
|
||||
wasi_thread_start(tid, startArg);
|
||||
}
|
||||
catch (err) {
|
||||
if (err !== 'unwind') {
|
||||
throw err;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
postMessage(createMessage('cleanup-thread', { tid }));
|
||||
}
|
||||
_loaded(err, source, payload) {
|
||||
if (err) {
|
||||
notifyPthreadCreateResult(payload.sab, 2, err);
|
||||
throw err;
|
||||
}
|
||||
if (source == null) {
|
||||
const err = new TypeError('onLoad should return an object');
|
||||
notifyPthreadCreateResult(payload.sab, 2, err);
|
||||
throw err;
|
||||
}
|
||||
const instance = source.instance;
|
||||
if (!instance) {
|
||||
const err = new TypeError('onLoad should return an object which includes "instance"');
|
||||
notifyPthreadCreateResult(payload.sab, 2, err);
|
||||
throw err;
|
||||
}
|
||||
this.instance = instance;
|
||||
const postMessage = this.postMessage;
|
||||
postMessage(createMessage('loaded', {}));
|
||||
const messages = this.messagesBeforeLoad;
|
||||
this.messagesBeforeLoad = [];
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const data = messages[i];
|
||||
this.handle({ data });
|
||||
}
|
||||
}
|
||||
handleAfterLoad(e, f) {
|
||||
if (this.instance !== undefined) {
|
||||
f.call(this, e);
|
||||
}
|
||||
else {
|
||||
this.messagesBeforeLoad.push(e.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
function notifyPthreadCreateResult(sab, result, error) {
|
||||
if (sab) {
|
||||
serizeErrorToBuffer(sab.buffer, result, error);
|
||||
Atomics.notify(sab, 0);
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion src/worker.ts
|
||||
|
||||
export { ThreadManager, ThreadMessageHandler, WASIThreads, createInstanceProxy, isSharedArrayBuffer, isTrapError };
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@emnapi/wasi-threads",
|
||||
"version": "2.0.1",
|
||||
"description": "WASI threads proposal implementation in JavaScript",
|
||||
"type": "module",
|
||||
"main": "./dist/wasi-threads.js",
|
||||
"module": "./dist/wasi-threads.js",
|
||||
"types": "./dist/wasi-threads.d.ts",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": "./dist/wasi-threads.js",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"build:test": "node ./test/build.js",
|
||||
"test": "node ./test/index.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/toyobayashi/emnapi.git"
|
||||
},
|
||||
"author": "toyobayashi",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/toyobayashi/emnapi/issues"
|
||||
},
|
||||
"homepage": "https://github.com/toyobayashi/emnapi#readme",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { defineConfig } from '@emnapi/shared'
|
||||
|
||||
export default defineConfig({
|
||||
outputName: 'wasiThreads',
|
||||
outputFile: 'wasi-threads'
|
||||
})
|
||||
Reference in New Issue
Block a user