space-data-module-sdk 0.8.20 → 0.8.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/isomorphic-pthreads.html +100 -0
- package/docs/isomorphic-pthreads.md +335 -0
- package/package.json +7 -2
- package/src/browser.js +6 -0
- package/src/compiler/invokeGlue.js +38 -0
- package/src/flow/flatsqlLinkShim.js +295 -0
- package/src/flow/index.d.ts +31 -0
- package/src/flow/index.js +8 -0
- package/src/host/browserCapabilityProbe.js +251 -0
- package/src/host/browserModuleHarness.js +24 -5
- package/src/host/flatsqlIo.js +14 -0
- package/src/host/flatsqlIoConformance.js +349 -0
- package/src/host/flatsqlIoContract.js +240 -0
- package/src/host/flatsqlIoImports.js +335 -0
- package/src/host/flatsqlIoMemoryBackend.js +178 -0
- package/src/host/flatsqlIoServer.js +960 -0
- package/src/host/flatsqlIoWorkers.js +323 -0
- package/src/host/hostWorkerBundleSources.js +11 -0
- package/src/host/hostWorkerBundles.js +79 -0
- package/src/host/index.js +9 -0
- package/src/host/isomorphicLoader.js +10 -2
- package/src/host/nodeSyncFsIo.js +633 -0
- package/src/host/opfsIoBackend.js +280 -0
- package/src/host/opfsIoWorker.mjs +223 -0
- package/src/host/sabIoChannel.js +848 -0
- package/src/host/sabIoMirror.js +306 -0
- package/src/host/wasiThreadBrowserWorker.mjs +21 -11
- package/src/host/wasiThreadHost.js +261 -43
- package/src/host/wasiThreadWorker.mjs +46 -8
- package/src/host/wasiThreadWorkerRuntime.js +54 -0
- package/src/index.d.ts +349 -0
- package/src/runtime/constants.js +6 -0
- package/src/testing/native/wasmedge_wasi_threads_runner.c +13 -5
- package/src/testing/parityHarness.js +7 -1
- package/src/testing/parityLanes.js +19 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHostcallBridge, DEFAULT_HOSTCALL_IMPORT_MODULE } from "./abi.js";
|
|
2
|
+
import { mergeImportFragments, resolveExtraImports } from "./flatsqlIoImports.js";
|
|
2
3
|
import {
|
|
3
4
|
createSabHostcallBuffer,
|
|
4
5
|
createSabHostcallClientDispatch,
|
|
@@ -54,11 +55,27 @@ function createThreadHostcallDispatch(options) {
|
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
/**
|
|
59
|
+
* The per-worker half of the wasi-threads host: the import object one guest
|
|
60
|
+
* thread instantiates the shared module with.
|
|
61
|
+
*
|
|
62
|
+
* Every pool thread gets WASI, the shared `env.memory`, a `wasi.thread-spawn`
|
|
63
|
+
* stub, the hostcall bridge when the module imports it, and the `extraImports`
|
|
64
|
+
* entries (T9): per-worker import objects such as FlatSQL's `env.flatsql_io_*`.
|
|
65
|
+
* Each entry is a factory `(ctx) => importObject` (or `{ imports, close }`), or
|
|
66
|
+
* a built-in descriptor such as `{ provider: "flatsql-io", instanceId, channels }`
|
|
67
|
+
* (flatsqlIoImports.js). Factories run once per worker, so each worker owns its
|
|
68
|
+
* own resources (for flatsql-io: its own request-ring slot). `ctx` is
|
|
69
|
+
* `{ memory, getMemory, tid, workerIndex }`.
|
|
70
|
+
*/
|
|
57
71
|
export function createWasiThreadWorkerRuntime({
|
|
58
72
|
wasmModule,
|
|
59
73
|
memory,
|
|
60
74
|
hostcallChannel,
|
|
61
75
|
processState,
|
|
76
|
+
extraImports,
|
|
77
|
+
workerIndex,
|
|
78
|
+
tid,
|
|
62
79
|
} = {}) {
|
|
63
80
|
const wasi = createBrowserWasiShim({ processState });
|
|
64
81
|
wasi.setMemory(memory);
|
|
@@ -79,7 +96,18 @@ export function createWasiThreadWorkerRuntime({
|
|
|
79
96
|
Object.assign(imports, bridge.imports);
|
|
80
97
|
}
|
|
81
98
|
|
|
99
|
+
const extras = resolveExtraImports(extraImports, {
|
|
100
|
+
memory,
|
|
101
|
+
getMemory: () => instance?.exports?.memory ?? memory,
|
|
102
|
+
workerIndex: workerIndex ?? null,
|
|
103
|
+
tid: tid ?? null,
|
|
104
|
+
});
|
|
105
|
+
mergeImportFragments(imports, extras.fragments);
|
|
106
|
+
// The shared memory is the one contract every import object must agree on.
|
|
107
|
+
imports.env.memory = memory;
|
|
108
|
+
|
|
82
109
|
return {
|
|
110
|
+
imports,
|
|
83
111
|
instantiate() {
|
|
84
112
|
instance = new WebAssembly.Instance(wasmModule, imports);
|
|
85
113
|
wasi.setMemory(instance.exports.memory ?? memory);
|
|
@@ -87,8 +115,34 @@ export function createWasiThreadWorkerRuntime({
|
|
|
87
115
|
},
|
|
88
116
|
close() {
|
|
89
117
|
hostcalls?.close();
|
|
118
|
+
extras.close();
|
|
90
119
|
},
|
|
91
120
|
};
|
|
92
121
|
}
|
|
93
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Resolve `{ moduleUrl, exportName?, config? }` entries by importing their
|
|
125
|
+
* factory module (module workers and Node; a classic blob worker cannot). The
|
|
126
|
+
* resolved entries are factories that receive `{ ...ctx, config }`. Other
|
|
127
|
+
* entries pass through unchanged.
|
|
128
|
+
*/
|
|
129
|
+
export async function resolveModuleExtraImports(extraImports) {
|
|
130
|
+
const out = [];
|
|
131
|
+
for (const entry of extraImports ?? []) {
|
|
132
|
+
if (entry && typeof entry.moduleUrl === "string") {
|
|
133
|
+
const mod = await import(/* @vite-ignore */ /* webpackIgnore: true */ entry.moduleUrl);
|
|
134
|
+
const factory = mod[entry.exportName ?? "default"];
|
|
135
|
+
if (typeof factory !== "function") {
|
|
136
|
+
throw new TypeError(
|
|
137
|
+
`extraImports module ${entry.moduleUrl} exports no factory "${entry.exportName ?? "default"}".`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
out.push({ factory, config: entry.config ?? null });
|
|
141
|
+
} else {
|
|
142
|
+
out.push(entry);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
|
|
94
148
|
export const WASI_THREAD_HOSTCALL_MESSAGE = THREAD_HOSTCALL_MESSAGE;
|
package/src/index.d.ts
CHANGED
|
@@ -2063,6 +2063,353 @@ export function createSabHostcallServer(options: {
|
|
|
2063
2063
|
dispatch: (operation: string, params: unknown) => Promise<unknown> | unknown;
|
|
2064
2064
|
}): { handleRequest(request: { operation: string; params?: unknown }): Promise<void> };
|
|
2065
2065
|
|
|
2066
|
+
// --- FlatSQL partition store host I/O (T9; docs/isomorphic-pthreads.md) ---
|
|
2067
|
+
export const DEFAULT_FLATSQL_IO_CHUNK_BYTES: number;
|
|
2068
|
+
export const DEFAULT_FLATSQL_IO_SPIN_MICROS: number;
|
|
2069
|
+
export const DEFAULT_SAB_IO_CLIENT_SPIN_MICROS: number;
|
|
2070
|
+
export const DEFAULT_SAB_IO_DATA_BYTES: number;
|
|
2071
|
+
export const DEFAULT_SAB_IO_MAX_INSTANCES: number;
|
|
2072
|
+
export const DEFAULT_SAB_IO_SLOTS: number;
|
|
2073
|
+
export const FLATSQL_IO_CREATE: number;
|
|
2074
|
+
export const FLATSQL_IO_CREATE_PARENTS: number;
|
|
2075
|
+
export const FLATSQL_IO_DELETE_ON_CLOSE: number;
|
|
2076
|
+
export const FLATSQL_IO_ERR_ACCESS: number;
|
|
2077
|
+
export const FLATSQL_IO_ERR_BADHANDLE: number;
|
|
2078
|
+
export const FLATSQL_IO_ERR_BUSY: number;
|
|
2079
|
+
export const FLATSQL_IO_ERR_GENERIC: number;
|
|
2080
|
+
export const FLATSQL_IO_ERR_IO: number;
|
|
2081
|
+
export const FLATSQL_IO_ERR_NOENT: number;
|
|
2082
|
+
export const FLATSQL_IO_ERR_NOSPACE: number;
|
|
2083
|
+
export const FLATSQL_IO_EXCL: number;
|
|
2084
|
+
export const FLATSQL_IO_MAX_PATH_BYTES: number;
|
|
2085
|
+
export const FLATSQL_IO_OK: number;
|
|
2086
|
+
export const FLATSQL_IO_OPEN_DEFERRED: number;
|
|
2087
|
+
export const FLATSQL_IO_PROBE: number;
|
|
2088
|
+
export const FLATSQL_IO_READ: number;
|
|
2089
|
+
export const FLATSQL_IO_SERVER_CREATE_FLAGS: number;
|
|
2090
|
+
export const FLATSQL_IO_TRUNC: number;
|
|
2091
|
+
export const FLATSQL_IO_UNLINK: number;
|
|
2092
|
+
export const FLATSQL_IO_UNLINK_IF_UNUSED: number;
|
|
2093
|
+
export const FLATSQL_IO_WRITE: number;
|
|
2094
|
+
export const NODE_SYNC_FS_ANONYMOUS_INSTANCE: number;
|
|
2095
|
+
export const SAB_IO_DATA_MEMORY: number;
|
|
2096
|
+
export const SAB_IO_DATA_SLOT: number;
|
|
2097
|
+
export const SAB_IO_DOORBELL_ATOMICS: number;
|
|
2098
|
+
export const SAB_IO_DOORBELL_MESSAGE: number;
|
|
2099
|
+
export const SAB_IO_H_CHANNEL_ID: number;
|
|
2100
|
+
export const SAB_IO_H_CLAIM_HINT: number;
|
|
2101
|
+
export const SAB_IO_H_DATA_BYTES: number;
|
|
2102
|
+
export const SAB_IO_H_DOORBELL: number;
|
|
2103
|
+
export const SAB_IO_H_DOORBELL_MODE: number;
|
|
2104
|
+
export const SAB_IO_H_MAGIC: number;
|
|
2105
|
+
export const SAB_IO_H_MAX_INSTANCES: number;
|
|
2106
|
+
export const SAB_IO_H_SERVED: number;
|
|
2107
|
+
export const SAB_IO_H_SERVER_EPOCH: number;
|
|
2108
|
+
export const SAB_IO_H_SERVER_STATE: number;
|
|
2109
|
+
export const SAB_IO_H_SLOT_BYTES: number;
|
|
2110
|
+
export const SAB_IO_H_SLOT_COUNT: number;
|
|
2111
|
+
export const SAB_IO_H_SLOT_RELEASES: number;
|
|
2112
|
+
export const SAB_IO_H_VERSION: number;
|
|
2113
|
+
export const SAB_IO_INSTANCE_ATTACHED: number;
|
|
2114
|
+
export const SAB_IO_INSTANCE_NONE: number;
|
|
2115
|
+
export const SAB_IO_INSTANCE_REVOKED: number;
|
|
2116
|
+
export const SAB_IO_INSTANCE_REVOKING: number;
|
|
2117
|
+
export const SAB_IO_MAGIC: number;
|
|
2118
|
+
export const SAB_IO_NOTIFY_ATOMICS: number;
|
|
2119
|
+
export const SAB_IO_NOTIFY_MESSAGE: number;
|
|
2120
|
+
export const SAB_IO_OP_CLOSE: number;
|
|
2121
|
+
export const SAB_IO_OP_OPEN: number;
|
|
2122
|
+
export const SAB_IO_OP_READ: number;
|
|
2123
|
+
export const SAB_IO_OP_SIZE: number;
|
|
2124
|
+
export const SAB_IO_OP_SYNC: number;
|
|
2125
|
+
export const SAB_IO_OP_TRUNCATE: number;
|
|
2126
|
+
export const SAB_IO_OP_WRITE: number;
|
|
2127
|
+
export const SAB_IO_SCRATCH_INSTANCE: number;
|
|
2128
|
+
export const SAB_IO_SERVER_DEAD: number;
|
|
2129
|
+
export const SAB_IO_SERVER_NOT_STARTED: number;
|
|
2130
|
+
export const SAB_IO_SERVER_RUNNING: number;
|
|
2131
|
+
export const SAB_IO_SERVER_STOPPED: number;
|
|
2132
|
+
export const SAB_IO_SLOT_DONE: number;
|
|
2133
|
+
export const SAB_IO_SLOT_FREE: number;
|
|
2134
|
+
export const SAB_IO_SLOT_IDLE: number;
|
|
2135
|
+
export const SAB_IO_SLOT_PENDING: number;
|
|
2136
|
+
export const SAB_IO_SLOT_SERVICING: number;
|
|
2137
|
+
export const SAB_IO_STATUS_DEAD: number;
|
|
2138
|
+
export const SAB_IO_STATUS_REVOKED: number;
|
|
2139
|
+
export const SAB_IO_S_DATA_MODE: number;
|
|
2140
|
+
export const SAB_IO_S_DONE_SEQ: number;
|
|
2141
|
+
export const SAB_IO_S_F64_OFFSET: number;
|
|
2142
|
+
export const SAB_IO_S_F64_RESULT: number;
|
|
2143
|
+
export const SAB_IO_S_FLAGS: number;
|
|
2144
|
+
export const SAB_IO_S_HANDLE: number;
|
|
2145
|
+
export const SAB_IO_S_INSTANCE: number;
|
|
2146
|
+
export const SAB_IO_S_LEN: number;
|
|
2147
|
+
export const SAB_IO_S_NOTIFY: number;
|
|
2148
|
+
export const SAB_IO_S_OP: number;
|
|
2149
|
+
export const SAB_IO_S_OWNER: number;
|
|
2150
|
+
export const SAB_IO_S_PATH_LEN: number;
|
|
2151
|
+
export const SAB_IO_S_PTR: number;
|
|
2152
|
+
export const SAB_IO_S_SEQ: number;
|
|
2153
|
+
export const SAB_IO_S_STATE: number;
|
|
2154
|
+
export const SAB_IO_S_STATUS: number;
|
|
2155
|
+
export const SAB_IO_VERSION: number;
|
|
2156
|
+
export const SAB_IO_WAIT_SLICE_MS: number;
|
|
2157
|
+
export const FLATSQL_IO_IMPORT_MODULE: string;
|
|
2158
|
+
export const FLATSQL_IO_PROVIDER_DESCRIPTOR: string;
|
|
2159
|
+
export const FLATSQL_IO_WORKER_FILENAME: string;
|
|
2160
|
+
export const NODE_SYNC_FS_PROVIDER_DESCRIPTOR: string;
|
|
2161
|
+
export const OPFS_SHARED_HANDLE_MODE: string;
|
|
2162
|
+
export const FLATSQL_IO_FLAGS: Readonly<Record<string, number>>;
|
|
2163
|
+
export const FLATSQL_IO_STATUS: Readonly<Record<string, number>>;
|
|
2164
|
+
export const FLATSQL_IO_IMPORT_NAMES: readonly string[];
|
|
2165
|
+
export const FLATSQL_IO_TRACE_OPS: Readonly<Record<string, number>>;
|
|
2166
|
+
|
|
2167
|
+
/** Synchronous provider behind the seven env.flatsql_io_* imports. */
|
|
2168
|
+
export interface FlatsqlIoProvider {
|
|
2169
|
+
open(path: Uint8Array | string, flags: number): number;
|
|
2170
|
+
read(handle: number, ptr: number, len: number, offset: number): number;
|
|
2171
|
+
write(handle: number, ptr: number, len: number, offset: number): number;
|
|
2172
|
+
truncate(handle: number, size: number): number;
|
|
2173
|
+
sync(handle: number): number;
|
|
2174
|
+
size(handle: number): number;
|
|
2175
|
+
close(handle: number): number;
|
|
2176
|
+
}
|
|
2177
|
+
export interface FlatsqlIoSyncClient extends FlatsqlIoProvider {
|
|
2178
|
+
readonly instanceId: number;
|
|
2179
|
+
readonly requests: number;
|
|
2180
|
+
readInto(handle: number, view: Uint8Array, offset: number): number;
|
|
2181
|
+
writeFrom(handle: number, view: Uint8Array, offset: number): number;
|
|
2182
|
+
release(): void;
|
|
2183
|
+
}
|
|
2184
|
+
export interface FlatsqlIoAsyncClient {
|
|
2185
|
+
readonly instanceId: number;
|
|
2186
|
+
open(path: Uint8Array | string, flags: number): Promise<number>;
|
|
2187
|
+
read(handle: number, length: number, offset: number): Promise<Uint8Array | number>;
|
|
2188
|
+
write(handle: number, bytes: Uint8Array, offset: number): Promise<number>;
|
|
2189
|
+
truncate(handle: number, size: number): Promise<number>;
|
|
2190
|
+
sync(handle: number): Promise<number>;
|
|
2191
|
+
size(handle: number): Promise<number>;
|
|
2192
|
+
close(handle: number): Promise<number>;
|
|
2193
|
+
close$(): void;
|
|
2194
|
+
}
|
|
2195
|
+
export interface FlatsqlIoBackendFile {
|
|
2196
|
+
read(view: Uint8Array, options?: { at?: number }): number;
|
|
2197
|
+
write(view: Uint8Array, options?: { at?: number }): number;
|
|
2198
|
+
truncate(size: number): void;
|
|
2199
|
+
flush(): void;
|
|
2200
|
+
getSize(): number;
|
|
2201
|
+
close(): void;
|
|
2202
|
+
}
|
|
2203
|
+
export interface FlatsqlIoBackend {
|
|
2204
|
+
kind: string;
|
|
2205
|
+
init?(): Promise<Record<string, unknown>>;
|
|
2206
|
+
openFile(path: string, components: string[], flags: number): FlatsqlIoBackendFile | Promise<FlatsqlIoBackendFile>;
|
|
2207
|
+
exists(path: string, components: string[]): boolean | Promise<boolean>;
|
|
2208
|
+
remove(path: string, components: string[]): void | Promise<void>;
|
|
2209
|
+
}
|
|
2210
|
+
export interface FlatsqlIoMirrorOptions {
|
|
2211
|
+
buffer: SharedArrayBuffer;
|
|
2212
|
+
suffixes?: string[];
|
|
2213
|
+
write?: boolean;
|
|
2214
|
+
}
|
|
2215
|
+
export type FlatsqlIoExtraImport =
|
|
2216
|
+
| { provider: "flatsql-io"; instanceId?: number; channels: SharedArrayBuffer[]; mirror?: FlatsqlIoMirrorOptions; trace?: { buffer: SharedArrayBuffer } }
|
|
2217
|
+
| { provider: "flatsql-io-node"; root: string; table: SharedArrayBuffer; instanceId?: number; mirror?: FlatsqlIoMirrorOptions; trace?: { buffer: SharedArrayBuffer } }
|
|
2218
|
+
| { moduleUrl: string; exportName?: string; config?: unknown };
|
|
2219
|
+
|
|
2220
|
+
export class FlatsqlIoStatusError extends Error {
|
|
2221
|
+
constructor(status: number, message?: string);
|
|
2222
|
+
status: number;
|
|
2223
|
+
}
|
|
2224
|
+
export function flatsqlIoStatusForError(error: unknown, options?: { during?: "open" }): number;
|
|
2225
|
+
export function flatsqlIoStatusOf(error: unknown, options?: { during?: "open" }): number;
|
|
2226
|
+
export function splitFlatsqlIoPath(path: string): string[] | number;
|
|
2227
|
+
export function decodeFlatsqlIoPath(bytes: Uint8Array): string | number;
|
|
2228
|
+
export function hashFlatsqlIoPath(bytes: Uint8Array): number;
|
|
2229
|
+
|
|
2230
|
+
export function createSabIoChannelBuffer(options?: { slots?: number; dataBytes?: number; maxInstances?: number }): SharedArrayBuffer;
|
|
2231
|
+
export function describeSabIoChannel(buffer: SharedArrayBuffer): Record<string, unknown>;
|
|
2232
|
+
export function sabIoDoorbellChannelName(channelId: number): string;
|
|
2233
|
+
export function sabIoCompletionChannelName(channelId: number): string;
|
|
2234
|
+
export function createSabIoClient(options: { buffer: SharedArrayBuffer; instanceId?: number; getMemory?: () => WebAssembly.Memory | null; spinMicros?: number }): FlatsqlIoSyncClient;
|
|
2235
|
+
export function createSabIoAsyncClient(options: { buffer: SharedArrayBuffer; instanceId?: number; forceMessages?: boolean }): FlatsqlIoAsyncClient;
|
|
2236
|
+
export function sabIoInstanceState(buffer: SharedArrayBuffer, instanceId: number): number;
|
|
2237
|
+
export function revokeSabIoInstance(buffer: SharedArrayBuffer, instanceId: number, options?: { pollMs?: number }): Promise<void>;
|
|
2238
|
+
export function resetSabIoInstance(buffer: SharedArrayBuffer, instanceId: number): void;
|
|
2239
|
+
export function failPendingSabIoRequests(buffer: SharedArrayBuffer, status?: number): number;
|
|
2240
|
+
export function reclaimSabIoSlots(buffer: SharedArrayBuffer, instanceId: number): number;
|
|
2241
|
+
export function completeSabIoSlot(layout: unknown, slot: unknown, status: number, f64Result?: number): void;
|
|
2242
|
+
|
|
2243
|
+
export function createSabIoMirrorBuffer(options?: { entries?: number; entryBytes?: number; maxPathBytes?: number }): SharedArrayBuffer;
|
|
2244
|
+
export function sabIoMirrorEntryBytes(buffer: SharedArrayBuffer): number;
|
|
2245
|
+
export function findSabIoMirrorEntry(buffer: SharedArrayBuffer, path: string | Uint8Array): number;
|
|
2246
|
+
export function updateSabIoMirror(buffer: SharedArrayBuffer, path: string | Uint8Array, offset: number, bytes: Uint8Array, options?: { truncateTo?: number }): boolean;
|
|
2247
|
+
export function clearSabIoMirror(buffer: SharedArrayBuffer, path: string | Uint8Array): void;
|
|
2248
|
+
export function readSabIoMirror(buffer: SharedArrayBuffer, path: string | Uint8Array, dst: Uint8Array, offset: number): number;
|
|
2249
|
+
export function readSabIoMirrorEntry(buffer: SharedArrayBuffer, index: number, dst: Uint8Array, offset: number): number;
|
|
2250
|
+
export function createSabIoMirrorMatcher(suffixes?: string[]): (path: string) => boolean;
|
|
2251
|
+
|
|
2252
|
+
export function createFlatsqlIoImports(options: {
|
|
2253
|
+
getMemory: () => WebAssembly.Memory;
|
|
2254
|
+
provider: FlatsqlIoProvider | FlatsqlIoProvider[];
|
|
2255
|
+
mirror?: FlatsqlIoMirrorOptions;
|
|
2256
|
+
trace?: { buffer: SharedArrayBuffer };
|
|
2257
|
+
}): { env: Record<string, (...args: number[]) => number> };
|
|
2258
|
+
export function resolveFlatsqlIoDescriptor(descriptor: FlatsqlIoExtraImport, ctx: { getMemory: () => WebAssembly.Memory }): { imports: WebAssembly.Imports; close(): void };
|
|
2259
|
+
export function resolveExtraImports(extraImports: Array<FlatsqlIoExtraImport | ((ctx: unknown) => unknown)>, ctx: { memory: WebAssembly.Memory; getMemory: () => WebAssembly.Memory; tid?: number | null; workerIndex?: number | null }): { fragments: WebAssembly.Imports[]; close(): void };
|
|
2260
|
+
export function mergeImportFragments(imports: WebAssembly.Imports, fragments: WebAssembly.Imports[]): WebAssembly.Imports;
|
|
2261
|
+
export function createFlatsqlIoTraceBuffer(capacity?: number): SharedArrayBuffer;
|
|
2262
|
+
export function readFlatsqlIoTrace(buffer: SharedArrayBuffer): { recorded: number; capacity: number; samples: Array<{ op: number; micros: number }> };
|
|
2263
|
+
|
|
2264
|
+
export interface FlatsqlIoServer {
|
|
2265
|
+
readonly doorbellMode: number;
|
|
2266
|
+
readonly stats: Record<string, number | boolean>;
|
|
2267
|
+
start(): Promise<void>;
|
|
2268
|
+
stop(): Promise<void>;
|
|
2269
|
+
attachMemory(instanceId: number, memory: WebAssembly.Memory | SharedArrayBuffer): boolean;
|
|
2270
|
+
detachMemory(instanceId: number): void;
|
|
2271
|
+
preopen(paths: string[], flags?: number): Promise<number[]>;
|
|
2272
|
+
releasePreopen(paths: string[]): void;
|
|
2273
|
+
inventory(): { files: Array<{ path: string; refs: number; pins: number; opening: boolean; openStatus: number }>; handles: number };
|
|
2274
|
+
}
|
|
2275
|
+
export function createFlatsqlIoServer(options: {
|
|
2276
|
+
buffer: SharedArrayBuffer;
|
|
2277
|
+
backend: FlatsqlIoBackend;
|
|
2278
|
+
chunkBytes?: number;
|
|
2279
|
+
doorbell?: "auto" | "atomics" | "message";
|
|
2280
|
+
mirror?: { buffer: SharedArrayBuffer; match(path: string): boolean; write?: boolean };
|
|
2281
|
+
spinMicros?: number;
|
|
2282
|
+
onEvent?: (event: Record<string, unknown>) => void;
|
|
2283
|
+
}): FlatsqlIoServer;
|
|
2284
|
+
export function createFlatsqlIoMemoryBackend(options?: { maxBytes?: number }): FlatsqlIoBackend & {
|
|
2285
|
+
reset(): void;
|
|
2286
|
+
usage(): { files: number; allocatedBytes: number };
|
|
2287
|
+
list(): string[];
|
|
2288
|
+
};
|
|
2289
|
+
export function createOpfsIoBackend(options?: {
|
|
2290
|
+
rootDirectory?: string;
|
|
2291
|
+
handleMode?: "auto" | "shared" | "exclusive";
|
|
2292
|
+
openDelayMs?: (path: string) => number;
|
|
2293
|
+
root?: unknown;
|
|
2294
|
+
busyRetry?: { attempts: number; baseMs?: number };
|
|
2295
|
+
}): FlatsqlIoBackend & { readonly sharedModes: boolean; readonly sharedViews: boolean | null; clear(): Promise<void> };
|
|
2296
|
+
export function probeOpfsSharedHandleModes(root: unknown, name?: string): Promise<{ supported: boolean; detail: string }>;
|
|
2297
|
+
export function probeOpfsSharedViews(root: unknown, name?: string): Promise<boolean>;
|
|
2298
|
+
|
|
2299
|
+
export class FlatsqlIoWorkerStartError extends Error {
|
|
2300
|
+
lockUnavailable?: boolean;
|
|
2301
|
+
}
|
|
2302
|
+
export interface FlatsqlIoWorkerController {
|
|
2303
|
+
readonly buffer: SharedArrayBuffer;
|
|
2304
|
+
readonly info: Record<string, unknown> | null;
|
|
2305
|
+
readonly dead: boolean;
|
|
2306
|
+
readonly restarts: number;
|
|
2307
|
+
readonly worker: unknown;
|
|
2308
|
+
attachMemory(instanceId: number, memory: WebAssembly.Memory | SharedArrayBuffer): Promise<boolean>;
|
|
2309
|
+
detachMemory(instanceId: number): void;
|
|
2310
|
+
revoke(instanceId: number): Promise<void>;
|
|
2311
|
+
preopen(paths: string[], flags?: number): Promise<number[]>;
|
|
2312
|
+
releasePreopen(paths: string[]): void;
|
|
2313
|
+
stats(): Promise<Record<string, unknown>>;
|
|
2314
|
+
reset(): Promise<void>;
|
|
2315
|
+
clear(): Promise<unknown>;
|
|
2316
|
+
stop(): Promise<void>;
|
|
2317
|
+
kill(): void;
|
|
2318
|
+
}
|
|
2319
|
+
export function createFlatsqlIoWorker(options?: {
|
|
2320
|
+
buffer?: SharedArrayBuffer;
|
|
2321
|
+
channel?: { slots?: number; dataBytes?: number; maxInstances?: number };
|
|
2322
|
+
backend?: "opfs" | "memory";
|
|
2323
|
+
role?: "writer" | "reader";
|
|
2324
|
+
chunkBytes?: number;
|
|
2325
|
+
doorbell?: "auto" | "atomics" | "message";
|
|
2326
|
+
mirror?: FlatsqlIoMirrorOptions;
|
|
2327
|
+
rootDirectory?: string;
|
|
2328
|
+
handleMode?: "auto" | "shared" | "exclusive";
|
|
2329
|
+
memoryMaxBytes?: number;
|
|
2330
|
+
openDelay?: { pattern: string; ms: number } | null;
|
|
2331
|
+
lock?: { name: string; ifAvailable?: boolean } | null;
|
|
2332
|
+
busyRetry?: { attempts: number; baseMs?: number } | null;
|
|
2333
|
+
workerUrl?: string | URL;
|
|
2334
|
+
workerType?: "module" | "classic";
|
|
2335
|
+
onError?: (error: unknown, info: { restarted: boolean; failedRequests: number }) => void;
|
|
2336
|
+
restart?: boolean;
|
|
2337
|
+
}): Promise<FlatsqlIoWorkerController>;
|
|
2338
|
+
|
|
2339
|
+
export const FLATSQL_IO_CONFORMANCE_CASES: ReadonlyArray<{ name: string; run(io: unknown, dir: string): Promise<void> }>;
|
|
2340
|
+
export function runFlatsqlIoConformance(io: unknown, options?: { prefix?: string; skip?: string[] }): Promise<{ passed: string[]; failed: Array<{ name: string; error: string }>; skipped: string[] }>;
|
|
2341
|
+
export function conformanceAdapterForAsyncClient(client: FlatsqlIoAsyncClient): unknown;
|
|
2342
|
+
|
|
2343
|
+
export function probeWorkerCapabilities(): Promise<Record<string, unknown>>;
|
|
2344
|
+
export function workerCapabilityProbeSource(): string;
|
|
2345
|
+
export function probeBrowserCapabilities(options?: { timeoutMs?: number }): Promise<{
|
|
2346
|
+
page: Record<string, unknown>;
|
|
2347
|
+
worker: Record<string, unknown> | null;
|
|
2348
|
+
localStore: { supported: boolean; missing: string[]; doorbell: "atomics" | "message"; transfer: "direct" | "scratch" };
|
|
2349
|
+
}>;
|
|
2350
|
+
export function flatsqlLocalStoreGate(matrix: { page: Record<string, unknown>; worker: Record<string, unknown> | null }): {
|
|
2351
|
+
supported: boolean;
|
|
2352
|
+
missing: string[];
|
|
2353
|
+
doorbell: "atomics" | "message";
|
|
2354
|
+
transfer: "direct" | "scratch";
|
|
2355
|
+
};
|
|
2356
|
+
|
|
2357
|
+
export function createNodeSyncFsIoTable(options?: { maxHandles?: number; maxPathBytes?: number; maxInstances?: number }): SharedArrayBuffer;
|
|
2358
|
+
export function createNodeSyncFsIo(options: {
|
|
2359
|
+
root: string;
|
|
2360
|
+
table: SharedArrayBuffer;
|
|
2361
|
+
instanceId?: number;
|
|
2362
|
+
getMemory?: () => WebAssembly.Memory | null;
|
|
2363
|
+
interpose?: (op: string, fn: (...args: unknown[]) => unknown, args: unknown[]) => unknown;
|
|
2364
|
+
}): FlatsqlIoProvider & {
|
|
2365
|
+
readInto(handle: number, view: Uint8Array, offset: number): number;
|
|
2366
|
+
writeFrom(handle: number, view: Uint8Array, offset: number): number;
|
|
2367
|
+
closeLocalFds(): void;
|
|
2368
|
+
liveHandles(): number;
|
|
2369
|
+
localFdCount(): number;
|
|
2370
|
+
};
|
|
2371
|
+
export function revokeNodeSyncFsIoInstance(table: SharedArrayBuffer, instanceId: number): void;
|
|
2372
|
+
export function resetNodeSyncFsIoInstance(table: SharedArrayBuffer, instanceId: number): void;
|
|
2373
|
+
export function resolveNodeFlatsqlIoDescriptors(entries: unknown[]): unknown[];
|
|
2374
|
+
|
|
2375
|
+
export interface WasiThreadSpawnReport {
|
|
2376
|
+
poolSize: number | null;
|
|
2377
|
+
armed: number;
|
|
2378
|
+
failedToArm: number;
|
|
2379
|
+
spawned: number;
|
|
2380
|
+
declined: number;
|
|
2381
|
+
declinedByReason: Record<string, number>;
|
|
2382
|
+
lastDeclineReason: string | null;
|
|
2383
|
+
active: number;
|
|
2384
|
+
idle?: number;
|
|
2385
|
+
}
|
|
2386
|
+
export function isWasiThreadsModule(wasmModule: WebAssembly.Module): boolean;
|
|
2387
|
+
export function createWasiThreadSpawn(options: {
|
|
2388
|
+
wasmModule: WebAssembly.Module;
|
|
2389
|
+
memory: WebAssembly.Memory;
|
|
2390
|
+
requestedThreads?: number;
|
|
2391
|
+
poolSize?: number;
|
|
2392
|
+
extraImports?: FlatsqlIoExtraImport[];
|
|
2393
|
+
instanceId?: number;
|
|
2394
|
+
onGuestError?: (instanceId: number | null, tid: number | null, error: unknown) => void;
|
|
2395
|
+
onSpawnDeclined?: (event: { reason: string; poolSize: number | null; declined: number }) => void;
|
|
2396
|
+
probeTimeoutMs?: number;
|
|
2397
|
+
hostcallChannel?: unknown;
|
|
2398
|
+
processState?: unknown;
|
|
2399
|
+
requiresHostcalls?: boolean;
|
|
2400
|
+
enableBrowserThreads?: boolean;
|
|
2401
|
+
browserWorkerBaseUrl?: string | URL;
|
|
2402
|
+
browserWorkerUrl?: string | URL;
|
|
2403
|
+
browserWorkerType?: "module" | "classic";
|
|
2404
|
+
}): Promise<{
|
|
2405
|
+
threadSpawn(startArg: number): number;
|
|
2406
|
+
activeThreadCount(): number;
|
|
2407
|
+
spawnCount(): number;
|
|
2408
|
+
distinctOsThreadCount(): number;
|
|
2409
|
+
spawnReport(): WasiThreadSpawnReport;
|
|
2410
|
+
terminateAll(): Promise<void>;
|
|
2411
|
+
}>;
|
|
2412
|
+
|
|
2066
2413
|
// --- Browser cron/timer driver (WS6.3) ---
|
|
2067
2414
|
export interface ModuleTimerRunRecord {
|
|
2068
2415
|
timerId: string;
|
|
@@ -2790,6 +3137,8 @@ export const DefaultInvokeExports: {
|
|
|
2790
3137
|
allocSymbol: string;
|
|
2791
3138
|
freeSymbol: string;
|
|
2792
3139
|
commandSymbol: string;
|
|
3140
|
+
reactorInitializeSymbol: string;
|
|
3141
|
+
constructorsSymbol: string;
|
|
2793
3142
|
};
|
|
2794
3143
|
|
|
2795
3144
|
export const DrainPolicy: {
|
package/src/runtime/constants.js
CHANGED
|
@@ -74,4 +74,10 @@ export const DefaultInvokeExports = Object.freeze({
|
|
|
74
74
|
allocSymbol: "plugin_alloc",
|
|
75
75
|
freeSymbol: "plugin_free",
|
|
76
76
|
commandSymbol: "_start",
|
|
77
|
+
// Direct-surface initialization. A reactor exports `_initialize`; a command
|
|
78
|
+
// artifact that also serves the direct surface exports `__wasm_call_ctors`
|
|
79
|
+
// (its `_start` would run main). A direct host calls the first one present,
|
|
80
|
+
// once per instance, before the first direct call.
|
|
81
|
+
reactorInitializeSymbol: "_initialize",
|
|
82
|
+
constructorsSymbol: "__wasm_call_ctors",
|
|
77
83
|
});
|
|
@@ -150,9 +150,12 @@ static WasmEdge_ModuleInstanceContext *memory_import(Threads *g) {
|
|
|
150
150
|
return env;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
//
|
|
153
|
+
// Direct calls use the same PIV request/response bytes as commands. Stage the
|
|
154
|
+
// request through the public allocator, then monitor the direct call with the
|
|
155
|
+
// same worker-group lifecycle as _start. No guest ABI is reimplemented. The
|
|
156
|
+
// module initializes first, once: a reactor through _initialize, a command
|
|
157
|
+
// artifact serving the direct surface through __wasm_call_ctors (its _start
|
|
158
|
+
// would run main).
|
|
156
159
|
static uint32_t stage_direct_request(Threads *g,
|
|
157
160
|
WasmEdge_ModuleInstanceContext *instance,
|
|
158
161
|
WasmEdge_MemoryInstanceContext *memory, WasmEdge_Value args[3]) {
|
|
@@ -171,6 +174,7 @@ static uint32_t stage_direct_request(Threads *g,
|
|
|
171
174
|
}
|
|
172
175
|
const WasmEdge_FunctionInstanceContext *init =
|
|
173
176
|
WasmEdge_ModuleInstanceFindFunction(instance, name("_initialize"));
|
|
177
|
+
if (!init) init = WasmEdge_ModuleInstanceFindFunction(instance, name("__wasm_call_ctors"));
|
|
174
178
|
if (init) require_result("initialize", WasmEdge_ExecutorInvoke(g->executor, init, NULL, 0, NULL, 0));
|
|
175
179
|
const WasmEdge_FunctionInstanceContext *alloc =
|
|
176
180
|
WasmEdge_ModuleInstanceFindFunction(instance, name("plugin_alloc"));
|
|
@@ -224,10 +228,12 @@ int main(int argc, char **argv) {
|
|
|
224
228
|
if (!envs) return 1;
|
|
225
229
|
uint32_t env_count = 0;
|
|
226
230
|
bool stats = false;
|
|
231
|
+
bool force_direct = false;
|
|
227
232
|
int first = 1;
|
|
228
233
|
for (; first < argc; ++first) {
|
|
229
234
|
if (strcmp(argv[first], "--enable-threads") == 0) continue;
|
|
230
235
|
if (strcmp(argv[first], "--sdm-thread-stats") == 0) { stats = true; continue; }
|
|
236
|
+
if (strcmp(argv[first], "--sdm-direct") == 0) { force_direct = true; continue; }
|
|
231
237
|
if (strcmp(argv[first], "--env") == 0 && first + 1 < argc) {
|
|
232
238
|
envs[env_count++] = argv[++first];
|
|
233
239
|
continue;
|
|
@@ -235,7 +241,7 @@ int main(int argc, char **argv) {
|
|
|
235
241
|
break;
|
|
236
242
|
}
|
|
237
243
|
if (first >= argc || argv[first][0] == '-') {
|
|
238
|
-
fprintf(stderr, "usage: %s [--env NAME=VALUE] module.wasm [args...]\n", argv[0]);
|
|
244
|
+
fprintf(stderr, "usage: %s [--sdm-direct] [--env NAME=VALUE] module.wasm [args...]\n", argv[0]);
|
|
239
245
|
free(envs);
|
|
240
246
|
return 2;
|
|
241
247
|
}
|
|
@@ -269,7 +275,9 @@ int main(int argc, char **argv) {
|
|
|
269
275
|
WasmEdge_ModuleInstanceContext *instance = NULL;
|
|
270
276
|
require_result("instantiate", WasmEdge_ExecutorInstantiate(
|
|
271
277
|
g.executor, &instance, g.store, g.ast));
|
|
272
|
-
|
|
278
|
+
// --sdm-direct serves the direct surface of an artifact that also has a
|
|
279
|
+
// command entry; a reactor (no _start) is always served directly.
|
|
280
|
+
const WasmEdge_FunctionInstanceContext *start = force_direct ? NULL :
|
|
273
281
|
WasmEdge_ModuleInstanceFindFunction(instance, name("_start"));
|
|
274
282
|
bool direct = start == NULL;
|
|
275
283
|
WasmEdge_Value direct_args[3], response_pointer;
|
|
@@ -499,6 +499,11 @@ function applyInjectedDivergence(runs, injectDivergence) {
|
|
|
499
499
|
* @param {string[]} [options.lanes] - subset of PARITY_LANES. Defaults to ALL
|
|
500
500
|
* THREE. Narrowing is always an explicit caller decision — lanes are never
|
|
501
501
|
* skipped silently, and a single-lane run can never claim parity.
|
|
502
|
+
* @param {string} [options.surface] - "direct" runs EVERY lane on the direct
|
|
503
|
+
* surface: the browser harness, and the wasi-threads runner with
|
|
504
|
+
* --sdm-direct (the WasmEdge CLI has no direct surface, so a single-thread
|
|
505
|
+
* artifact cannot be compared on it). Omitted: the browser lane follows
|
|
506
|
+
* browserSurface and the WasmEdge lanes run the artifact's default surface.
|
|
502
507
|
* @param {Object} [options.laneRunners] - {laneName: async (context) => runs[]}
|
|
503
508
|
* override for tests. Default runners come from parityLanes.js.
|
|
504
509
|
* @param {string} [options.injectDivergence] - fire-drill: XOR one output byte
|
|
@@ -555,7 +560,8 @@ export async function runParityHarness(options = {}) {
|
|
|
555
560
|
chromeBinary: options.chromeBinary,
|
|
556
561
|
wasmedgeBinary: options.wasmedgeBinary,
|
|
557
562
|
wasmEdgeRunnerBinary: options.wasmEdgeRunnerBinary,
|
|
558
|
-
|
|
563
|
+
surface: options.surface ?? null,
|
|
564
|
+
browserSurface: options.browserSurface ?? options.surface ??
|
|
559
565
|
(WebAssembly.Module.exports(await WebAssembly.compile(loadableBytes)).some((entry) => entry.name === "_start") ? "command" : "direct"),
|
|
560
566
|
dockerBinary: options.dockerBinary,
|
|
561
567
|
dockerPlatform: options.dockerPlatform,
|
|
@@ -130,6 +130,21 @@ function wasmedgeInvocationArgs(planCase, plan, threadCount) {
|
|
|
130
130
|
return args;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
// Runner flags that select the surface. Only the wasi-threads runner serves the
|
|
134
|
+
// direct surface of an artifact with a command entry; the WasmEdge CLI runs
|
|
135
|
+
// `_start` or nothing, so a direct-surface comparison on it would silently
|
|
136
|
+
// compare two different surfaces.
|
|
137
|
+
function surfaceArgs(context, threaded, lane) {
|
|
138
|
+
if (context.surface !== "direct") return [];
|
|
139
|
+
if (!threaded) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`${lane}: the WasmEdge CLI serves only the command surface of a single-thread artifact; ` +
|
|
142
|
+
"it cannot take part in a direct-surface parity run.",
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return ["--sdm-direct"];
|
|
146
|
+
}
|
|
147
|
+
|
|
133
148
|
async function stageModuleWorkdir(context, label) {
|
|
134
149
|
const workdir = await mkdtemp(path.join(os.tmpdir(), `sdm-parity-${label}-`));
|
|
135
150
|
// Stage the canonical loadable bytes (publication records stripped by the
|
|
@@ -183,6 +198,7 @@ export async function runNativeWasmEdgeLane(context) {
|
|
|
183
198
|
`native binary ${binary}`,
|
|
184
199
|
);
|
|
185
200
|
|
|
201
|
+
const directArgs = surfaceArgs(context, threaded, "native WasmEdge lane");
|
|
186
202
|
const workdir = await stageModuleWorkdir(context, "native");
|
|
187
203
|
const runs = [];
|
|
188
204
|
try {
|
|
@@ -191,6 +207,7 @@ export async function runNativeWasmEdgeLane(context) {
|
|
|
191
207
|
const outcome = await spawnWithStdin(
|
|
192
208
|
binary,
|
|
193
209
|
[...(threaded ? ["--sdm-thread-stats"] : []),
|
|
210
|
+
...directArgs,
|
|
194
211
|
...wasmedgeInvocationArgs(planCase, context.plan, threadCount)],
|
|
195
212
|
{
|
|
196
213
|
cwd: workdir,
|
|
@@ -288,6 +305,7 @@ export async function runDockerWasmEdgeLane(context) {
|
|
|
288
305
|
`docker WasmEdge lane: cannot execute "${dockerBinary}" (${error.message}).`,
|
|
289
306
|
);
|
|
290
307
|
}
|
|
308
|
+
const directArgs = surfaceArgs(context, threaded, "docker WasmEdge lane");
|
|
291
309
|
const image = await ensureDockerParityImage(context);
|
|
292
310
|
|
|
293
311
|
const workdir = await stageModuleWorkdir(context, "docker");
|
|
@@ -313,6 +331,7 @@ export async function runDockerWasmEdgeLane(context) {
|
|
|
313
331
|
dockerArgs.push(
|
|
314
332
|
image,
|
|
315
333
|
...(threaded ? ["--sdm-thread-stats"] : []),
|
|
334
|
+
...directArgs,
|
|
316
335
|
...wasmedgeInvocationArgs(planCase, context.plan, threadCount),
|
|
317
336
|
);
|
|
318
337
|
const outcome = await spawnWithStdin(dockerBinary, dockerArgs, {
|