knitting 0.1.55 → 0.1.60
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/README.md +254 -40
- package/knitting.d.ts +7 -797
- package/knitting.js +6 -9780
- package/map.md +31 -15
- package/package.json +20 -6
- package/process-shared-buffer.d.ts +1 -0
- package/process-shared-buffer.js +1 -0
- package/scripts/build-compiled-worker.ts +276 -0
- package/scripts/compiled-worker/porffor.d.ts +5 -0
- package/scripts/compiled-worker/runtime.ts +529 -0
- package/scripts/compiled-worker/task-shim.ts +32 -0
- package/shared-memory.d.ts +1 -155
- package/shared-memory.js +1 -1342
- package/src/api.d.ts +92 -0
- package/src/api.js +798 -0
- package/src/common/envelope.d.ts +17 -0
- package/src/common/envelope.js +22 -0
- package/src/common/module-url.d.ts +2 -0
- package/src/common/module-url.js +52 -0
- package/src/common/node-compat.d.ts +22 -0
- package/src/common/node-compat.js +24 -0
- package/src/common/path-canonical.d.ts +6 -0
- package/src/common/path-canonical.js +55 -0
- package/src/common/runtime.d.ts +16 -0
- package/src/common/runtime.js +105 -0
- package/src/common/shared-buffer-region.d.ts +11 -0
- package/src/common/shared-buffer-region.js +21 -0
- package/src/common/shared-buffer-text.d.ts +16 -0
- package/src/common/shared-buffer-text.js +65 -0
- package/src/common/task-source.d.ts +5 -0
- package/src/common/task-source.js +100 -0
- package/src/common/task-symbol.d.ts +1 -0
- package/src/common/task-symbol.js +1 -0
- package/src/common/with-resolvers.d.ts +9 -0
- package/src/common/with-resolvers.js +23 -0
- package/src/common/worker-runtime.d.ts +42 -0
- package/src/common/worker-runtime.js +70 -0
- package/src/connections/buffer-reference-native.d.ts +56 -0
- package/src/connections/buffer-reference-native.js +241 -0
- package/src/connections/buffer-reference.d.ts +99 -0
- package/src/connections/buffer-reference.js +575 -0
- package/src/connections/bun.d.ts +22 -0
- package/src/connections/bun.js +225 -0
- package/src/connections/deno.d.ts +24 -0
- package/src/connections/deno.js +226 -0
- package/src/connections/external-array-buffer.d.ts +2 -0
- package/src/connections/external-array-buffer.js +33 -0
- package/src/connections/file-descriptor.d.ts +39 -0
- package/src/connections/file-descriptor.js +161 -0
- package/src/connections/index.d.ts +4 -0
- package/src/connections/index.js +4 -0
- package/src/connections/node-addons.d.ts +16 -0
- package/src/connections/node-addons.js +92 -0
- package/src/connections/node-buffer-pointer.d.ts +20 -0
- package/src/connections/node-buffer-pointer.js +16 -0
- package/src/connections/node-ffi-api.d.ts +19 -0
- package/src/connections/node-ffi-api.js +28 -0
- package/src/connections/node-ffi.d.ts +27 -0
- package/src/connections/node-ffi.js +262 -0
- package/src/connections/node.d.ts +30 -0
- package/src/connections/node.js +62 -0
- package/src/connections/numeric-array.d.ts +5 -0
- package/src/connections/numeric-array.js +17 -0
- package/src/connections/package-assets.d.ts +2 -0
- package/src/connections/package-assets.js +55 -0
- package/src/connections/posix.d.ts +74 -0
- package/src/connections/posix.js +178 -0
- package/src/connections/process-shared-buffer.d.ts +76 -0
- package/src/connections/process-shared-buffer.js +245 -0
- package/src/connections/shared-array-buffer-payload.d.ts +36 -0
- package/src/connections/shared-array-buffer-payload.js +235 -0
- package/src/connections/types.d.ts +54 -0
- package/src/connections/types.js +37 -0
- package/src/connections/windows.d.ts +37 -0
- package/src/connections/windows.js +245 -0
- package/src/debug/env-diff.d.ts +26 -0
- package/src/debug/env-diff.js +49 -0
- package/src/debug/gate.d.ts +18 -0
- package/src/debug/gate.js +69 -0
- package/src/debug/handle.d.ts +23 -0
- package/src/debug/handle.js +48 -0
- package/src/error.d.ts +14 -0
- package/src/error.js +50 -0
- package/src/ipc/tools/ring-queue.d.ts +33 -0
- package/src/ipc/tools/ring-queue.js +159 -0
- package/src/ipc/transport/shared-memory.d.ts +23 -0
- package/src/ipc/transport/shared-memory.js +35 -0
- package/src/memory/byte-carpet.d.ts +73 -0
- package/src/memory/byte-carpet.js +157 -0
- package/src/memory/lock.d.ts +213 -0
- package/src/memory/lock.js +760 -0
- package/src/memory/payload-config.d.ts +31 -0
- package/src/memory/payload-config.js +67 -0
- package/src/memory/payloadCodec.d.ts +55 -0
- package/src/memory/payloadCodec.js +1473 -0
- package/src/memory/regionRegistry.d.ts +17 -0
- package/src/memory/regionRegistry.js +285 -0
- package/src/memory/shared-buffer-io.d.ts +55 -0
- package/src/memory/shared-buffer-io.js +403 -0
- package/src/permission/compatibility.d.ts +28 -0
- package/src/permission/compatibility.js +239 -0
- package/src/permission/index.d.ts +4 -0
- package/src/permission/index.js +3 -0
- package/src/permission/protocol.d.ts +167 -0
- package/src/permission/protocol.js +690 -0
- package/src/runtime/balancer.d.ts +19 -0
- package/src/runtime/balancer.js +149 -0
- package/src/runtime/compiled-artifact.d.ts +21 -0
- package/src/runtime/compiled-artifact.js +260 -0
- package/src/runtime/compiled-builder.d.ts +8 -0
- package/src/runtime/compiled-builder.js +42 -0
- package/src/runtime/compiled-worker.d.ts +27 -0
- package/src/runtime/compiled-worker.js +562 -0
- package/src/runtime/dispatcher.d.ts +40 -0
- package/src/runtime/dispatcher.js +142 -0
- package/src/runtime/inline-executor.d.ts +10 -0
- package/src/runtime/inline-executor.js +294 -0
- package/src/runtime/pool.d.ts +44 -0
- package/src/runtime/pool.js +472 -0
- package/src/runtime/process-worker.d.ts +92 -0
- package/src/runtime/process-worker.js +713 -0
- package/src/runtime/tx-queue.d.ts +28 -0
- package/src/runtime/tx-queue.js +158 -0
- package/src/shared/abortSignal.d.ts +23 -0
- package/src/shared/abortSignal.js +134 -0
- package/src/types.d.ts +399 -0
- package/src/types.js +2 -0
- package/src/utils/http.d.ts +29 -0
- package/src/utils/http.js +100 -0
- package/src/worker/bootstrap.d.ts +5 -0
- package/src/worker/bootstrap.js +78 -0
- package/src/worker/composable-runners.d.ts +12 -0
- package/src/worker/composable-runners.js +98 -0
- package/src/worker/loop.d.ts +3 -0
- package/src/worker/loop.js +395 -0
- package/src/worker/process-worker-bootstrap.d.ts +8 -0
- package/src/worker/process-worker-bootstrap.js +160 -0
- package/src/worker/rx-queue.d.ts +25 -0
- package/src/worker/rx-queue.js +173 -0
- package/src/worker/safety/index.d.ts +4 -0
- package/src/worker/safety/index.js +4 -0
- package/src/worker/safety/performance.d.ts +1 -0
- package/src/worker/safety/performance.js +17 -0
- package/src/worker/safety/process.d.ts +2 -0
- package/src/worker/safety/process.js +79 -0
- package/src/worker/safety/startup.d.ts +16 -0
- package/src/worker/safety/startup.js +30 -0
- package/src/worker/safety/worker-data.d.ts +2 -0
- package/src/worker/safety/worker-data.js +36 -0
- package/src/worker/task-loader.d.ts +28 -0
- package/src/worker/task-loader.js +76 -0
- package/src/worker/timers.d.ts +18 -0
- package/src/worker/timers.js +118 -0
- package/unsafe.d.ts +1 -71
- package/unsafe.js +1 -878
- package/utils.d.ts +1 -31
- package/utils.js +1 -116
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type CreateSharedMemoryOptions, type MapSharedMemoryOptions, type SharedMemoryConnectionPrimitives, type SharedMemoryMapping } from "./types.js";
|
|
2
|
+
export type NodeSharedMemoryNativeMapping = {
|
|
3
|
+
sab: SharedArrayBuffer;
|
|
4
|
+
fd: number;
|
|
5
|
+
name?: string;
|
|
6
|
+
size: number;
|
|
7
|
+
baseAddressMod64?: number;
|
|
8
|
+
};
|
|
9
|
+
export type NodeSharedMemoryAddon = {
|
|
10
|
+
createSharedMemory: (size: number, name?: string, mode?: CreateSharedMemoryOptions["mode"]) => NodeSharedMemoryNativeMapping;
|
|
11
|
+
mapSharedMemory: (fd: number, size: number, name?: string) => NodeSharedMemoryNativeMapping;
|
|
12
|
+
unlinkSharedMemory?: (name: string) => boolean;
|
|
13
|
+
};
|
|
14
|
+
export type NodeFutexWaitResult = "woken" | "changed" | "interrupted" | "timed-out";
|
|
15
|
+
export type NodeFutexAddon = {
|
|
16
|
+
waitU32: (buffer: ArrayBuffer | SharedArrayBuffer, byteOffset: number, expected: number, timeoutMs?: number) => NodeFutexWaitResult;
|
|
17
|
+
wakeU32: (buffer: ArrayBuffer | SharedArrayBuffer, byteOffset: number, count?: number) => number;
|
|
18
|
+
notifyU32?: (buffer: ArrayBuffer | SharedArrayBuffer, byteOffset: number, count?: number) => number;
|
|
19
|
+
sleep?: (milliseconds?: number) => void;
|
|
20
|
+
yield?: () => void;
|
|
21
|
+
};
|
|
22
|
+
export declare const DEFAULT_NODE_SHARED_MEMORY_ADDON = "../../build/Release/knitting_shared_memory.node";
|
|
23
|
+
export declare const DEFAULT_NODE_FUTEX_ADDON = "../../build/Release/knitting_shm.node";
|
|
24
|
+
export declare const loadNodeSharedMemoryAddon: (specifier?: string) => NodeSharedMemoryAddon;
|
|
25
|
+
export declare const loadNodeFutexAddon: (specifier?: string) => NodeFutexAddon;
|
|
26
|
+
export declare const fromNodeNativeMapping: (mapped: NodeSharedMemoryNativeMapping) => SharedMemoryMapping<SharedArrayBuffer>;
|
|
27
|
+
export declare const createNodeSharedMemory: (options: number | CreateSharedMemoryOptions, addon?: NodeSharedMemoryAddon) => SharedMemoryMapping<SharedArrayBuffer>;
|
|
28
|
+
export declare const mapNodeSharedMemory: (options: MapSharedMemoryOptions, addon?: NodeSharedMemoryAddon) => SharedMemoryMapping<SharedArrayBuffer>;
|
|
29
|
+
export declare const createNodeConnectionPrimitives: (addon?: NodeSharedMemoryAddon) => SharedMemoryConnectionPrimitives;
|
|
30
|
+
export declare const unlinkNodeSharedMemory: (name: string, addon?: NodeSharedMemoryAddon) => boolean;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { getNodeBuiltinModule } from "../common/node-compat.js";
|
|
2
|
+
import { loadNodeNativeAddon, } from "./node-addons.js";
|
|
3
|
+
import { createNodeFfiConnectionPrimitives, isNodeFfiTarget, } from "./node-ffi.js";
|
|
4
|
+
import { expectFd, expectPositiveSize, readCreateMode, readCreateName, readCreateSize, readRequiredCreateName, } from "./types.js";
|
|
5
|
+
export const DEFAULT_NODE_SHARED_MEMORY_ADDON = "../../build/Release/knitting_shared_memory.node";
|
|
6
|
+
export const DEFAULT_NODE_FUTEX_ADDON = "../../build/Release/knitting_shm.node";
|
|
7
|
+
const loadNodeAddon = (label, name, specifier) => {
|
|
8
|
+
const nodeModule = getNodeBuiltinModule("node:module");
|
|
9
|
+
if (nodeModule === undefined) {
|
|
10
|
+
throw new Error(`Node ${label} addon can only be loaded in Node`);
|
|
11
|
+
}
|
|
12
|
+
return loadNodeNativeAddon(nodeModule.createRequire(import.meta.url), name, specifier);
|
|
13
|
+
};
|
|
14
|
+
export const loadNodeSharedMemoryAddon = (specifier) => loadNodeAddon("shared memory", "knitting_shared_memory", specifier);
|
|
15
|
+
export const loadNodeFutexAddon = (specifier) => loadNodeAddon("futex", "knitting_shm", specifier);
|
|
16
|
+
export const fromNodeNativeMapping = (mapped) => {
|
|
17
|
+
const mapping = {
|
|
18
|
+
runtime: "node",
|
|
19
|
+
fd: mapped.fd,
|
|
20
|
+
size: mapped.size,
|
|
21
|
+
byteLength: mapped.sab.byteLength,
|
|
22
|
+
buffer: mapped.sab,
|
|
23
|
+
kind: "shared-array-buffer",
|
|
24
|
+
sab: mapped.sab,
|
|
25
|
+
baseAddressMod64: mapped.baseAddressMod64,
|
|
26
|
+
};
|
|
27
|
+
if (mapped.name !== undefined && mapped.name.length > 0) {
|
|
28
|
+
mapping.name = mapped.name;
|
|
29
|
+
}
|
|
30
|
+
return mapping;
|
|
31
|
+
};
|
|
32
|
+
export const createNodeSharedMemory = (options, addon = loadNodeSharedMemoryAddon()) => {
|
|
33
|
+
const size = expectPositiveSize(readCreateSize(options));
|
|
34
|
+
const mode = readCreateMode(options);
|
|
35
|
+
const name = mode === "anonymous"
|
|
36
|
+
? readCreateName(options, "knitting_shared_memory")
|
|
37
|
+
: readRequiredCreateName(options);
|
|
38
|
+
return fromNodeNativeMapping(addon.createSharedMemory(size, name, mode));
|
|
39
|
+
};
|
|
40
|
+
export const mapNodeSharedMemory = (options, addon = loadNodeSharedMemoryAddon()) => {
|
|
41
|
+
const fd = expectFd(options.fd);
|
|
42
|
+
const size = expectPositiveSize(options.size);
|
|
43
|
+
return fromNodeNativeMapping(addon.mapSharedMemory(fd, size, options.name));
|
|
44
|
+
};
|
|
45
|
+
export const createNodeConnectionPrimitives = (addon) => {
|
|
46
|
+
if (addon === undefined && isNodeFfiTarget()) {
|
|
47
|
+
return createNodeFfiConnectionPrimitives();
|
|
48
|
+
}
|
|
49
|
+
const resolvedAddon = addon ?? loadNodeSharedMemoryAddon();
|
|
50
|
+
return {
|
|
51
|
+
runtime: "node",
|
|
52
|
+
createSharedMemory: (options) => createNodeSharedMemory(options, resolvedAddon),
|
|
53
|
+
mapSharedMemory: (options) => mapNodeSharedMemory(options, resolvedAddon),
|
|
54
|
+
unlinkSharedMemory: (name) => unlinkNodeSharedMemory(name, resolvedAddon),
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
export const unlinkNodeSharedMemory = (name, addon = loadNodeSharedMemoryAddon()) => {
|
|
58
|
+
if (typeof addon.unlinkSharedMemory !== "function") {
|
|
59
|
+
throw new Error("Node shared memory addon cannot unlink named mappings");
|
|
60
|
+
}
|
|
61
|
+
return addon.unlinkSharedMemory(name);
|
|
62
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const NUMERIC_ARRAY_BRAND: unique symbol;
|
|
2
|
+
export declare class NumericArray extends Array<number> {
|
|
3
|
+
}
|
|
4
|
+
export declare const isNumericArray: (value: object) => value is NumericArray;
|
|
5
|
+
export declare const numericArrayFromFloat64: (view: Float64Array) => NumericArray;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const NUMERIC_ARRAY_BRAND = Symbol.for("knitting.numericArray");
|
|
2
|
+
export class NumericArray extends Array {
|
|
3
|
+
}
|
|
4
|
+
Object.defineProperty(NumericArray.prototype, NUMERIC_ARRAY_BRAND, {
|
|
5
|
+
value: true,
|
|
6
|
+
enumerable: false,
|
|
7
|
+
writable: false,
|
|
8
|
+
configurable: false,
|
|
9
|
+
});
|
|
10
|
+
export const isNumericArray = (value) => value[NUMERIC_ARRAY_BRAND] === true;
|
|
11
|
+
export const numericArrayFromFloat64 = (view) => {
|
|
12
|
+
const length = view.length;
|
|
13
|
+
const out = new NumericArray(length);
|
|
14
|
+
for (let i = 0; i < length; i++)
|
|
15
|
+
out[i] = view[i];
|
|
16
|
+
return out;
|
|
17
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { getNodeBuiltinModule } from "../common/node-compat.js";
|
|
2
|
+
let fsModule;
|
|
3
|
+
let pathModule;
|
|
4
|
+
let urlModule;
|
|
5
|
+
const requireNode = (value, specifier) => {
|
|
6
|
+
if (value === undefined) {
|
|
7
|
+
throw new Error(`${specifier} is not available in this runtime`);
|
|
8
|
+
}
|
|
9
|
+
return value;
|
|
10
|
+
};
|
|
11
|
+
const fs = () => requireNode(fsModule ??= getNodeBuiltinModule("node:fs"), "node:fs");
|
|
12
|
+
const path = () => requireNode(pathModule ??= getNodeBuiltinModule("node:path"), "node:path");
|
|
13
|
+
const url = () => requireNode(urlModule ??= getNodeBuiltinModule("node:url"), "node:url");
|
|
14
|
+
const safeExists = (target) => {
|
|
15
|
+
try {
|
|
16
|
+
return fs().existsSync(target);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const safeIsDirectory = (target) => {
|
|
23
|
+
try {
|
|
24
|
+
return fs().statSync(target).isDirectory();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const moduleDirectory = (moduleUrl) => {
|
|
31
|
+
const target = url().fileURLToPath(moduleUrl);
|
|
32
|
+
return safeIsDirectory(target) ? target : path().dirname(target);
|
|
33
|
+
};
|
|
34
|
+
const isKnittingPackageRoot = (dir) => safeExists(path().join(dir, "package.json")) &&
|
|
35
|
+
(safeExists(path().join(dir, "prebuilds")) ||
|
|
36
|
+
safeExists(path().join(dir, "src")) ||
|
|
37
|
+
safeExists(path().join(dir, "build")));
|
|
38
|
+
let cachedPackageRoot;
|
|
39
|
+
export const findKnittingPackageRoot = (moduleUrl = import.meta.url) => {
|
|
40
|
+
if (cachedPackageRoot !== undefined)
|
|
41
|
+
return cachedPackageRoot;
|
|
42
|
+
let current = moduleDirectory(moduleUrl);
|
|
43
|
+
while (true) {
|
|
44
|
+
if (isKnittingPackageRoot(current)) {
|
|
45
|
+
cachedPackageRoot = current;
|
|
46
|
+
return current;
|
|
47
|
+
}
|
|
48
|
+
const parent = path().dirname(current);
|
|
49
|
+
if (parent === current) {
|
|
50
|
+
throw new Error(`Could not locate knitting package root from ${moduleUrl}`);
|
|
51
|
+
}
|
|
52
|
+
current = parent;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
export const resolveKnittingPackageAsset = (...segments) => path().join(findKnittingPackageRoot(), ...segments);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export type PosixPlatform = "linux" | "darwin";
|
|
2
|
+
export declare const LINUX_LIBC_SO = "libc.so.6";
|
|
3
|
+
export declare const DARWIN_LIBSYSTEM = "/usr/lib/libSystem.B.dylib";
|
|
4
|
+
export declare const PROT_READ = 1;
|
|
5
|
+
export declare const PROT_WRITE = 2;
|
|
6
|
+
export declare const MAP_SHARED = 1;
|
|
7
|
+
export declare const O_RDWR = 2;
|
|
8
|
+
export declare const LINUX_O_CREAT = 64;
|
|
9
|
+
export declare const LINUX_O_EXCL = 128;
|
|
10
|
+
export declare const DARWIN_O_CREAT = 512;
|
|
11
|
+
export declare const DARWIN_O_EXCL = 2048;
|
|
12
|
+
export declare const DARWIN_SHM_MODE = 384;
|
|
13
|
+
export declare const POSIX_SHM_MODE = 384;
|
|
14
|
+
export declare const F_GETFD = 1;
|
|
15
|
+
export declare const F_SETFD = 2;
|
|
16
|
+
export declare const FD_CLOEXEC = 1;
|
|
17
|
+
export declare const encodeCString: (value: string) => Uint8Array;
|
|
18
|
+
export declare const getErrnoSymbolName: (platform: PosixPlatform) => string;
|
|
19
|
+
/** Best effort: the runtime can issue its own libc call before errno is read. */
|
|
20
|
+
export type ErrnoReader = () => number | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Wraps a bound `__error`/`__errno_location` into a reader that never throws.
|
|
23
|
+
* Runtimes differ only in how they turn the returned address into an int.
|
|
24
|
+
*/
|
|
25
|
+
export declare const makeErrnoReader: <Pointer>(locate: (() => Pointer) | undefined, readInt32: (pointer: Pointer) => number) => ErrnoReader;
|
|
26
|
+
/**
|
|
27
|
+
* Builds `<message>: EACCES (13)`, reading errno as late as possible, or the
|
|
28
|
+
* bare message when the runtime has no errno reader.
|
|
29
|
+
*/
|
|
30
|
+
export declare const posixError: (message: string, errno?: ErrnoReader, platform?: PosixPlatform) => Error;
|
|
31
|
+
export declare const checkPosixResult: (result: number, message: string, errno?: ErrnoReader, platform?: PosixPlatform) => number;
|
|
32
|
+
type FcntlLibc = {
|
|
33
|
+
symbols: {
|
|
34
|
+
fcntl: (fd: number, cmd: number, arg: number) => number;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Whether `shm_open`'s mode argument has to be delivered on the stack.
|
|
39
|
+
*
|
|
40
|
+
* macOS declares `shm_open(const char *, int, ...)` variadic and its libc
|
|
41
|
+
* wrapper reads the mode with `va_arg`. Apple's arm64 ABI passes variadic
|
|
42
|
+
* arguments on the stack, not in registers, so the mode a fixed-arity FFI call
|
|
43
|
+
* leaves in a register is never read: the segment is created with whatever the
|
|
44
|
+
* stack happened to hold. The creator's own descriptor still works, so the
|
|
45
|
+
* damage only surfaces when the name is opened again and fails with EACCES —
|
|
46
|
+
* and because the value comes from uninitialized stack, unrelated changes to
|
|
47
|
+
* the calling code can flip it between working and not.
|
|
48
|
+
*
|
|
49
|
+
* Declaring six extra register arguments moves the mode into the first stack
|
|
50
|
+
* slot, which is exactly where `va_arg` looks. Linux (fixed prototype) and
|
|
51
|
+
* x86-64 macOS (variadic arguments in registers) both want the plain form.
|
|
52
|
+
*/
|
|
53
|
+
export declare const shmOpenModeIsStackPassed: (platform?: PosixPlatform) => boolean;
|
|
54
|
+
/** Fills x2–x7 so the mode becomes the first stack argument. */
|
|
55
|
+
export declare const SHM_OPEN_STACK_PAD: readonly number[];
|
|
56
|
+
/**
|
|
57
|
+
* Proves a freshly created macOS segment can be reopened by name, closing the
|
|
58
|
+
* owner descriptor and throwing if it cannot.
|
|
59
|
+
*
|
|
60
|
+
* Without this, a mode that failed to apply produces a confusing EACCES in
|
|
61
|
+
* whichever process later opens the name, far from the call that caused it.
|
|
62
|
+
* No-ops off macOS, where the mode always applies.
|
|
63
|
+
*/
|
|
64
|
+
export declare const assertNamedSharedMemoryIsReopenable: (platform: PosixPlatform, ownerFd: number, reopen: () => number, close: (fd: number) => number, errno?: ErrnoReader) => void;
|
|
65
|
+
export declare const setCloseOnExec: <T extends FcntlLibc>(libc: T, fd: number) => number;
|
|
66
|
+
export declare const shmOpenCreateFlag: (platform: PosixPlatform) => number;
|
|
67
|
+
export declare const shmOpenExclusiveFlag: (platform: PosixPlatform) => number;
|
|
68
|
+
export declare const POSIX_SHM_MAX_NAME_LEN = 30;
|
|
69
|
+
export declare const toPosixSharedMemoryName: (name: string) => string;
|
|
70
|
+
export declare const detectPosixPlatform: () => PosixPlatform;
|
|
71
|
+
export declare const assertPosixSharedMemoryPlatform: (feature: string) => void;
|
|
72
|
+
export declare const getPosixLibcPath: (platform?: PosixPlatform) => string;
|
|
73
|
+
export declare const makeDarwinSharedMemoryName: (_name: string, runtime: string) => string;
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
export const LINUX_LIBC_SO = "libc.so.6";
|
|
2
|
+
export const DARWIN_LIBSYSTEM = "/usr/lib/libSystem.B.dylib";
|
|
3
|
+
export const PROT_READ = 1;
|
|
4
|
+
export const PROT_WRITE = 2;
|
|
5
|
+
export const MAP_SHARED = 1;
|
|
6
|
+
export const O_RDWR = 0x0002;
|
|
7
|
+
export const LINUX_O_CREAT = 0x0040;
|
|
8
|
+
export const LINUX_O_EXCL = 0x0080;
|
|
9
|
+
export const DARWIN_O_CREAT = 0x0200;
|
|
10
|
+
export const DARWIN_O_EXCL = 0x0800;
|
|
11
|
+
export const DARWIN_SHM_MODE = 0o600;
|
|
12
|
+
export const POSIX_SHM_MODE = 0o600;
|
|
13
|
+
export const F_GETFD = 1;
|
|
14
|
+
export const F_SETFD = 2;
|
|
15
|
+
export const FD_CLOEXEC = 1;
|
|
16
|
+
export const encodeCString = (value) => new TextEncoder().encode(`${value}\0`);
|
|
17
|
+
// libc exposes errno through a function returning its address: `__error()` on
|
|
18
|
+
// macOS, `__errno_location()` on Linux. FFI callers read the int behind it to
|
|
19
|
+
// turn a bare -1 into something diagnosable.
|
|
20
|
+
export const getErrnoSymbolName = (platform) => platform === "darwin" ? "__error" : "__errno_location";
|
|
21
|
+
// Only the codes shm_open/mmap/ftruncate/dup realistically return; anything
|
|
22
|
+
// else prints as a bare number.
|
|
23
|
+
const ERRNO_NAMES = {
|
|
24
|
+
1: "EPERM",
|
|
25
|
+
2: "ENOENT",
|
|
26
|
+
9: "EBADF",
|
|
27
|
+
12: "ENOMEM",
|
|
28
|
+
13: "EACCES",
|
|
29
|
+
17: "EEXIST",
|
|
30
|
+
22: "EINVAL",
|
|
31
|
+
24: "EMFILE",
|
|
32
|
+
28: "ENOSPC",
|
|
33
|
+
};
|
|
34
|
+
const PLATFORM_ERRNO_NAMES = {
|
|
35
|
+
darwin: { 45: "ENOTSUP", 63: "ENAMETOOLONG" },
|
|
36
|
+
linux: { 36: "ENAMETOOLONG", 95: "ENOTSUP" },
|
|
37
|
+
};
|
|
38
|
+
const isNullPointer = (pointer) => pointer === null || pointer === undefined || pointer === 0 || pointer === 0n;
|
|
39
|
+
/**
|
|
40
|
+
* Wraps a bound `__error`/`__errno_location` into a reader that never throws.
|
|
41
|
+
* Runtimes differ only in how they turn the returned address into an int.
|
|
42
|
+
*/
|
|
43
|
+
export const makeErrnoReader = (locate, readInt32) => {
|
|
44
|
+
if (locate === undefined)
|
|
45
|
+
return () => undefined;
|
|
46
|
+
return () => {
|
|
47
|
+
try {
|
|
48
|
+
const pointer = locate();
|
|
49
|
+
return isNullPointer(pointer) ? undefined : readInt32(pointer);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
const describeErrno = (errno, platform) => {
|
|
57
|
+
if (errno === undefined)
|
|
58
|
+
return "errno unavailable";
|
|
59
|
+
const name = PLATFORM_ERRNO_NAMES[platform][errno] ?? ERRNO_NAMES[errno];
|
|
60
|
+
return name === undefined ? `errno ${errno}` : `${name} (${errno})`;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Builds `<message>: EACCES (13)`, reading errno as late as possible, or the
|
|
64
|
+
* bare message when the runtime has no errno reader.
|
|
65
|
+
*/
|
|
66
|
+
export const posixError = (message, errno, platform = detectPosixPlatform()) => errno === undefined
|
|
67
|
+
? new Error(message)
|
|
68
|
+
: new Error(`${message}: ${describeErrno(errno(), platform)}`);
|
|
69
|
+
export const checkPosixResult = (result, message, errno, platform) => {
|
|
70
|
+
if (result < 0)
|
|
71
|
+
throw posixError(message, errno, platform);
|
|
72
|
+
return result;
|
|
73
|
+
};
|
|
74
|
+
const detectArch = () => globalThis
|
|
75
|
+
.Deno?.build?.arch ??
|
|
76
|
+
globalThis
|
|
77
|
+
.process?.arch ??
|
|
78
|
+
"";
|
|
79
|
+
/**
|
|
80
|
+
* Whether `shm_open`'s mode argument has to be delivered on the stack.
|
|
81
|
+
*
|
|
82
|
+
* macOS declares `shm_open(const char *, int, ...)` variadic and its libc
|
|
83
|
+
* wrapper reads the mode with `va_arg`. Apple's arm64 ABI passes variadic
|
|
84
|
+
* arguments on the stack, not in registers, so the mode a fixed-arity FFI call
|
|
85
|
+
* leaves in a register is never read: the segment is created with whatever the
|
|
86
|
+
* stack happened to hold. The creator's own descriptor still works, so the
|
|
87
|
+
* damage only surfaces when the name is opened again and fails with EACCES —
|
|
88
|
+
* and because the value comes from uninitialized stack, unrelated changes to
|
|
89
|
+
* the calling code can flip it between working and not.
|
|
90
|
+
*
|
|
91
|
+
* Declaring six extra register arguments moves the mode into the first stack
|
|
92
|
+
* slot, which is exactly where `va_arg` looks. Linux (fixed prototype) and
|
|
93
|
+
* x86-64 macOS (variadic arguments in registers) both want the plain form.
|
|
94
|
+
*/
|
|
95
|
+
export const shmOpenModeIsStackPassed = (platform = detectPosixPlatform()) => platform === "darwin" && /arm64|aarch64/.test(detectArch());
|
|
96
|
+
/** Fills x2–x7 so the mode becomes the first stack argument. */
|
|
97
|
+
export const SHM_OPEN_STACK_PAD = [0, 0, 0, 0, 0, 0];
|
|
98
|
+
/**
|
|
99
|
+
* Proves a freshly created macOS segment can be reopened by name, closing the
|
|
100
|
+
* owner descriptor and throwing if it cannot.
|
|
101
|
+
*
|
|
102
|
+
* Without this, a mode that failed to apply produces a confusing EACCES in
|
|
103
|
+
* whichever process later opens the name, far from the call that caused it.
|
|
104
|
+
* No-ops off macOS, where the mode always applies.
|
|
105
|
+
*/
|
|
106
|
+
export const assertNamedSharedMemoryIsReopenable = (platform, ownerFd, reopen, close, errno) => {
|
|
107
|
+
if (platform !== "darwin")
|
|
108
|
+
return;
|
|
109
|
+
const probe = reopen();
|
|
110
|
+
if (probe >= 0) {
|
|
111
|
+
close(probe);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const error = posixError("created shared memory could not be reopened by name", errno, platform);
|
|
115
|
+
close(ownerFd);
|
|
116
|
+
throw error;
|
|
117
|
+
};
|
|
118
|
+
export const setCloseOnExec = (libc, fd) => {
|
|
119
|
+
const flags = libc.symbols.fcntl(fd, F_GETFD, 0);
|
|
120
|
+
if (flags < 0) {
|
|
121
|
+
throw new Error("fcntl(F_GETFD) failed");
|
|
122
|
+
}
|
|
123
|
+
const result = libc.symbols.fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
|
|
124
|
+
if (result < 0) {
|
|
125
|
+
throw new Error("fcntl(F_SETFD) failed");
|
|
126
|
+
}
|
|
127
|
+
return fd;
|
|
128
|
+
};
|
|
129
|
+
export const shmOpenCreateFlag = (platform) => platform === "darwin" ? DARWIN_O_CREAT : LINUX_O_CREAT;
|
|
130
|
+
export const shmOpenExclusiveFlag = (platform) => platform === "darwin" ? DARWIN_O_EXCL : LINUX_O_EXCL;
|
|
131
|
+
// macOS limits POSIX shared memory names to 31 characters including the
|
|
132
|
+
// leading "/", so the usable name portion is at most 30 characters.
|
|
133
|
+
export const POSIX_SHM_MAX_NAME_LEN = 30;
|
|
134
|
+
export const toPosixSharedMemoryName = (name) => {
|
|
135
|
+
if (name.length === 0) {
|
|
136
|
+
throw new TypeError("shared memory name must be non-empty");
|
|
137
|
+
}
|
|
138
|
+
if (name.includes("\0")) {
|
|
139
|
+
throw new TypeError("shared memory name must not contain NUL bytes");
|
|
140
|
+
}
|
|
141
|
+
const out = name.startsWith("/") ? name : `/${name}`;
|
|
142
|
+
if (out.length < 2 || out.slice(1).includes("/")) {
|
|
143
|
+
throw new TypeError("POSIX shared memory name must not contain path separators");
|
|
144
|
+
}
|
|
145
|
+
if (out.length > POSIX_SHM_MAX_NAME_LEN + 1) {
|
|
146
|
+
throw new TypeError(`POSIX shared memory name must be at most ${POSIX_SHM_MAX_NAME_LEN} characters (macOS limit); got ${out.length - 1}`);
|
|
147
|
+
}
|
|
148
|
+
return out;
|
|
149
|
+
};
|
|
150
|
+
export const detectPosixPlatform = () => {
|
|
151
|
+
const denoOs = globalThis.Deno?.build?.os;
|
|
152
|
+
if (denoOs === "darwin" || denoOs === "linux")
|
|
153
|
+
return denoOs;
|
|
154
|
+
const processPlatform = globalThis.process?.platform;
|
|
155
|
+
if (processPlatform === "darwin" || processPlatform === "linux") {
|
|
156
|
+
return processPlatform;
|
|
157
|
+
}
|
|
158
|
+
throw new Error("shared memory connections support Linux and macOS only");
|
|
159
|
+
};
|
|
160
|
+
export const assertPosixSharedMemoryPlatform = (feature) => {
|
|
161
|
+
try {
|
|
162
|
+
detectPosixPlatform();
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
throw new Error(`${feature} is supported on Linux and macOS only`);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
export const getPosixLibcPath = (platform = detectPosixPlatform()) => platform === "darwin" ? DARWIN_LIBSYSTEM : LINUX_LIBC_SO;
|
|
169
|
+
export const makeDarwinSharedMemoryName = (_name, runtime) => {
|
|
170
|
+
const processId = globalThis.process?.pid ??
|
|
171
|
+
globalThis.Deno?.pid ??
|
|
172
|
+
0;
|
|
173
|
+
const runtimeTag = runtime.slice(0, 1) || "x";
|
|
174
|
+
const pidTag = Math.abs(processId).toString(36).slice(-5);
|
|
175
|
+
const timeTag = Date.now().toString(36).slice(-6);
|
|
176
|
+
const nonce = Math.random().toString(36).slice(2, 6);
|
|
177
|
+
return `/knit_${runtimeTag}_${pidTag}_${timeTag}_${nonce}`;
|
|
178
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { SharedBuffer, SharedBufferRegion } from "../common/shared-buffer-region.js";
|
|
2
|
+
import { FileDescriptor, type FileDescriptorMetadata } from "./file-descriptor.js";
|
|
3
|
+
import { type CreateSharedMemoryOptions, type SharedMemoryConnectionPrimitives, type SharedMemoryMapping } from "./types.js";
|
|
4
|
+
export type ProcessSharedBufferMetadata = {
|
|
5
|
+
version: 1;
|
|
6
|
+
descriptor: FileDescriptorMetadata;
|
|
7
|
+
byteOffset: number;
|
|
8
|
+
byteLength: number;
|
|
9
|
+
};
|
|
10
|
+
export declare const PROCESS_SHARED_BUFFER_BRAND: unique symbol;
|
|
11
|
+
export declare const PROCESS_SHARED_BUFFER_NUMERIC_TRANSFER: unique symbol;
|
|
12
|
+
declare const EXTERNAL_PAYLOAD_BRAND: unique symbol;
|
|
13
|
+
export type ProcessSharedBufferNumericMetadata = readonly [
|
|
14
|
+
fd: number,
|
|
15
|
+
size: number,
|
|
16
|
+
descriptorByteLength: number,
|
|
17
|
+
byteOffset: number,
|
|
18
|
+
byteLength: number,
|
|
19
|
+
runtime: number,
|
|
20
|
+
kind: number,
|
|
21
|
+
baseAddressMod64: number
|
|
22
|
+
];
|
|
23
|
+
export type ProcessSharedBufferRange = {
|
|
24
|
+
byteOffset?: number;
|
|
25
|
+
byteLength?: number;
|
|
26
|
+
};
|
|
27
|
+
export type ProcessSharedBufferCreator = Pick<SharedMemoryConnectionPrimitives, "createSharedMemory">;
|
|
28
|
+
export type ProcessSharedBufferMapper = Pick<SharedMemoryConnectionPrimitives, "mapSharedMemory">;
|
|
29
|
+
export type ProcessSharedBufferPrimitives = Pick<SharedMemoryConnectionPrimitives, "createSharedMemory" | "mapSharedMemory"> & {
|
|
30
|
+
unlinkSharedMemory?: (name: string) => boolean;
|
|
31
|
+
};
|
|
32
|
+
export type ProcessSharedBufferView = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | BigInt64Array | BigUint64Array | Float32Array | Float64Array;
|
|
33
|
+
export type ProcessSharedBufferViewConstructor<View extends ProcessSharedBufferView = ProcessSharedBufferView> = {
|
|
34
|
+
readonly BYTES_PER_ELEMENT: number;
|
|
35
|
+
new (buffer: SharedBuffer, byteOffset: number, length: number): View;
|
|
36
|
+
};
|
|
37
|
+
export declare const setDefaultProcessSharedBufferPrimitives: (primitives: ProcessSharedBufferPrimitives | undefined) => void;
|
|
38
|
+
export declare const getDefaultProcessSharedBufferPrimitives: () => ProcessSharedBufferPrimitives;
|
|
39
|
+
/**
|
|
40
|
+
* Zero-copy shared bytes for process workers.
|
|
41
|
+
*
|
|
42
|
+
* Use this when the boundary is a separate process, container, or sandbox.
|
|
43
|
+
* For thread workers, `SharedArrayBuffer` or `BufferReference` can be cheaper.
|
|
44
|
+
*/
|
|
45
|
+
export declare class ProcessSharedBuffer {
|
|
46
|
+
readonly [PROCESS_SHARED_BUFFER_BRAND] = true;
|
|
47
|
+
readonly [EXTERNAL_PAYLOAD_BRAND] = "knitting.processSharedBuffer";
|
|
48
|
+
readonly descriptor: FileDescriptor;
|
|
49
|
+
readonly byteOffset: number;
|
|
50
|
+
readonly byteLength: number;
|
|
51
|
+
constructor(descriptor: FileDescriptor, range?: ProcessSharedBufferRange);
|
|
52
|
+
static create(options: number | CreateSharedMemoryOptions, creator?: ProcessSharedBufferCreator): ProcessSharedBuffer;
|
|
53
|
+
static fromMapping(mapping: SharedMemoryMapping): ProcessSharedBuffer;
|
|
54
|
+
static fromDescriptor(descriptor: FileDescriptor, range?: ProcessSharedBufferRange): ProcessSharedBuffer;
|
|
55
|
+
static fromMetadata(metadata: unknown): ProcessSharedBuffer;
|
|
56
|
+
static parse(serialized: string): ProcessSharedBuffer;
|
|
57
|
+
static [PROCESS_SHARED_BUFFER_NUMERIC_TRANSFER](metadata: ProcessSharedBufferNumericMetadata): ProcessSharedBuffer;
|
|
58
|
+
get fd(): number;
|
|
59
|
+
get size(): number;
|
|
60
|
+
subbuffer(byteOffset: number, byteLength?: number): ProcessSharedBuffer;
|
|
61
|
+
getSharedArrayBuffer(mapper?: ProcessSharedBufferMapper): SharedArrayBuffer;
|
|
62
|
+
getSAB(mapper?: ProcessSharedBufferMapper): SharedArrayBuffer;
|
|
63
|
+
getBuffer(mapper?: ProcessSharedBufferMapper): SharedBuffer;
|
|
64
|
+
getRegion(mapper?: ProcessSharedBufferMapper): SharedBufferRegion;
|
|
65
|
+
view<View extends ProcessSharedBufferView>(constructor: ProcessSharedBufferViewConstructor<View>, mapper?: ProcessSharedBufferMapper): View;
|
|
66
|
+
bytes(mapper?: ProcessSharedBufferMapper): Uint8Array;
|
|
67
|
+
dataView(mapper?: ProcessSharedBufferMapper): DataView;
|
|
68
|
+
toMetadata(): ProcessSharedBufferMetadata;
|
|
69
|
+
toJSON(): ProcessSharedBufferMetadata;
|
|
70
|
+
stringify(): string;
|
|
71
|
+
stringifyMetadata(): string;
|
|
72
|
+
toString(): string;
|
|
73
|
+
}
|
|
74
|
+
export declare const isProcessSharedBufferValue: (value: unknown) => value is ProcessSharedBuffer;
|
|
75
|
+
export declare const parseProcessSharedBufferMetadata: (input: unknown) => ProcessSharedBufferMetadata;
|
|
76
|
+
export {};
|