knitting 0.1.46 → 0.1.50
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 +274 -77
- package/map.md +9 -3
- package/package.json +2 -1
- package/prebuilds/darwin-arm64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/darwin-x64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/darwin-x64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/linux-x64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/linux-x64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/win32-x64/knitting_windows_shared_memory.dll +0 -0
- package/prebuilds/win32-x64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/win32-x64-node-127/knitting_shm.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_shm.node +0 -0
- package/scripts/build-native-addons.ts +31 -5
- package/src/api.js +37 -13
- package/src/common/task-source.d.ts +1 -0
- package/src/common/task-source.js +1 -0
- package/src/connections/bun.d.ts +2 -0
- package/src/connections/bun.js +64 -9
- package/src/connections/deno.d.ts +2 -0
- package/src/connections/deno.js +64 -9
- package/src/connections/file-descriptor.d.ts +2 -0
- package/src/connections/file-descriptor.js +24 -2
- package/src/connections/node.d.ts +2 -1
- package/src/connections/node.js +17 -14
- package/src/connections/posix.d.ts +1 -0
- package/src/connections/posix.js +6 -0
- package/src/connections/process-shared-buffer.d.ts +3 -1
- package/src/connections/process-shared-buffer.js +17 -13
- package/src/connections/types.d.ts +2 -0
- package/src/connections/windows.d.ts +28 -0
- package/src/connections/windows.js +224 -0
- package/src/knitting_shared_memory.cc +310 -24
- package/src/knitting_windows_shared_memory.cc +156 -0
- package/src/memory/payloadCodec.js +1 -0
- package/src/runtime/pool.d.ts +1 -13
- package/src/runtime/pool.js +10 -542
- package/src/runtime/process-worker.d.ts +92 -0
- package/src/runtime/process-worker.js +670 -0
- package/src/runtime/tx-queue.d.ts +1 -1
- package/src/runtime/tx-queue.js +3 -1
- package/src/shared/abortSignal.d.ts +1 -1
- package/src/shared/abortSignal.js +10 -2
- package/src/types.d.ts +49 -3
- package/src/worker/bootstrap.d.ts +5 -0
- package/src/worker/bootstrap.js +78 -0
- package/src/worker/composable-runners.js +0 -8
- package/src/worker/loop.js +19 -154
- package/src/worker/process-worker-bootstrap.d.ts +8 -0
- package/src/worker/process-worker-bootstrap.js +159 -0
- package/src/worker/timers.js +19 -5
package/src/api.js
CHANGED
|
@@ -6,7 +6,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
6
6
|
}
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
|
-
import { getCallerFilePath } from "./common/task-source.js";
|
|
9
|
+
import { getCallerFilePath, getCallerHref } from "./common/task-source.js";
|
|
10
10
|
import { genTaskID } from "./common/task-source.js";
|
|
11
11
|
import { toModuleUrl } from "./common/module-url.js";
|
|
12
12
|
import { endpointSymbol } from "./common/task-symbol.js";
|
|
@@ -18,6 +18,7 @@ import { managerMethod } from "./runtime/balancer.js";
|
|
|
18
18
|
import { createInlineExecutor } from "./runtime/inline-executor.js";
|
|
19
19
|
const MAX_FUNCTION_ID = 0xFFFF;
|
|
20
20
|
const MAX_FUNCTION_COUNT = MAX_FUNCTION_ID + 1;
|
|
21
|
+
const DEFAULT_IMPORT_EXPORT_NAME = "default";
|
|
21
22
|
export const isMain = RUNTIME_IS_MAIN_THREAD;
|
|
22
23
|
export { endpointSymbol as endpointSymbol };
|
|
23
24
|
/**
|
|
@@ -44,6 +45,34 @@ export const toListAndIds = (args) => {
|
|
|
44
45
|
at: [...result[2]],
|
|
45
46
|
};
|
|
46
47
|
};
|
|
48
|
+
const resolveImportHref = (href, callerHref) => {
|
|
49
|
+
try {
|
|
50
|
+
return new URL(href, callerHref).href;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return toModuleUrl(href);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const resolveWorkerBootstrapSettings = (worker, callerHref) => {
|
|
57
|
+
const bootstrap = worker?.bootstrap;
|
|
58
|
+
if (bootstrap === undefined)
|
|
59
|
+
return worker;
|
|
60
|
+
const name = bootstrap.name ?? DEFAULT_IMPORT_EXPORT_NAME;
|
|
61
|
+
if (typeof bootstrap.href !== "string" || bootstrap.href.length === 0) {
|
|
62
|
+
throw new TypeError("worker.bootstrap.href must be a non-empty string");
|
|
63
|
+
}
|
|
64
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
65
|
+
throw new TypeError("worker.bootstrap.name must be a non-empty string");
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
...worker,
|
|
69
|
+
bootstrap: {
|
|
70
|
+
...bootstrap,
|
|
71
|
+
href: resolveImportHref(bootstrap.href, callerHref),
|
|
72
|
+
name,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
};
|
|
47
76
|
export const createPool = ({ threads, debug, inliner, balancer, payload, payloadInitialBytes, payloadMaxBytes, bufferMode, maxPayloadBytes, abortSignalCapacity, source, worker, workerExecArgv, permission, dispatcher, host, }) => (tasks) => {
|
|
48
77
|
/**
|
|
49
78
|
* This functions is only available in the main thread.
|
|
@@ -148,8 +177,12 @@ export const createPool = ({ threads, debug, inliner, balancer, payload, payload
|
|
|
148
177
|
const execArgv = sanitizeExecArgv(combinedExecArgv.length > 0 ? combinedExecArgv : undefined);
|
|
149
178
|
const hostDispatcher = host ?? dispatcher;
|
|
150
179
|
const usesAbortSignal = listOfFunctions.some((fn) => fn.abortSignal !== undefined);
|
|
151
|
-
const
|
|
152
|
-
|
|
180
|
+
const resolvedWorker = resolveWorkerBootstrapSettings(worker, getCallerHref(3));
|
|
181
|
+
if (usingInliner && resolvedWorker?.bootstrap !== undefined) {
|
|
182
|
+
throw new Error("worker.bootstrap cannot be used with the inliner");
|
|
183
|
+
}
|
|
184
|
+
const hardTimeoutMs = Number.isFinite(resolvedWorker?.hardTimeoutMs)
|
|
185
|
+
? Math.max(1, Math.floor(resolvedWorker?.hardTimeoutMs))
|
|
153
186
|
: undefined;
|
|
154
187
|
let workers = Array.from({
|
|
155
188
|
length: threads ?? 1,
|
|
@@ -161,7 +194,7 @@ export const createPool = ({ threads, debug, inliner, balancer, payload, payload
|
|
|
161
194
|
debug,
|
|
162
195
|
totalNumberOfThread,
|
|
163
196
|
source,
|
|
164
|
-
workerOptions:
|
|
197
|
+
workerOptions: resolvedWorker,
|
|
165
198
|
workerExecArgv: execArgv,
|
|
166
199
|
host: hostDispatcher,
|
|
167
200
|
payload,
|
|
@@ -303,7 +336,6 @@ export const createPool = ({ threads, debug, inliner, balancer, payload, payload
|
|
|
303
336
|
};
|
|
304
337
|
};
|
|
305
338
|
const SINGLE_TASK_KEY = "__task__";
|
|
306
|
-
const DEFAULT_IMPORT_EXPORT_NAME = "default";
|
|
307
339
|
const createSingleTaskPool = (single, options) => {
|
|
308
340
|
const pool = createPool(options ?? {})({
|
|
309
341
|
[SINGLE_TASK_KEY]: single,
|
|
@@ -334,14 +366,6 @@ const buildTaskDefinition = (input, callerOffset) => {
|
|
|
334
366
|
const [href, at] = getCallerFilePath(callerOffset);
|
|
335
367
|
return buildTaskDefinitionFromCaller(input, href, at);
|
|
336
368
|
};
|
|
337
|
-
const resolveImportHref = (href, callerHref) => {
|
|
338
|
-
try {
|
|
339
|
-
return new URL(href, callerHref).href;
|
|
340
|
-
}
|
|
341
|
-
catch {
|
|
342
|
-
return toModuleUrl(href);
|
|
343
|
-
}
|
|
344
|
-
};
|
|
345
369
|
const createImportedTaskFn = (href, exportName) => {
|
|
346
370
|
let cachedFn;
|
|
347
371
|
let cachedLoad;
|
|
@@ -71,6 +71,7 @@ const resolveCallerHref = (offset) => {
|
|
|
71
71
|
return toModuleUrl(caller);
|
|
72
72
|
};
|
|
73
73
|
const linkingMap = new Map();
|
|
74
|
+
export const getCallerHref = (offset = 3) => resolveCallerHref(offset);
|
|
74
75
|
export const getCallerFilePath = (offset = 3) => {
|
|
75
76
|
const href = resolveCallerHref(offset);
|
|
76
77
|
const at = linkingMap.get(href) ?? 0;
|
package/src/connections/bun.d.ts
CHANGED
|
@@ -16,5 +16,7 @@ type BunLibc = {
|
|
|
16
16
|
export declare const openBunLibc: () => BunLibc;
|
|
17
17
|
export declare const mapBunSharedMemory: (options: MapSharedMemoryOptions, libc?: BunLibc) => SharedMemoryMapping<ArrayBuffer>;
|
|
18
18
|
export declare const createBunSharedMemory: (options: number | CreateSharedMemoryOptions, libc?: BunLibc) => SharedMemoryMapping<ArrayBuffer>;
|
|
19
|
+
export declare const unlinkBunSharedMemory: (name: string, libc?: BunLibc) => boolean;
|
|
20
|
+
export declare const createBunPosixConnectionPrimitives: (libc?: BunLibc) => SharedMemoryConnectionPrimitives<SharedMemoryMapping<ArrayBuffer>>;
|
|
19
21
|
export declare const createBunConnectionPrimitives: (libc?: BunLibc) => SharedMemoryConnectionPrimitives<SharedMemoryMapping<ArrayBuffer>>;
|
|
20
22
|
export {};
|
package/src/connections/bun.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { DARWIN_O_CREAT, DARWIN_O_EXCL, DARWIN_SHM_MODE, setCloseOnExec, detectPosixPlatform, encodeCString, getPosixLibcPath, makeDarwinSharedMemoryName, MAP_SHARED, O_RDWR, PROT_READ, PROT_WRITE, } from "./posix.js";
|
|
2
|
-
import { expectFd, expectPositiveSize, readCreateName, readCreateSize, } from "./types.js";
|
|
1
|
+
import { DARWIN_O_CREAT, DARWIN_O_EXCL, DARWIN_SHM_MODE, LINUX_O_CREAT, LINUX_O_EXCL, setCloseOnExec, detectPosixPlatform, encodeCString, getPosixLibcPath, makeDarwinSharedMemoryName, MAP_SHARED, O_RDWR, POSIX_SHM_MODE, PROT_READ, PROT_WRITE, toPosixSharedMemoryName, } from "./posix.js";
|
|
2
|
+
import { expectFd, expectPositiveSize, readCreateName, readCreateMode, readRequiredCreateName, readCreateSize, } from "./types.js";
|
|
3
|
+
import { createBunWindowsConnectionPrimitives, isWindowsRuntime, } from "./windows.js";
|
|
3
4
|
const FFIType = {
|
|
4
5
|
i32: 5,
|
|
5
6
|
i64: 7,
|
|
@@ -65,6 +66,14 @@ const getBunCreateSymbols = (platform = detectPosixPlatform()) => platform === "
|
|
|
65
66
|
args: [FFIType.ptr, FFIType.u32],
|
|
66
67
|
returns: FFIType.i32,
|
|
67
68
|
},
|
|
69
|
+
shm_open: {
|
|
70
|
+
args: [FFIType.ptr, FFIType.i32, FFIType.u32],
|
|
71
|
+
returns: FFIType.i32,
|
|
72
|
+
},
|
|
73
|
+
shm_unlink: {
|
|
74
|
+
args: [FFIType.ptr],
|
|
75
|
+
returns: FFIType.i32,
|
|
76
|
+
},
|
|
68
77
|
};
|
|
69
78
|
const checkResult = (result, message) => {
|
|
70
79
|
if (result < 0)
|
|
@@ -103,11 +112,33 @@ const createBunSharedMemoryFd = (name, platform, libc) => {
|
|
|
103
112
|
throw error;
|
|
104
113
|
}
|
|
105
114
|
};
|
|
115
|
+
const createNamedBunSharedMemoryFd = (name, mode, platform, libc) => {
|
|
116
|
+
const shmOpen = libc.symbols.shm_open;
|
|
117
|
+
if (shmOpen === undefined) {
|
|
118
|
+
throw new Error("shm_open symbol is not available");
|
|
119
|
+
}
|
|
120
|
+
const createFlags = platform === "darwin" ? DARWIN_O_CREAT : LINUX_O_CREAT;
|
|
121
|
+
const exclusiveFlags = platform === "darwin" ? DARWIN_O_EXCL : LINUX_O_EXCL;
|
|
122
|
+
const flags = mode === "create"
|
|
123
|
+
? O_RDWR | createFlags | exclusiveFlags
|
|
124
|
+
: O_RDWR;
|
|
125
|
+
const fd = checkResult(shmOpen(encodeCString(toPosixSharedMemoryName(name)), flags, platform === "darwin" ? DARWIN_SHM_MODE : POSIX_SHM_MODE), "shm_open failed");
|
|
126
|
+
try {
|
|
127
|
+
return setCloseOnExec(libc, fd);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
libc.symbols.close(fd);
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
106
134
|
export const mapBunSharedMemory = (options, libc = openBunLibc()) => {
|
|
107
|
-
const sourceFd = expectFd(options.fd);
|
|
135
|
+
const sourceFd = options.name === undefined ? expectFd(options.fd) : -1;
|
|
108
136
|
const size = expectPositiveSize(options.size);
|
|
109
137
|
let fd = sourceFd;
|
|
110
|
-
if (options.
|
|
138
|
+
if (options.name !== undefined) {
|
|
139
|
+
fd = createNamedBunSharedMemoryFd(options.name, "open", detectPosixPlatform(), libc);
|
|
140
|
+
}
|
|
141
|
+
else if (options.duplicateFd !== false) {
|
|
111
142
|
fd = checkResult(libc.symbols.dup(sourceFd), "dup(fd) failed");
|
|
112
143
|
try {
|
|
113
144
|
setCloseOnExec(libc, fd);
|
|
@@ -127,6 +158,7 @@ export const mapBunSharedMemory = (options, libc = openBunLibc()) => {
|
|
|
127
158
|
return {
|
|
128
159
|
runtime: "bun",
|
|
129
160
|
fd,
|
|
161
|
+
name: options.name,
|
|
130
162
|
size,
|
|
131
163
|
byteLength: arrayBuffer.byteLength,
|
|
132
164
|
buffer: arrayBuffer,
|
|
@@ -141,19 +173,42 @@ export const mapBunSharedMemory = (options, libc = openBunLibc()) => {
|
|
|
141
173
|
};
|
|
142
174
|
export const createBunSharedMemory = (options, libc = openBunLibc()) => {
|
|
143
175
|
const size = expectPositiveSize(readCreateSize(options));
|
|
144
|
-
const
|
|
145
|
-
const
|
|
176
|
+
const mode = readCreateMode(options);
|
|
177
|
+
const name = mode === "anonymous"
|
|
178
|
+
? readCreateName(options, "knitting_shared_memory")
|
|
179
|
+
: readRequiredCreateName(options);
|
|
180
|
+
const platform = detectPosixPlatform();
|
|
181
|
+
const fd = mode === "anonymous"
|
|
182
|
+
? createBunSharedMemoryFd(name, platform, libc)
|
|
183
|
+
: createNamedBunSharedMemoryFd(name, mode, platform, libc);
|
|
146
184
|
try {
|
|
147
|
-
|
|
148
|
-
|
|
185
|
+
if (mode !== "open") {
|
|
186
|
+
checkResult(libc.symbols.ftruncate(fd, BigInt(size)), "ftruncate failed");
|
|
187
|
+
}
|
|
188
|
+
const mapping = mapBunSharedMemory({ fd, size, duplicateFd: false }, libc);
|
|
189
|
+
if (mode !== "anonymous") {
|
|
190
|
+
return { ...mapping, name };
|
|
191
|
+
}
|
|
192
|
+
return mapping;
|
|
149
193
|
}
|
|
150
194
|
catch (error) {
|
|
151
195
|
libc.symbols.close(fd);
|
|
152
196
|
throw error;
|
|
153
197
|
}
|
|
154
198
|
};
|
|
155
|
-
export const
|
|
199
|
+
export const unlinkBunSharedMemory = (name, libc = openBunLibc()) => {
|
|
200
|
+
const shmUnlink = libc.symbols.shm_unlink;
|
|
201
|
+
if (shmUnlink === undefined) {
|
|
202
|
+
throw new Error("shm_unlink symbol is not available");
|
|
203
|
+
}
|
|
204
|
+
return shmUnlink(encodeCString(toPosixSharedMemoryName(name))) === 0;
|
|
205
|
+
};
|
|
206
|
+
export const createBunPosixConnectionPrimitives = (libc = openBunLibc()) => ({
|
|
156
207
|
runtime: "bun",
|
|
157
208
|
createSharedMemory: (options) => createBunSharedMemory(options, libc),
|
|
158
209
|
mapSharedMemory: (options) => mapBunSharedMemory(options, libc),
|
|
210
|
+
unlinkSharedMemory: (name) => unlinkBunSharedMemory(name, libc),
|
|
159
211
|
});
|
|
212
|
+
export const createBunConnectionPrimitives = (libc) => isWindowsRuntime()
|
|
213
|
+
? createBunWindowsConnectionPrimitives()
|
|
214
|
+
: createBunPosixConnectionPrimitives(libc);
|
|
@@ -16,5 +16,7 @@ type DenoLibc = {
|
|
|
16
16
|
export declare const openDenoLibc: () => DenoLibc;
|
|
17
17
|
export declare const mapDenoSharedMemory: (options: MapSharedMemoryOptions, libc?: DenoLibc) => SharedMemoryMapping<ArrayBuffer>;
|
|
18
18
|
export declare const createDenoSharedMemory: (options: number | CreateSharedMemoryOptions, libc?: DenoLibc) => SharedMemoryMapping<ArrayBuffer>;
|
|
19
|
+
export declare const unlinkDenoSharedMemory: (name: string, libc?: DenoLibc) => boolean;
|
|
20
|
+
export declare const createDenoPosixConnectionPrimitives: (libc?: DenoLibc) => SharedMemoryConnectionPrimitives<SharedMemoryMapping<ArrayBuffer>>;
|
|
19
21
|
export declare const createDenoConnectionPrimitives: (libc?: DenoLibc) => SharedMemoryConnectionPrimitives<SharedMemoryMapping<ArrayBuffer>>;
|
|
20
22
|
export {};
|
package/src/connections/deno.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { DARWIN_O_CREAT, DARWIN_O_EXCL, DARWIN_SHM_MODE, setCloseOnExec, detectPosixPlatform, encodeCString, getPosixLibcPath, makeDarwinSharedMemoryName, MAP_SHARED, O_RDWR, PROT_READ, PROT_WRITE, } from "./posix.js";
|
|
2
|
-
import { expectFd, expectPositiveSize, readCreateName, readCreateSize, } from "./types.js";
|
|
1
|
+
import { DARWIN_O_CREAT, DARWIN_O_EXCL, DARWIN_SHM_MODE, LINUX_O_CREAT, LINUX_O_EXCL, setCloseOnExec, detectPosixPlatform, encodeCString, getPosixLibcPath, makeDarwinSharedMemoryName, MAP_SHARED, O_RDWR, POSIX_SHM_MODE, PROT_READ, PROT_WRITE, toPosixSharedMemoryName, } from "./posix.js";
|
|
2
|
+
import { expectFd, expectPositiveSize, readCreateName, readCreateMode, readRequiredCreateName, readCreateSize, } from "./types.js";
|
|
3
|
+
import { createDenoWindowsConnectionPrimitives, isWindowsRuntime, } from "./windows.js";
|
|
3
4
|
const getDeno = () => {
|
|
4
5
|
const deno = globalThis.Deno;
|
|
5
6
|
if (deno === undefined) {
|
|
@@ -50,6 +51,14 @@ const getDenoCreateSymbols = (platform = detectPosixPlatform()) => platform ===
|
|
|
50
51
|
parameters: ["buffer", "u32"],
|
|
51
52
|
result: "i32",
|
|
52
53
|
},
|
|
54
|
+
shm_open: {
|
|
55
|
+
parameters: ["buffer", "i32", "u32"],
|
|
56
|
+
result: "i32",
|
|
57
|
+
},
|
|
58
|
+
shm_unlink: {
|
|
59
|
+
parameters: ["buffer"],
|
|
60
|
+
result: "i32",
|
|
61
|
+
},
|
|
53
62
|
};
|
|
54
63
|
const checkResult = (result, message) => {
|
|
55
64
|
if (result < 0)
|
|
@@ -93,11 +102,33 @@ const createDenoSharedMemoryFd = (name, platform, libc) => {
|
|
|
93
102
|
throw error;
|
|
94
103
|
}
|
|
95
104
|
};
|
|
105
|
+
const createNamedDenoSharedMemoryFd = (name, mode, platform, libc) => {
|
|
106
|
+
const shmOpen = libc.symbols.shm_open;
|
|
107
|
+
if (shmOpen === undefined) {
|
|
108
|
+
throw new Error("shm_open symbol is not available");
|
|
109
|
+
}
|
|
110
|
+
const createFlags = platform === "darwin" ? DARWIN_O_CREAT : LINUX_O_CREAT;
|
|
111
|
+
const exclusiveFlags = platform === "darwin" ? DARWIN_O_EXCL : LINUX_O_EXCL;
|
|
112
|
+
const flags = mode === "create"
|
|
113
|
+
? O_RDWR | createFlags | exclusiveFlags
|
|
114
|
+
: O_RDWR;
|
|
115
|
+
const fd = checkResult(shmOpen(encodeCString(toPosixSharedMemoryName(name)), flags, platform === "darwin" ? DARWIN_SHM_MODE : POSIX_SHM_MODE), "shm_open failed");
|
|
116
|
+
try {
|
|
117
|
+
return setCloseOnExec(libc, fd);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
libc.symbols.close(fd);
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
96
124
|
export const mapDenoSharedMemory = (options, libc = openDenoLibc()) => {
|
|
97
|
-
const sourceFd = expectFd(options.fd);
|
|
125
|
+
const sourceFd = options.name === undefined ? expectFd(options.fd) : -1;
|
|
98
126
|
const size = expectPositiveSize(options.size);
|
|
99
127
|
let fd = sourceFd;
|
|
100
|
-
if (options.
|
|
128
|
+
if (options.name !== undefined) {
|
|
129
|
+
fd = createNamedDenoSharedMemoryFd(options.name, "open", detectPosixPlatform(), libc);
|
|
130
|
+
}
|
|
131
|
+
else if (options.duplicateFd !== false) {
|
|
101
132
|
fd = checkResult(libc.symbols.dup(sourceFd), "dup(fd) failed");
|
|
102
133
|
try {
|
|
103
134
|
setCloseOnExec(libc, fd);
|
|
@@ -118,6 +149,7 @@ export const mapDenoSharedMemory = (options, libc = openDenoLibc()) => {
|
|
|
118
149
|
return {
|
|
119
150
|
runtime: "deno",
|
|
120
151
|
fd,
|
|
152
|
+
name: options.name,
|
|
121
153
|
size,
|
|
122
154
|
byteLength: arrayBuffer.byteLength,
|
|
123
155
|
buffer: arrayBuffer,
|
|
@@ -132,19 +164,42 @@ export const mapDenoSharedMemory = (options, libc = openDenoLibc()) => {
|
|
|
132
164
|
};
|
|
133
165
|
export const createDenoSharedMemory = (options, libc = openDenoLibc()) => {
|
|
134
166
|
const size = expectPositiveSize(readCreateSize(options));
|
|
135
|
-
const
|
|
136
|
-
const
|
|
167
|
+
const mode = readCreateMode(options);
|
|
168
|
+
const name = mode === "anonymous"
|
|
169
|
+
? readCreateName(options, "knitting_shared_memory")
|
|
170
|
+
: readRequiredCreateName(options);
|
|
171
|
+
const platform = detectPosixPlatform();
|
|
172
|
+
const fd = mode === "anonymous"
|
|
173
|
+
? createDenoSharedMemoryFd(name, platform, libc)
|
|
174
|
+
: createNamedDenoSharedMemoryFd(name, mode, platform, libc);
|
|
137
175
|
try {
|
|
138
|
-
|
|
139
|
-
|
|
176
|
+
if (mode !== "open") {
|
|
177
|
+
checkResult(libc.symbols.ftruncate(fd, BigInt(size)), "ftruncate failed");
|
|
178
|
+
}
|
|
179
|
+
const mapping = mapDenoSharedMemory({ fd, size, duplicateFd: false }, libc);
|
|
180
|
+
if (mode !== "anonymous") {
|
|
181
|
+
return { ...mapping, name };
|
|
182
|
+
}
|
|
183
|
+
return mapping;
|
|
140
184
|
}
|
|
141
185
|
catch (error) {
|
|
142
186
|
libc.symbols.close(fd);
|
|
143
187
|
throw error;
|
|
144
188
|
}
|
|
145
189
|
};
|
|
146
|
-
export const
|
|
190
|
+
export const unlinkDenoSharedMemory = (name, libc = openDenoLibc()) => {
|
|
191
|
+
const shmUnlink = libc.symbols.shm_unlink;
|
|
192
|
+
if (shmUnlink === undefined) {
|
|
193
|
+
throw new Error("shm_unlink symbol is not available");
|
|
194
|
+
}
|
|
195
|
+
return shmUnlink(encodeCString(toPosixSharedMemoryName(name))) === 0;
|
|
196
|
+
};
|
|
197
|
+
export const createDenoPosixConnectionPrimitives = (libc = openDenoLibc()) => ({
|
|
147
198
|
runtime: "deno",
|
|
148
199
|
createSharedMemory: (options) => createDenoSharedMemory(options, libc),
|
|
149
200
|
mapSharedMemory: (options) => mapDenoSharedMemory(options, libc),
|
|
201
|
+
unlinkSharedMemory: (name) => unlinkDenoSharedMemory(name, libc),
|
|
150
202
|
});
|
|
203
|
+
export const createDenoConnectionPrimitives = (libc) => isWindowsRuntime()
|
|
204
|
+
? createDenoWindowsConnectionPrimitives()
|
|
205
|
+
: createDenoPosixConnectionPrimitives(libc);
|
|
@@ -2,6 +2,7 @@ import { type ConnectionRuntime, type SharedMemoryBuffer, type SharedMemoryConne
|
|
|
2
2
|
export type FileDescriptorMetadata = {
|
|
3
3
|
version: 1;
|
|
4
4
|
fd: number;
|
|
5
|
+
name?: string;
|
|
5
6
|
size: number;
|
|
6
7
|
byteLength: number;
|
|
7
8
|
runtime?: ConnectionRuntime;
|
|
@@ -12,6 +13,7 @@ type FileDescriptorMapper = Pick<SharedMemoryConnectionPrimitives, "mapSharedMem
|
|
|
12
13
|
export declare class FileDescriptor {
|
|
13
14
|
#private;
|
|
14
15
|
readonly fd: number;
|
|
16
|
+
readonly name?: string;
|
|
15
17
|
readonly size: number;
|
|
16
18
|
readonly byteLength: number;
|
|
17
19
|
readonly runtime?: ConnectionRuntime;
|
|
@@ -24,8 +24,20 @@ const readOptionalNumber = (value, label) => {
|
|
|
24
24
|
}
|
|
25
25
|
return Math.trunc(value);
|
|
26
26
|
};
|
|
27
|
+
const readOptionalName = (value) => {
|
|
28
|
+
if (value === undefined)
|
|
29
|
+
return undefined;
|
|
30
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
31
|
+
throw new TypeError("file descriptor name must be a non-empty string when provided");
|
|
32
|
+
}
|
|
33
|
+
if (value.includes("\0")) {
|
|
34
|
+
throw new TypeError("file descriptor name must not contain NUL bytes");
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
};
|
|
27
38
|
export class FileDescriptor {
|
|
28
39
|
fd;
|
|
40
|
+
name;
|
|
29
41
|
size;
|
|
30
42
|
byteLength;
|
|
31
43
|
runtime;
|
|
@@ -34,6 +46,7 @@ export class FileDescriptor {
|
|
|
34
46
|
#mapping;
|
|
35
47
|
constructor(metadata, mapping) {
|
|
36
48
|
this.fd = expectFd(metadata.fd);
|
|
49
|
+
this.name = metadata.name;
|
|
37
50
|
this.size = expectPositiveSize(metadata.size);
|
|
38
51
|
this.byteLength = expectPositiveSize(metadata.byteLength);
|
|
39
52
|
this.runtime = metadata.runtime;
|
|
@@ -45,6 +58,7 @@ export class FileDescriptor {
|
|
|
45
58
|
return new FileDescriptor({
|
|
46
59
|
version: 1,
|
|
47
60
|
fd: mapping.fd,
|
|
61
|
+
name: mapping.name,
|
|
48
62
|
size: mapping.size,
|
|
49
63
|
byteLength: mapping.byteLength,
|
|
50
64
|
runtime: mapping.runtime,
|
|
@@ -59,7 +73,7 @@ export class FileDescriptor {
|
|
|
59
73
|
return FileDescriptor.fromMetadata(serialized);
|
|
60
74
|
}
|
|
61
75
|
toMetadata() {
|
|
62
|
-
|
|
76
|
+
const metadata = {
|
|
63
77
|
version: 1,
|
|
64
78
|
fd: this.fd,
|
|
65
79
|
size: this.size,
|
|
@@ -68,6 +82,9 @@ export class FileDescriptor {
|
|
|
68
82
|
kind: this.kind,
|
|
69
83
|
baseAddressMod64: this.baseAddressMod64,
|
|
70
84
|
};
|
|
85
|
+
if (this.name !== undefined)
|
|
86
|
+
metadata.name = this.name;
|
|
87
|
+
return metadata;
|
|
71
88
|
}
|
|
72
89
|
toJSON() {
|
|
73
90
|
return this.toMetadata();
|
|
@@ -92,6 +109,7 @@ export class FileDescriptor {
|
|
|
92
109
|
map(mapper) {
|
|
93
110
|
const options = {
|
|
94
111
|
fd: this.fd,
|
|
112
|
+
name: this.name,
|
|
95
113
|
size: this.size,
|
|
96
114
|
};
|
|
97
115
|
this.#mapping = mapper.mapSharedMemory(options);
|
|
@@ -125,7 +143,7 @@ export const parseFileDescriptorMetadata = (input) => {
|
|
|
125
143
|
if (value.version !== 1) {
|
|
126
144
|
throw new TypeError("unsupported file descriptor metadata version");
|
|
127
145
|
}
|
|
128
|
-
|
|
146
|
+
const parsed = {
|
|
129
147
|
version: 1,
|
|
130
148
|
fd: expectFd(readOptionalNumber(value.fd, "fd") ?? -1),
|
|
131
149
|
size: expectPositiveSize(readOptionalNumber(value.size, "size") ?? 0),
|
|
@@ -136,4 +154,8 @@ export const parseFileDescriptorMetadata = (input) => {
|
|
|
136
154
|
kind: readOptionalKind(value.kind),
|
|
137
155
|
baseAddressMod64: readOptionalNumber(value.baseAddressMod64, "baseAddressMod64"),
|
|
138
156
|
};
|
|
157
|
+
const name = readOptionalName(value.name);
|
|
158
|
+
if (name !== undefined)
|
|
159
|
+
parsed.name = name;
|
|
160
|
+
return parsed;
|
|
139
161
|
};
|
|
@@ -2,12 +2,13 @@ import { type CreateSharedMemoryOptions, type MapSharedMemoryOptions, type Share
|
|
|
2
2
|
export type NodeSharedMemoryNativeMapping = {
|
|
3
3
|
sab: SharedArrayBuffer;
|
|
4
4
|
fd: number;
|
|
5
|
+
name?: string;
|
|
5
6
|
size: number;
|
|
6
7
|
baseAddressMod64?: number;
|
|
7
8
|
};
|
|
8
9
|
export type NodeSharedMemoryAddon = {
|
|
9
10
|
createSharedMemory: (size: number, name?: string, mode?: CreateSharedMemoryOptions["mode"]) => NodeSharedMemoryNativeMapping;
|
|
10
|
-
mapSharedMemory: (fd: number, size: number) => NodeSharedMemoryNativeMapping;
|
|
11
|
+
mapSharedMemory: (fd: number, size: number, name?: string) => NodeSharedMemoryNativeMapping;
|
|
11
12
|
unlinkSharedMemory?: (name: string) => boolean;
|
|
12
13
|
};
|
|
13
14
|
export type NodeFutexWaitResult = "woken" | "changed" | "interrupted" | "timed-out";
|
package/src/connections/node.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { getNodeBuiltinModule } from "../common/node-compat.js";
|
|
2
2
|
import { loadNodeNativeAddon } from "./node-addons.js";
|
|
3
|
-
import { assertPosixSharedMemoryPlatform } from "./posix.js";
|
|
4
3
|
import { expectFd, expectPositiveSize, readCreateMode, readCreateName, readRequiredCreateName, readCreateSize, } from "./types.js";
|
|
5
4
|
export const DEFAULT_NODE_SHARED_MEMORY_ADDON = "../../build/Release/knitting_shared_memory.node";
|
|
6
5
|
export const DEFAULT_NODE_FUTEX_ADDON = "../../build/Release/knitting_shm.node";
|
|
7
6
|
export const loadNodeSharedMemoryAddon = (specifier) => {
|
|
8
|
-
assertPosixSharedMemoryPlatform("Node native shared memory");
|
|
9
7
|
const nodeModule = getNodeBuiltinModule("node:module");
|
|
10
8
|
if (nodeModule === undefined) {
|
|
11
9
|
throw new Error("Node shared memory addon can only be loaded in Node");
|
|
@@ -14,7 +12,6 @@ export const loadNodeSharedMemoryAddon = (specifier) => {
|
|
|
14
12
|
return loadNodeNativeAddon(require, "knitting_shared_memory", specifier);
|
|
15
13
|
};
|
|
16
14
|
export const loadNodeFutexAddon = (specifier) => {
|
|
17
|
-
assertPosixSharedMemoryPlatform("Node native futex helpers");
|
|
18
15
|
const nodeModule = getNodeBuiltinModule("node:module");
|
|
19
16
|
if (nodeModule === undefined) {
|
|
20
17
|
throw new Error("Node futex addon can only be loaded in Node");
|
|
@@ -22,16 +19,22 @@ export const loadNodeFutexAddon = (specifier) => {
|
|
|
22
19
|
const require = nodeModule.createRequire(import.meta.url);
|
|
23
20
|
return loadNodeNativeAddon(require, "knitting_shm", specifier);
|
|
24
21
|
};
|
|
25
|
-
export const fromNodeNativeMapping = (mapped) =>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
22
|
+
export const fromNodeNativeMapping = (mapped) => {
|
|
23
|
+
const mapping = {
|
|
24
|
+
runtime: "node",
|
|
25
|
+
fd: mapped.fd,
|
|
26
|
+
size: mapped.size,
|
|
27
|
+
byteLength: mapped.sab.byteLength,
|
|
28
|
+
buffer: mapped.sab,
|
|
29
|
+
kind: "shared-array-buffer",
|
|
30
|
+
sab: mapped.sab,
|
|
31
|
+
baseAddressMod64: mapped.baseAddressMod64,
|
|
32
|
+
};
|
|
33
|
+
if (mapped.name !== undefined && mapped.name.length > 0) {
|
|
34
|
+
mapping.name = mapped.name;
|
|
35
|
+
}
|
|
36
|
+
return mapping;
|
|
37
|
+
};
|
|
35
38
|
export const createNodeSharedMemory = (options, addon = loadNodeSharedMemoryAddon()) => {
|
|
36
39
|
const size = expectPositiveSize(readCreateSize(options));
|
|
37
40
|
const mode = readCreateMode(options);
|
|
@@ -43,7 +46,7 @@ export const createNodeSharedMemory = (options, addon = loadNodeSharedMemoryAddo
|
|
|
43
46
|
export const mapNodeSharedMemory = (options, addon = loadNodeSharedMemoryAddon()) => {
|
|
44
47
|
const fd = expectFd(options.fd);
|
|
45
48
|
const size = expectPositiveSize(options.size);
|
|
46
|
-
return fromNodeNativeMapping(addon.mapSharedMemory(fd, size));
|
|
49
|
+
return fromNodeNativeMapping(addon.mapSharedMemory(fd, size, options.name));
|
|
47
50
|
};
|
|
48
51
|
export const createNodeConnectionPrimitives = (addon = loadNodeSharedMemoryAddon()) => ({
|
|
49
52
|
runtime: "node",
|
|
@@ -23,6 +23,7 @@ type FcntlLibc = {
|
|
|
23
23
|
export declare const setCloseOnExec: <T extends FcntlLibc>(libc: T, fd: number) => number;
|
|
24
24
|
export declare const shmOpenCreateFlag: (platform: PosixPlatform) => number;
|
|
25
25
|
export declare const shmOpenExclusiveFlag: (platform: PosixPlatform) => number;
|
|
26
|
+
export declare const POSIX_SHM_MAX_NAME_LEN = 30;
|
|
26
27
|
export declare const toPosixSharedMemoryName: (name: string) => string;
|
|
27
28
|
export declare const detectPosixPlatform: () => PosixPlatform;
|
|
28
29
|
export declare const assertPosixSharedMemoryPlatform: (feature: string) => void;
|
package/src/connections/posix.js
CHANGED
|
@@ -27,6 +27,9 @@ export const setCloseOnExec = (libc, fd) => {
|
|
|
27
27
|
};
|
|
28
28
|
export const shmOpenCreateFlag = (platform) => platform === "darwin" ? DARWIN_O_CREAT : LINUX_O_CREAT;
|
|
29
29
|
export const shmOpenExclusiveFlag = (platform) => platform === "darwin" ? DARWIN_O_EXCL : LINUX_O_EXCL;
|
|
30
|
+
// macOS limits POSIX shared memory names to 31 characters including the
|
|
31
|
+
// leading "/", so the usable name portion is at most 30 characters.
|
|
32
|
+
export const POSIX_SHM_MAX_NAME_LEN = 30;
|
|
30
33
|
export const toPosixSharedMemoryName = (name) => {
|
|
31
34
|
if (name.length === 0) {
|
|
32
35
|
throw new TypeError("shared memory name must be non-empty");
|
|
@@ -38,6 +41,9 @@ export const toPosixSharedMemoryName = (name) => {
|
|
|
38
41
|
if (out.length < 2 || out.slice(1).includes("/")) {
|
|
39
42
|
throw new TypeError("POSIX shared memory name must not contain path separators");
|
|
40
43
|
}
|
|
44
|
+
if (out.length > POSIX_SHM_MAX_NAME_LEN + 1) {
|
|
45
|
+
throw new TypeError(`POSIX shared memory name must be at most ${POSIX_SHM_MAX_NAME_LEN} characters (macOS limit); got ${out.length - 1}`);
|
|
46
|
+
}
|
|
41
47
|
return out;
|
|
42
48
|
};
|
|
43
49
|
export const detectPosixPlatform = () => {
|
|
@@ -26,7 +26,9 @@ export type ProcessSharedBufferRange = {
|
|
|
26
26
|
};
|
|
27
27
|
export type ProcessSharedBufferCreator = Pick<SharedMemoryConnectionPrimitives, "createSharedMemory">;
|
|
28
28
|
export type ProcessSharedBufferMapper = Pick<SharedMemoryConnectionPrimitives, "mapSharedMemory">;
|
|
29
|
-
export type ProcessSharedBufferPrimitives = Pick<SharedMemoryConnectionPrimitives, "createSharedMemory" | "mapSharedMemory"
|
|
29
|
+
export type ProcessSharedBufferPrimitives = Pick<SharedMemoryConnectionPrimitives, "createSharedMemory" | "mapSharedMemory"> & {
|
|
30
|
+
unlinkSharedMemory?: (name: string) => boolean;
|
|
31
|
+
};
|
|
30
32
|
export type ProcessSharedBufferView = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | BigInt64Array | BigUint64Array | Float32Array | Float64Array;
|
|
31
33
|
export type ProcessSharedBufferViewConstructor<View extends ProcessSharedBufferView = ProcessSharedBufferView> = {
|
|
32
34
|
readonly BYTES_PER_ELEMENT: number;
|
|
@@ -4,7 +4,6 @@ import { RUNTIME } from "../common/runtime.js";
|
|
|
4
4
|
import { createBunConnectionPrimitives } from "./bun.js";
|
|
5
5
|
import { createDenoConnectionPrimitives } from "./deno.js";
|
|
6
6
|
import { loadNodeNativeAddon } from "./node-addons.js";
|
|
7
|
-
import { assertPosixSharedMemoryPlatform } from "./posix.js";
|
|
8
7
|
import { expectFd, expectPositiveSize, readCreateMode, readCreateName, readRequiredCreateName, readCreateSize, } from "./types.js";
|
|
9
8
|
export const PROCESS_SHARED_BUFFER_BRAND = Symbol.for("knitting.processSharedBuffer");
|
|
10
9
|
export const PROCESS_SHARED_BUFFER_NUMERIC_TRANSFER = Symbol.for("knitting.processSharedBuffer.numericTransfer");
|
|
@@ -40,16 +39,22 @@ const decodeKind = (value) => {
|
|
|
40
39
|
}
|
|
41
40
|
};
|
|
42
41
|
let defaultPrimitives;
|
|
43
|
-
const fromDefaultNodeNativeMapping = (mapped) =>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
const fromDefaultNodeNativeMapping = (mapped) => {
|
|
43
|
+
const mapping = {
|
|
44
|
+
runtime: "node",
|
|
45
|
+
fd: mapped.fd,
|
|
46
|
+
size: mapped.size,
|
|
47
|
+
byteLength: mapped.sab.byteLength,
|
|
48
|
+
buffer: mapped.sab,
|
|
49
|
+
kind: "shared-array-buffer",
|
|
50
|
+
sab: mapped.sab,
|
|
51
|
+
baseAddressMod64: mapped.baseAddressMod64,
|
|
52
|
+
};
|
|
53
|
+
if (mapped.name !== undefined && mapped.name.length > 0) {
|
|
54
|
+
mapping.name = mapped.name;
|
|
55
|
+
}
|
|
56
|
+
return mapping;
|
|
57
|
+
};
|
|
53
58
|
const createDefaultNodePrimitives = () => {
|
|
54
59
|
const nodeModule = getNodeBuiltinModule("node:module");
|
|
55
60
|
if (nodeModule === undefined) {
|
|
@@ -69,12 +74,11 @@ const createDefaultNodePrimitives = () => {
|
|
|
69
74
|
mapSharedMemory: (options) => {
|
|
70
75
|
const fd = expectFd(options.fd);
|
|
71
76
|
const size = expectPositiveSize(options.size);
|
|
72
|
-
return fromDefaultNodeNativeMapping(addon.mapSharedMemory(fd, size));
|
|
77
|
+
return fromDefaultNodeNativeMapping(addon.mapSharedMemory(fd, size, options.name));
|
|
73
78
|
},
|
|
74
79
|
};
|
|
75
80
|
};
|
|
76
81
|
const createDefaultPrimitives = () => {
|
|
77
|
-
assertPosixSharedMemoryPlatform("ProcessSharedBuffer");
|
|
78
82
|
if (RUNTIME === "bun")
|
|
79
83
|
return createBunConnectionPrimitives();
|
|
80
84
|
if (RUNTIME === "deno")
|
|
@@ -6,6 +6,7 @@ export type SharedMemoryCreateMode = "anonymous" | "create" | "open";
|
|
|
6
6
|
export type SharedMemoryMapping<Buffer extends SharedMemoryBuffer = SharedMemoryBuffer> = {
|
|
7
7
|
runtime: ConnectionRuntime;
|
|
8
8
|
fd: number;
|
|
9
|
+
name?: string;
|
|
9
10
|
size: number;
|
|
10
11
|
byteLength: number;
|
|
11
12
|
buffer: Buffer;
|
|
@@ -28,6 +29,7 @@ export type CreateSharedMemoryOptions = {
|
|
|
28
29
|
};
|
|
29
30
|
export type MapSharedMemoryOptions = {
|
|
30
31
|
fd: number;
|
|
32
|
+
name?: string;
|
|
31
33
|
size: number;
|
|
32
34
|
duplicateFd?: boolean;
|
|
33
35
|
};
|