knitting 0.1.56 → 0.1.61
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/BROWSER.md +134 -0
- package/README.md +347 -46
- package/knitting.browser.d.ts +6 -0
- package/knitting.browser.js +1 -0
- package/knitting.d.ts +3 -2
- package/knitting.js +3 -3
- package/map.md +21 -9
- package/package.json +17 -2
- package/prebuilds/darwin-arm64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-arm64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/darwin-arm64-node-127/knitting_shm.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_shm.node +0 -0
- package/prebuilds/win32-x64/knitting_windows_shared_memory.dll +0 -0
- package/prebuilds/win32-x64-node-127/knitting_buffer_pointer.node +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_buffer_pointer.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-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/src/api.d.ts +24 -1
- package/src/api.js +311 -27
- package/src/common/module-url.d.ts +1 -0
- package/src/common/module-url.js +30 -2
- package/src/common/node-compat.d.ts +2 -0
- package/src/common/path-canonical.js +20 -6
- package/src/common/runtime.d.ts +3 -1
- package/src/common/runtime.js +25 -4
- package/src/common/task-source.d.ts +2 -0
- package/src/common/task-source.js +17 -1
- package/src/common/worker-runtime.js +16 -7
- package/src/connections/buffer-reference-native.js +25 -1
- package/src/connections/buffer-reference.d.ts +23 -2
- package/src/connections/buffer-reference.js +143 -29
- package/src/connections/bun.d.ts +1 -1
- package/src/connections/bun.js +25 -14
- package/src/connections/deno.d.ts +3 -1
- package/src/connections/deno.js +35 -14
- package/src/connections/external-array-buffer.d.ts +2 -0
- package/src/connections/external-array-buffer.js +33 -0
- package/src/connections/node-addons.d.ts +11 -0
- package/src/connections/node-addons.js +49 -1
- 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 +1 -1
- package/src/connections/node.js +20 -20
- package/src/connections/package-assets.js +25 -15
- package/src/connections/posix.d.ts +42 -0
- package/src/connections/posix.js +101 -0
- package/src/connections/process-shared-buffer.d.ts +1 -0
- package/src/connections/process-shared-buffer.js +11 -45
- package/src/connections/types.d.ts +4 -0
- package/src/connections/windows.d.ts +19 -10
- package/src/connections/windows.js +87 -66
- package/src/error.d.ts +7 -6
- package/src/error.js +8 -7
- package/src/memory/lock.d.ts +112 -77
- package/src/memory/lock.js +314 -87
- package/src/memory/payloadCodec.d.ts +11 -2
- package/src/memory/payloadCodec.js +129 -37
- package/src/memory/regionRegistry.d.ts +1 -3
- package/src/memory/regionRegistry.js +16 -14
- package/src/permission/compatibility.d.ts +28 -0
- package/src/permission/compatibility.js +239 -0
- package/src/permission/index.d.ts +2 -0
- package/src/permission/index.js +1 -0
- package/src/permission/protocol.d.ts +1 -0
- package/src/permission/protocol.js +59 -15
- 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.js +9 -0
- package/src/runtime/inline-executor.js +27 -15
- package/src/runtime/pool.d.ts +121 -5
- package/src/runtime/pool.js +263 -49
- package/src/runtime/process-worker.d.ts +3 -13
- package/src/runtime/process-worker.js +97 -101
- package/src/runtime/tx-queue.d.ts +18 -2
- package/src/runtime/tx-queue.js +56 -16
- package/src/runtime/worker-common.d.ts +13 -0
- package/src/runtime/worker-common.js +102 -0
- package/src/types.d.ts +89 -9
- package/src/worker/composable-runners.js +3 -2
- package/src/worker/loop.d.ts +1 -0
- package/src/worker/loop.js +52 -17
- package/src/worker/rx-queue.d.ts +11 -1
- package/src/worker/rx-queue.js +4 -1
- package/src/worker/task-loader.d.ts +5 -4
- package/src/worker/task-loader.js +13 -7
- package/src/worker/timers.d.ts +8 -1
- package/src/worker/timers.js +28 -23
package/src/runtime/pool.js
CHANGED
|
@@ -1,22 +1,36 @@
|
|
|
1
1
|
import { createHostTxQueue } from "./tx-queue.js";
|
|
2
|
-
import { cleanupProcessWorkerMemoryQuietly, createProcessSharedMemoryAllocator, createProcessWorkerMemoryLayout, createProcessWorkerNativeSignalNotifier, readProcessSharedMemorySettings, readProcessWorkerCommandPrefix, readProcessWorkerRuntime,
|
|
2
|
+
import { cleanupProcessWorkerMemoryQuietly, createProcessSharedMemoryAllocator, createProcessWorkerMemoryLayout, createProcessWorkerNativeSignalNotifier, readProcessSharedMemorySettings, readProcessWorkerCommandPrefix, readProcessWorkerRuntime, spawnProcessWorker, toProcessWorkerBootPayload, } from "./process-worker.js";
|
|
3
|
+
import { serializeWorkerBootstrapData, terminateWorkerQuietly, toWorkerCompatExecArgv, toWorkerSafeExecArgv, } from "./worker-common.js";
|
|
3
4
|
import { createSharedMemoryTransport, TRANSPORT_SIGNAL_BYTES, } from "../ipc/transport/shared-memory.js";
|
|
4
5
|
import { ChannelHandler, hostDispatcherLoop, } from "./dispatcher.js";
|
|
5
6
|
import { HEADER_SLOT_STRIDE_U32, lock2, LOCK_SECTOR_BYTE_LENGTH, LockBound, } from "../memory/lock.js";
|
|
7
|
+
// Side-effect import: registers the payload codec (cycle break for Andromeda;
|
|
8
|
+
// see lock.ts). Must run before any lock2() call.
|
|
9
|
+
import "../memory/payloadCodec.js";
|
|
6
10
|
import "../worker/loop.js";
|
|
7
|
-
import { createSharedArrayBuffer, createWasmSharedArrayBuffer, } from "../common/runtime.js";
|
|
11
|
+
import { createSharedArrayBuffer, createWasmSharedArrayBuffer, IS_BROWSER, } from "../common/runtime.js";
|
|
8
12
|
import { HAS_NODE_WORKER_THREADS, RUNTIME_WORKER, } from "../common/worker-runtime.js";
|
|
9
13
|
import { probeLockBufferTextCompat } from "../common/shared-buffer-text.js";
|
|
10
14
|
import { signalAbortFactory } from "../shared/abortSignal.js";
|
|
11
15
|
import { createLockControlCarpet } from "../memory/byte-carpet.js";
|
|
12
16
|
import { resolvePayloadBufferOptions, } from "../memory/payload-config.js";
|
|
13
|
-
import { createBufferReferenceReturnReleaseMessage } from "../connections/buffer-reference.js";
|
|
17
|
+
import { createBufferReferenceReturnReleaseMessage, detachArrayBufferBestEffort, } from "../connections/buffer-reference.js";
|
|
18
|
+
import { spawnCompiledWorkerContext } from "./compiled-worker.js";
|
|
14
19
|
const WORKER_FATAL_MESSAGE_KEY = "__knittingWorkerFatal";
|
|
15
20
|
const isWorkerFatalMessage = (value) => !!value &&
|
|
16
21
|
typeof value === "object" &&
|
|
17
22
|
typeof value[WORKER_FATAL_MESSAGE_KEY] === "string";
|
|
18
23
|
// Keep idle workers self-healing if an Atomics.notify wake is missed.
|
|
19
24
|
const DEFAULT_WORKER_PARK_MS = 1;
|
|
25
|
+
const DEFAULT_ABORT_SIGNAL_CAPACITY = 258;
|
|
26
|
+
const sanitizePositiveInteger = (value) => {
|
|
27
|
+
if (!Number.isFinite(value))
|
|
28
|
+
return undefined;
|
|
29
|
+
const parsed = Math.floor(value);
|
|
30
|
+
return parsed > 0 ? parsed : undefined;
|
|
31
|
+
};
|
|
32
|
+
const resolveAbortSignalCapacity = (value) => sanitizePositiveInteger(value) ?? DEFAULT_ABORT_SIGNAL_CAPACITY;
|
|
33
|
+
const abortSignalByteLength = (capacity) => Math.max(1, Math.ceil(capacity / 32)) * Uint32Array.BYTES_PER_ELEMENT;
|
|
20
34
|
const withDefaultWorkerTimers = (options) => {
|
|
21
35
|
const parkMs = options?.timers?.parkMs ?? DEFAULT_WORKER_PARK_MS;
|
|
22
36
|
if (options === undefined)
|
|
@@ -34,7 +48,121 @@ const withFixedPayloadConfig = (config) => ({
|
|
|
34
48
|
mode: "fixed",
|
|
35
49
|
payloadInitialBytes: config.payloadMaxByteLength,
|
|
36
50
|
});
|
|
37
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Build the buffers, host-side locks and pending registry that a stealing pool
|
|
53
|
+
* shares. One submit region for every worker to claim from, one private return
|
|
54
|
+
* region per worker, and a single pool-global queue so a response arriving on
|
|
55
|
+
* any lane settles the right promise.
|
|
56
|
+
*
|
|
57
|
+
* Region width follows paper §6.1: the largest power-of-two `g` leaving a spare
|
|
58
|
+
* region for a delayed claimant (`slots / g >= consumers + 1`). At 32 slots that
|
|
59
|
+
* caps how wide a region can be well before it caps the worker count.
|
|
60
|
+
*/
|
|
61
|
+
export const resolveStealRegionLanes = (consumers) => {
|
|
62
|
+
for (let lanes = LockBound.slots; lanes >= 1; lanes >>= 1) {
|
|
63
|
+
if (LockBound.slots / lanes >= consumers + 1)
|
|
64
|
+
return lanes;
|
|
65
|
+
}
|
|
66
|
+
throw new RangeError(`${consumers} stealing workers need more than ${LockBound.slots} lanes`);
|
|
67
|
+
};
|
|
68
|
+
/** Maximum claimants that leave the protocol's required spare region. */
|
|
69
|
+
export const MAX_STEAL_CONSUMERS = LockBound.slots - 1;
|
|
70
|
+
export const createStealPoolBuffers = ({ threads, payload, regionLanes, abortSignalCapacity, usesAbortSignal, }) => {
|
|
71
|
+
const payloadConfig = resolvePayloadBufferOptions({ options: payload });
|
|
72
|
+
const makePayload = () => payloadConfig.mode === "growable"
|
|
73
|
+
? createSharedArrayBuffer(payloadConfig.payloadInitialBytes, payloadConfig.payloadMaxByteLength)
|
|
74
|
+
: createSharedArrayBuffer(payloadConfig.payloadInitialBytes);
|
|
75
|
+
const carpet = () => createLockControlCarpet({
|
|
76
|
+
signalBytes: 0,
|
|
77
|
+
abortBytes: 0,
|
|
78
|
+
lockSectorBytes: LOCK_SECTOR_BYTE_LENGTH,
|
|
79
|
+
headerSlotStrideU32: HEADER_SLOT_STRIDE_U32,
|
|
80
|
+
slotCount: LockBound.slots,
|
|
81
|
+
headerLayout: "split",
|
|
82
|
+
});
|
|
83
|
+
const toBuffers = (half, payloadSab) => ({
|
|
84
|
+
...half,
|
|
85
|
+
payload: payloadSab,
|
|
86
|
+
textCompat: probeLockBufferTextCompat({
|
|
87
|
+
headers: half.headers,
|
|
88
|
+
payload: payloadSab,
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
const maxLanes = resolveStealRegionLanes(threads);
|
|
92
|
+
const lanes = regionLanes === undefined
|
|
93
|
+
? maxLanes
|
|
94
|
+
: Math.min(Math.max(1, regionLanes | 0), maxLanes);
|
|
95
|
+
const submitCarpet = carpet();
|
|
96
|
+
const submitBuffers = toBuffers(submitCarpet.lock, makePayload());
|
|
97
|
+
const hostSubmitLock = lock2({
|
|
98
|
+
headers: submitBuffers.headers,
|
|
99
|
+
headerSlotStrideU32: submitBuffers.headerSlotStrideU32,
|
|
100
|
+
LockBoundSector: submitBuffers.lockSector,
|
|
101
|
+
payload: submitBuffers.payload,
|
|
102
|
+
payloadSector: submitBuffers.payloadSector,
|
|
103
|
+
payloadConfig,
|
|
104
|
+
textCompat: submitBuffers.textCompat,
|
|
105
|
+
consumers: threads,
|
|
106
|
+
regionLanes: lanes,
|
|
107
|
+
});
|
|
108
|
+
const returnBuffers = [];
|
|
109
|
+
const hostReturnLocks = [];
|
|
110
|
+
for (let i = 0; i < threads; i++) {
|
|
111
|
+
const buffers = toBuffers(carpet().returnLock, makePayload());
|
|
112
|
+
returnBuffers.push(buffers);
|
|
113
|
+
hostReturnLocks.push(lock2({
|
|
114
|
+
headers: buffers.headers,
|
|
115
|
+
headerSlotStrideU32: buffers.headerSlotStrideU32,
|
|
116
|
+
LockBoundSector: buffers.lockSector,
|
|
117
|
+
payload: buffers.payload,
|
|
118
|
+
payloadSector: buffers.payloadSector,
|
|
119
|
+
payloadConfig,
|
|
120
|
+
textCompat: buffers.textCompat,
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
// Abort-aware tasks can execute on any claimant, so all workers and the
|
|
124
|
+
// pool-global queue must observe one bitmap. Per-lane abort buffers would
|
|
125
|
+
// encode an id from one allocator and test it against another lane's bits.
|
|
126
|
+
const resolvedAbortSignalCapacity = resolveAbortSignalCapacity(abortSignalCapacity);
|
|
127
|
+
const abortSignalSAB = usesAbortSignal === true
|
|
128
|
+
? createSharedArrayBuffer(abortSignalByteLength(resolvedAbortSignalCapacity))
|
|
129
|
+
: undefined;
|
|
130
|
+
const abortSignals = abortSignalSAB === undefined
|
|
131
|
+
? undefined
|
|
132
|
+
: signalAbortFactory({
|
|
133
|
+
sab: abortSignalSAB,
|
|
134
|
+
maxSignals: resolvedAbortSignalCapacity,
|
|
135
|
+
});
|
|
136
|
+
const sharedQueue = createHostTxQueue({
|
|
137
|
+
lock: hostSubmitLock,
|
|
138
|
+
returnLock: hostReturnLocks[0],
|
|
139
|
+
extraReturnLocks: hostReturnLocks.slice(1),
|
|
140
|
+
abortSignals,
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
submitBuffers,
|
|
144
|
+
returnBuffers,
|
|
145
|
+
hostSubmitLock,
|
|
146
|
+
hostReturnLocks,
|
|
147
|
+
sharedQueue,
|
|
148
|
+
regionLanes: lanes,
|
|
149
|
+
abortSignalSAB,
|
|
150
|
+
abortSignalMax: abortSignalSAB === undefined
|
|
151
|
+
? undefined
|
|
152
|
+
: resolvedAbortSignalCapacity,
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostDebug, totalNumberOfThread, source, at, workerOptions, workerExecArgv, permission, host, payload, bufferReferenceReturn, abortSignalCapacity, usesAbortSignal, sharedChannelHandler, stealPool, }) => {
|
|
156
|
+
if (workerOptions?.runtime === "compiled") {
|
|
157
|
+
return spawnCompiledWorkerContext({
|
|
158
|
+
list,
|
|
159
|
+
names,
|
|
160
|
+
workerOptions,
|
|
161
|
+
hostDebug,
|
|
162
|
+
abortSignalCapacity,
|
|
163
|
+
usesAbortSignal,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
38
166
|
const tsFileUrl = new URL(import.meta.url);
|
|
39
167
|
const poliWorker = RUNTIME_WORKER;
|
|
40
168
|
const resolvedWorkerOptions = serializeWorkerBootstrapData(withDefaultWorkerTimers(workerOptions));
|
|
@@ -51,32 +179,32 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostD
|
|
|
51
179
|
if (!useProcessWorkerRuntime && typeof poliWorker !== "function") {
|
|
52
180
|
throw new Error("Worker is not available in this runtime");
|
|
53
181
|
}
|
|
182
|
+
// Without this the failure is a `ReferenceError` from whichever module
|
|
183
|
+
// happens to allocate first.
|
|
184
|
+
if (IS_BROWSER && typeof SharedArrayBuffer !== "function") {
|
|
185
|
+
throw new Error("SharedArrayBuffer is unavailable: serve the page cross-origin isolated " +
|
|
186
|
+
"(Cross-Origin-Opener-Policy: same-origin, " +
|
|
187
|
+
"Cross-Origin-Embedder-Policy: require-corp).");
|
|
188
|
+
}
|
|
54
189
|
const WorkerCtor = poliWorker;
|
|
55
190
|
// Lock buffers must be shared between host and worker.
|
|
56
|
-
const sanitizeBytes =
|
|
57
|
-
if (!Number.isFinite(value))
|
|
58
|
-
return undefined;
|
|
59
|
-
const bytes = Math.floor(value);
|
|
60
|
-
return bytes > 0 ? bytes : undefined;
|
|
61
|
-
};
|
|
191
|
+
const sanitizeBytes = sanitizePositiveInteger;
|
|
62
192
|
const basePayloadConfig = resolvePayloadBufferOptions({
|
|
63
193
|
options: payload,
|
|
64
194
|
});
|
|
65
195
|
const resolvedPayloadConfig = useProcessWorkerRuntime
|
|
66
196
|
? withFixedPayloadConfig(basePayloadConfig)
|
|
67
197
|
: basePayloadConfig;
|
|
68
|
-
const
|
|
69
|
-
const requestedAbortSignalCapacity = sanitizeBytes(abortSignalCapacity);
|
|
70
|
-
const resolvedAbortSignalCapacity = requestedAbortSignalCapacity ??
|
|
71
|
-
defaultAbortSignalCapacity;
|
|
72
|
-
const abortSignalWords = Math.max(1, Math.ceil(resolvedAbortSignalCapacity / 32));
|
|
198
|
+
const resolvedAbortSignalCapacity = resolveAbortSignalCapacity(abortSignalCapacity);
|
|
73
199
|
const requestedSignalBytes = sanitizeBytes(sab?.size);
|
|
74
200
|
const externalSignalSab = sab?.sharedSab;
|
|
75
201
|
if (useProcessWorkerRuntime && externalSignalSab != null) {
|
|
76
202
|
throw new Error("process worker runtime cannot use an external signal buffer");
|
|
77
203
|
}
|
|
78
204
|
const signalBytes = Math.max(TRANSPORT_SIGNAL_BYTES, requestedSignalBytes ?? TRANSPORT_SIGNAL_BYTES);
|
|
79
|
-
const abortBytes =
|
|
205
|
+
const abortBytes = stealPool === undefined && usesAbortSignal === true
|
|
206
|
+
? abortSignalByteLength(resolvedAbortSignalCapacity)
|
|
207
|
+
: 0;
|
|
80
208
|
const processWorkerMemory = useProcessWorkerRuntime
|
|
81
209
|
? createProcessWorkerMemoryLayout({
|
|
82
210
|
signalBytes,
|
|
@@ -118,7 +246,7 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostD
|
|
|
118
246
|
const controlLayout = processWorkerMemory?.controlLayout ??
|
|
119
247
|
makeLockControlLayout();
|
|
120
248
|
const lockPayload = processWorkerMemory?.lockPayload ?? makePayloadBuffer();
|
|
121
|
-
const lockBuffers = {
|
|
249
|
+
const lockBuffers = stealPool?.submitBuffers ?? {
|
|
122
250
|
...controlLayout.lock,
|
|
123
251
|
payload: lockPayload,
|
|
124
252
|
textCompat: probeLockBufferTextCompat({
|
|
@@ -128,7 +256,7 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostD
|
|
|
128
256
|
};
|
|
129
257
|
const returnPayload = processWorkerMemory?.returnPayload ??
|
|
130
258
|
makePayloadBuffer();
|
|
131
|
-
const returnLockBuffers = {
|
|
259
|
+
const returnLockBuffers = stealPool?.returnBuffers ?? {
|
|
132
260
|
...controlLayout.returnLock,
|
|
133
261
|
payload: returnPayload,
|
|
134
262
|
textCompat: probeLockBufferTextCompat({
|
|
@@ -136,7 +264,7 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostD
|
|
|
136
264
|
payload: returnPayload,
|
|
137
265
|
}),
|
|
138
266
|
};
|
|
139
|
-
const lock = lock2({
|
|
267
|
+
const lock = stealPool?.hostSubmitLock ?? lock2({
|
|
140
268
|
headers: lockBuffers.headers,
|
|
141
269
|
headerSlotStrideU32: lockBuffers.headerSlotStrideU32,
|
|
142
270
|
LockBoundSector: lockBuffers.lockSector,
|
|
@@ -144,8 +272,9 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostD
|
|
|
144
272
|
payloadSector: lockBuffers.payloadSector,
|
|
145
273
|
payloadConfig: resolvedPayloadConfig,
|
|
146
274
|
textCompat: lockBuffers.textCompat,
|
|
275
|
+
processBoundary: useProcessWorkerRuntime,
|
|
147
276
|
});
|
|
148
|
-
const returnLock = lock2({
|
|
277
|
+
const returnLock = stealPool?.hostReturnLock ?? lock2({
|
|
149
278
|
headers: returnLockBuffers.headers,
|
|
150
279
|
headerSlotStrideU32: returnLockBuffers.headerSlotStrideU32,
|
|
151
280
|
LockBoundSector: returnLockBuffers.lockSector,
|
|
@@ -153,11 +282,11 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostD
|
|
|
153
282
|
payloadSector: returnLockBuffers.payloadSector,
|
|
154
283
|
payloadConfig: resolvedPayloadConfig,
|
|
155
284
|
textCompat: returnLockBuffers.textCompat,
|
|
285
|
+
processBoundary: useProcessWorkerRuntime,
|
|
156
286
|
});
|
|
157
|
-
const abortSignalSAB =
|
|
158
|
-
? controlLayout.abortSignals
|
|
159
|
-
|
|
160
|
-
const abortSignals = abortSignalSAB
|
|
287
|
+
const abortSignalSAB = stealPool?.abortSignalSAB ??
|
|
288
|
+
(usesAbortSignal === true ? controlLayout.abortSignals : undefined);
|
|
289
|
+
const abortSignals = abortSignalSAB && stealPool === undefined
|
|
161
290
|
? signalAbortFactory({
|
|
162
291
|
sab: abortSignalSAB,
|
|
163
292
|
maxSignals: resolvedAbortSignalCapacity,
|
|
@@ -178,16 +307,68 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostD
|
|
|
178
307
|
processRuntime: processWorkerRuntime,
|
|
179
308
|
signal: signalBox.opView,
|
|
180
309
|
});
|
|
181
|
-
const
|
|
310
|
+
const outstandingBorrowedReturns = bufferReferenceReturn === "borrow" && typeof WeakRef === "function"
|
|
311
|
+
? new Map()
|
|
312
|
+
: undefined;
|
|
313
|
+
const releaseBorrowedReturnToken = (token) => {
|
|
314
|
+
outstandingBorrowedReturns?.delete(token);
|
|
315
|
+
worker?.postMessage?.(createBufferReferenceReturnReleaseMessage(token));
|
|
316
|
+
};
|
|
317
|
+
const revokeOutstandingBorrowedReturns = (copyBytes) => {
|
|
318
|
+
if (outstandingBorrowedReturns === undefined)
|
|
319
|
+
return;
|
|
320
|
+
for (const [token, record] of outstandingBorrowedReturns) {
|
|
321
|
+
try {
|
|
322
|
+
const ref = record.ref.deref();
|
|
323
|
+
if (ref !== undefined) {
|
|
324
|
+
ref.revokeBorrow(copyBytes);
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const aliasBuffer = record.aliasBuffer?.deref();
|
|
328
|
+
if (aliasBuffer !== undefined &&
|
|
329
|
+
detachArrayBufferBestEffort(record.runtime, aliasBuffer) &&
|
|
330
|
+
copyBytes) {
|
|
331
|
+
releaseBorrowedReturnToken(token);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
// best effort
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
outstandingBorrowedReturns.clear();
|
|
339
|
+
};
|
|
340
|
+
const returnHooks = bufferReferenceReturn === "borrow"
|
|
341
|
+
? {
|
|
342
|
+
release: releaseBorrowedReturnToken,
|
|
343
|
+
track: (ref, token, aliasBuffer) => {
|
|
344
|
+
if (outstandingBorrowedReturns === undefined)
|
|
345
|
+
return;
|
|
346
|
+
const record = outstandingBorrowedReturns.get(token);
|
|
347
|
+
if (record !== undefined) {
|
|
348
|
+
if (aliasBuffer !== undefined) {
|
|
349
|
+
record.aliasBuffer = new WeakRef(aliasBuffer);
|
|
350
|
+
}
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
outstandingBorrowedReturns.set(token, {
|
|
354
|
+
ref: new WeakRef(ref),
|
|
355
|
+
aliasBuffer: aliasBuffer === undefined
|
|
356
|
+
? undefined
|
|
357
|
+
: new WeakRef(aliasBuffer),
|
|
358
|
+
runtime: ref.runtime,
|
|
359
|
+
});
|
|
360
|
+
},
|
|
361
|
+
}
|
|
362
|
+
: undefined;
|
|
363
|
+
const queue = stealPool?.sharedQueue ?? createHostTxQueue({
|
|
182
364
|
lock,
|
|
183
365
|
returnLock,
|
|
184
366
|
abortSignals,
|
|
185
|
-
releaseBufferReferenceReturn:
|
|
186
|
-
? (token) => {
|
|
187
|
-
worker?.postMessage?.(createBufferReferenceReturnReleaseMessage(token));
|
|
188
|
-
}
|
|
189
|
-
: undefined,
|
|
367
|
+
releaseBufferReferenceReturn: returnHooks,
|
|
190
368
|
});
|
|
369
|
+
if (stealPool !== undefined) {
|
|
370
|
+
stealPool.sharedQueue.setReturnHooks(stealPool.consumerId, returnHooks);
|
|
371
|
+
}
|
|
191
372
|
const { enqueue, rejectAll, txIdle, } = queue;
|
|
192
373
|
const thisSignal = signalBox.opView;
|
|
193
374
|
const a_add = Atomics.add;
|
|
@@ -207,16 +388,21 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostD
|
|
|
207
388
|
let dispatchSend = () => { };
|
|
208
389
|
const send = () => dispatchSend();
|
|
209
390
|
let channelHandler;
|
|
210
|
-
const ownsChannel = sharedChannelHandler === undefined
|
|
391
|
+
const ownsChannel = sharedChannelHandler === undefined &&
|
|
392
|
+
stealPool === undefined;
|
|
211
393
|
const ownChannel = sharedChannelHandler ?? new ChannelHandler();
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
394
|
+
// Under stealing there is one queue, so one dispatcher drives it from the
|
|
395
|
+
// pool; a per-lane dispatcher would double-drain the shared registry.
|
|
396
|
+
const { check: dispatcherCheck } = stealPool !== undefined
|
|
397
|
+
? { check: undefined }
|
|
398
|
+
: hostDispatcherLoop({
|
|
399
|
+
signalBox,
|
|
400
|
+
queue,
|
|
401
|
+
channelHandler: ownChannel,
|
|
402
|
+
dispatcherOptions: host,
|
|
403
|
+
notifySignal: nativeNotifySignal,
|
|
404
|
+
});
|
|
405
|
+
if (ownsChannel && dispatcherCheck !== undefined) {
|
|
220
406
|
ownChannel.open(dispatcherCheck);
|
|
221
407
|
channelHandler = ownChannel;
|
|
222
408
|
dispatchSend = () => {
|
|
@@ -240,9 +426,8 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostD
|
|
|
240
426
|
const workerDataPayload = {
|
|
241
427
|
sab: signals.sab,
|
|
242
428
|
abortSignalSAB,
|
|
243
|
-
abortSignalMax:
|
|
244
|
-
? resolvedAbortSignalCapacity
|
|
245
|
-
: undefined,
|
|
429
|
+
abortSignalMax: stealPool?.abortSignalMax ??
|
|
430
|
+
(usesAbortSignal === true ? resolvedAbortSignalCapacity : undefined),
|
|
246
431
|
list,
|
|
247
432
|
ids,
|
|
248
433
|
names,
|
|
@@ -257,6 +442,11 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostD
|
|
|
257
442
|
payloadConfig: resolvedPayloadConfig,
|
|
258
443
|
bufferReferenceReturn,
|
|
259
444
|
permission,
|
|
445
|
+
steal: stealPool === undefined ? undefined : {
|
|
446
|
+
consumers: stealPool.consumers,
|
|
447
|
+
consumerId: stealPool.consumerId,
|
|
448
|
+
regionLanes: stealPool.regionLanes,
|
|
449
|
+
},
|
|
260
450
|
};
|
|
261
451
|
const baseWorkerOptions = {
|
|
262
452
|
//@ts-ignore Reason
|
|
@@ -325,29 +515,51 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostD
|
|
|
325
515
|
worker.postMessage?.(workerDataPayload);
|
|
326
516
|
}
|
|
327
517
|
let closedReason;
|
|
328
|
-
const
|
|
518
|
+
const deactivateStealConsumer = () => {
|
|
519
|
+
stealPool?.hostSubmitLock.deactivateStealConsumer(stealPool.consumerId);
|
|
520
|
+
};
|
|
521
|
+
const terminateFailedWorker = () => {
|
|
522
|
+
try {
|
|
523
|
+
worker.unref?.();
|
|
524
|
+
void Promise.resolve(worker.terminate())
|
|
525
|
+
.catch(() => { })
|
|
526
|
+
.finally(deactivateStealConsumer);
|
|
527
|
+
}
|
|
528
|
+
catch {
|
|
529
|
+
deactivateStealConsumer();
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
// `workerBytesReadable`: while the worker is still alive its pinned bytes
|
|
533
|
+
// can be copied out; after a crash/exit they are gone and borrows are
|
|
534
|
+
// detached so stale reads fail loud instead of aliasing freed memory.
|
|
535
|
+
const markWorkerClosed = (reason, workerBytesReadable = false) => {
|
|
329
536
|
if (closedReason)
|
|
330
537
|
return;
|
|
331
538
|
closedReason = reason;
|
|
539
|
+
revokeOutstandingBorrowedReturns(workerBytesReadable);
|
|
332
540
|
rejectAll(reason);
|
|
333
541
|
channelHandler?.close();
|
|
334
542
|
};
|
|
335
543
|
const onWorkerMessage = (message) => {
|
|
336
544
|
if (!isWorkerFatalMessage(message))
|
|
337
545
|
return;
|
|
338
|
-
markWorkerClosed(`Worker startup failed: ${message[WORKER_FATAL_MESSAGE_KEY]}
|
|
339
|
-
|
|
546
|
+
markWorkerClosed(`Worker startup failed: ${message[WORKER_FATAL_MESSAGE_KEY]}`, true);
|
|
547
|
+
terminateFailedWorker();
|
|
340
548
|
};
|
|
341
549
|
const onWorkerError = (error) => {
|
|
342
550
|
const message = String(error?.message ?? error);
|
|
343
551
|
markWorkerClosed(`Worker crashed: ${message}`);
|
|
552
|
+
terminateFailedWorker();
|
|
344
553
|
};
|
|
345
554
|
const nodeWorker = worker;
|
|
346
555
|
if (typeof nodeWorker.on === "function") {
|
|
347
556
|
nodeWorker.on("message", onWorkerMessage);
|
|
348
557
|
nodeWorker.on("error", onWorkerError);
|
|
349
558
|
nodeWorker.on("exit", (code) => {
|
|
350
|
-
|
|
559
|
+
// Exit is the point at which this endpoint can no longer write WANT, so
|
|
560
|
+
// survivors may safely ignore any intent it left behind.
|
|
561
|
+
deactivateStealConsumer();
|
|
562
|
+
if (closedReason !== undefined)
|
|
351
563
|
return;
|
|
352
564
|
const normalized = typeof code === "number" ? code : -1;
|
|
353
565
|
markWorkerClosed(`Worker exited with code ${normalized}`);
|
|
@@ -388,15 +600,17 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostD
|
|
|
388
600
|
txIdle,
|
|
389
601
|
call,
|
|
390
602
|
kills: async () => {
|
|
391
|
-
markWorkerClosed("Thread closed");
|
|
603
|
+
markWorkerClosed("Thread closed", true);
|
|
392
604
|
terminateWorkerQuietly(worker);
|
|
393
605
|
cleanupProcessWorkerMemoryQuietly(processWorkerMemory);
|
|
394
606
|
},
|
|
395
607
|
lock,
|
|
396
608
|
processSharedMemoryBackings: processSharedMemory?.backings,
|
|
397
609
|
dispatcherCheck,
|
|
398
|
-
laneWake: sharedChannelHandler !== undefined
|
|
399
|
-
|
|
610
|
+
laneWake: sharedChannelHandler !== undefined || stealPool !== undefined
|
|
611
|
+
? laneWake
|
|
612
|
+
: undefined,
|
|
613
|
+
bindSend: sharedChannelHandler !== undefined || stealPool !== undefined
|
|
400
614
|
? ((fn) => void (dispatchSend = fn))
|
|
401
615
|
: undefined,
|
|
402
616
|
};
|
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
import { type LockControlCarpet } from "../memory/byte-carpet.js";
|
|
2
2
|
import type { DebugOptions, LockBuffers, WorkerData, WorkerSettings } from "../types.js";
|
|
3
|
+
import { type NodeWorkerLike, type SpawnedWorker } from "./worker-common.js";
|
|
3
4
|
import { RUNTIME_PROCESS_WORKER_BOOT_VERSION } from "../common/worker-runtime.js";
|
|
4
5
|
import { type SharedBufferSource } from "../common/shared-buffer-region.js";
|
|
5
6
|
import { FileDescriptor, type ProcessSharedBufferMetadata } from "../connections/index.js";
|
|
6
7
|
import type { SharedMemoryBuffer, SharedMemoryMapping } from "../connections/types.js";
|
|
7
|
-
export type SpawnedWorker = {
|
|
8
|
-
terminate: () => unknown;
|
|
9
|
-
unref?: () => unknown;
|
|
10
|
-
postMessage?: (message: unknown) => void;
|
|
11
|
-
};
|
|
12
|
-
export type NodeWorkerLike = {
|
|
13
|
-
on?: (event: "error" | "exit" | "message", listener: (...args: unknown[]) => void) => void;
|
|
14
|
-
};
|
|
15
8
|
type ProcessWorkerWireLockBuffers = Omit<LockBuffers, "headers" | "lockSector" | "payload" | "payloadSector"> & {
|
|
16
9
|
headers: ProcessSharedBufferMetadata;
|
|
17
10
|
lockSector: ProcessSharedBufferMetadata;
|
|
@@ -36,7 +29,7 @@ export type ProcessWorkerMemoryLayout = {
|
|
|
36
29
|
returnPayload: SharedBufferSource;
|
|
37
30
|
cleanup: () => void;
|
|
38
31
|
};
|
|
39
|
-
export type ProcessWorkerRuntime =
|
|
32
|
+
export type ProcessWorkerRuntime = "bun" | "deno" | "node";
|
|
40
33
|
export type ProcessWorkerCommandPrefix = NonNullable<WorkerSettings["processCommandPrefix"]>;
|
|
41
34
|
export type ResolvedProcessSharedMemorySettings = {
|
|
42
35
|
mode: "inherit" | "named";
|
|
@@ -60,8 +53,6 @@ type ProcessSharedMemoryAllocator = {
|
|
|
60
53
|
createBuffer: (byteLength: number) => SharedArrayBuffer;
|
|
61
54
|
backings: ProcessSharedMemoryBacking[];
|
|
62
55
|
};
|
|
63
|
-
export declare const toWorkerSafeExecArgv: (flags: string[] | undefined) => string[] | undefined;
|
|
64
|
-
export declare const toWorkerCompatExecArgv: (flags: string[] | undefined) => string[] | undefined;
|
|
65
56
|
export declare const createProcessSharedMemoryAllocator: (debug: DebugOptions | undefined) => ProcessSharedMemoryAllocator | undefined;
|
|
66
57
|
export declare const createProcessWorkerMemoryLayout: ({ signalBytes, abortBytes, payloadBytes, thread, sharedMemory, }: {
|
|
67
58
|
signalBytes: number;
|
|
@@ -70,11 +61,11 @@ export declare const createProcessWorkerMemoryLayout: ({ signalBytes, abortBytes
|
|
|
70
61
|
thread: number;
|
|
71
62
|
sharedMemory: ResolvedProcessSharedMemorySettings;
|
|
72
63
|
}) => ProcessWorkerMemoryLayout;
|
|
73
|
-
export declare const serializeWorkerBootstrapData: (options: WorkerSettings) => WorkerSettings;
|
|
74
64
|
export declare const toProcessWorkerBootPayload: (workerData: WorkerData, memory: ProcessWorkerMemoryLayout) => ProcessWorkerBootPayload;
|
|
75
65
|
export declare const readProcessWorkerRuntime: (options: WorkerSettings | undefined) => ProcessWorkerRuntime;
|
|
76
66
|
export declare const readProcessWorkerCommandPrefix: (options: WorkerSettings | undefined) => ProcessWorkerCommandPrefix | undefined;
|
|
77
67
|
export declare const readProcessSharedMemorySettings: (options: WorkerSettings | undefined) => ResolvedProcessSharedMemorySettings;
|
|
68
|
+
export declare const readProcessWorkerNodeMajor: (options: WorkerSettings | undefined) => number | undefined;
|
|
78
69
|
export declare const createProcessWorkerNativeSignalNotifier: ({ processRuntime, signal, }: {
|
|
79
70
|
processRuntime: ProcessWorkerRuntime | undefined;
|
|
80
71
|
signal: Int32Array;
|
|
@@ -87,6 +78,5 @@ export declare const spawnProcessWorker: (options: {
|
|
|
87
78
|
commandPrefix?: ProcessWorkerCommandPrefix;
|
|
88
79
|
permission?: WorkerData["permission"];
|
|
89
80
|
}) => SpawnedWorker & NodeWorkerLike;
|
|
90
|
-
export declare const terminateWorkerQuietly: (worker: SpawnedWorker) => void;
|
|
91
81
|
export declare const cleanupProcessWorkerMemoryQuietly: (memory: ProcessWorkerMemoryLayout | undefined) => void;
|
|
92
82
|
export {};
|