knitting 0.1.51 → 0.1.52
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 +213 -54
- package/knitting.d.ts +1 -0
- package/map.md +15 -3
- package/package.json +14 -2
- package/prebuilds/darwin-arm64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-x64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-x64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/linux-x64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/linux-x64-node-137/knitting_buffer_pointer.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-native-addons.ts +5 -0
- package/src/api.d.ts +3 -10
- package/src/api.js +25 -21
- package/src/common/envelope.d.ts +9 -3
- package/src/common/envelope.js +14 -0
- package/src/common/worker-runtime.d.ts +2 -0
- package/src/common/worker-runtime.js +9 -0
- package/src/connections/buffer-reference-native.d.ts +56 -0
- package/src/connections/buffer-reference-native.js +217 -0
- package/src/connections/buffer-reference.d.ts +76 -0
- package/src/connections/buffer-reference.js +459 -0
- package/src/connections/index.d.ts +1 -0
- package/src/connections/index.js +1 -0
- package/src/connections/node-addons.d.ts +1 -1
- package/src/connections/node-buffer-pointer.d.ts +20 -0
- package/src/connections/node-buffer-pointer.js +16 -0
- package/src/connections/shared-array-buffer-payload.d.ts +36 -0
- package/src/connections/shared-array-buffer-payload.js +235 -0
- package/src/knitting_buffer_pointer.cc +425 -0
- package/src/memory/lock.d.ts +12 -1
- package/src/memory/lock.js +47 -4
- package/src/memory/payloadCodec.js +220 -37
- package/src/runtime/pool.d.ts +2 -1
- package/src/runtime/pool.js +8 -1
- package/src/runtime/process-worker.js +3 -1
- package/src/runtime/tx-queue.d.ts +3 -2
- package/src/runtime/tx-queue.js +18 -13
- package/src/types.d.ts +26 -18
- package/src/utils/http.d.ts +21 -0
- package/src/utils/http.js +93 -0
- package/src/worker/loop.js +23 -3
- package/src/worker/rx-queue.d.ts +4 -1
- package/src/worker/rx-queue.js +53 -4
- package/unsafe.d.ts +1 -0
- package/unsafe.js +1 -0
- package/utils.d.ts +1 -0
- package/utils.js +1 -0
package/src/memory/lock.js
CHANGED
|
@@ -54,6 +54,12 @@ export var PayloadBuffer;
|
|
|
54
54
|
PayloadBuffer[PayloadBuffer["ExternalPayload"] = 44] = "ExternalPayload";
|
|
55
55
|
PayloadBuffer[PayloadBuffer["StaticExternalPayload"] = 45] = "StaticExternalPayload";
|
|
56
56
|
PayloadBuffer[PayloadBuffer["ProcessSharedBuffer"] = 46] = "ProcessSharedBuffer";
|
|
57
|
+
PayloadBuffer[PayloadBuffer["BufferReference"] = 47] = "BufferReference";
|
|
58
|
+
PayloadBuffer[PayloadBuffer["SharedArrayBuffer"] = 48] = "SharedArrayBuffer";
|
|
59
|
+
PayloadBuffer[PayloadBuffer["EnvelopeStaticHeaderExternal"] = 49] = "EnvelopeStaticHeaderExternal";
|
|
60
|
+
PayloadBuffer[PayloadBuffer["EnvelopeDynamicHeaderExternal"] = 50] = "EnvelopeDynamicHeaderExternal";
|
|
61
|
+
PayloadBuffer[PayloadBuffer["EnvelopeStaticHeaderStringExternal"] = 51] = "EnvelopeStaticHeaderStringExternal";
|
|
62
|
+
PayloadBuffer[PayloadBuffer["EnvelopeDynamicHeaderStringExternal"] = 52] = "EnvelopeDynamicHeaderStringExternal";
|
|
57
63
|
})(PayloadBuffer || (PayloadBuffer = {}));
|
|
58
64
|
export var LockBound;
|
|
59
65
|
(function (LockBound) {
|
|
@@ -64,6 +70,7 @@ export var LockBound;
|
|
|
64
70
|
})(LockBound || (LockBound = {}));
|
|
65
71
|
export const LOCK_CACHE_LINE_BYTES = 64;
|
|
66
72
|
export const LOCK_SECTOR_BYTES = 256;
|
|
73
|
+
export const PayloadTransportFinalizer = Symbol.for("knitting.payloadCodec.transportFinalizer");
|
|
67
74
|
export const PromisePayloadMarker = Symbol.for("knitting.promise.payload");
|
|
68
75
|
const TASK_LOCAL_FLAGS_INDEX = 7;
|
|
69
76
|
const TASK_LOCAL_PROMISE_PENDING_FLAG = 1 << 0;
|
|
@@ -72,7 +79,8 @@ export const beginPromisePayload = (task) => {
|
|
|
72
79
|
const flags = task[TASK_LOCAL_FLAGS_INDEX];
|
|
73
80
|
if ((flags & TASK_LOCAL_PROMISE_PENDING_FLAG) !== 0)
|
|
74
81
|
return false;
|
|
75
|
-
task[TASK_LOCAL_FLAGS_INDEX] = (flags | TASK_LOCAL_PROMISE_PENDING_FLAG) >>>
|
|
82
|
+
task[TASK_LOCAL_FLAGS_INDEX] = (flags | TASK_LOCAL_PROMISE_PENDING_FLAG) >>>
|
|
83
|
+
0;
|
|
76
84
|
return true;
|
|
77
85
|
};
|
|
78
86
|
export const finishPromisePayload = (task) => {
|
|
@@ -83,6 +91,36 @@ export const isPromisePayloadPending = (task) => (task[TASK_LOCAL_FLAGS_INDEX] &
|
|
|
83
91
|
export const resetTaskLocalFlags = (task) => {
|
|
84
92
|
task[TASK_LOCAL_FLAGS_INDEX] = 0;
|
|
85
93
|
};
|
|
94
|
+
export const addTaskFinalizer = (task, finalizer) => {
|
|
95
|
+
const previous = task.finalize;
|
|
96
|
+
task.finalize = previous === undefined ? finalizer : () => {
|
|
97
|
+
try {
|
|
98
|
+
previous();
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
finalizer();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
export const attachPayloadTransportFinalizer = (task, value) => {
|
|
106
|
+
if (task.finalize !== undefined || value === null || typeof value !== "object") {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const finalizer = value[PayloadTransportFinalizer]?.();
|
|
110
|
+
if (typeof finalizer === "function")
|
|
111
|
+
addTaskFinalizer(task, finalizer);
|
|
112
|
+
};
|
|
113
|
+
export const runTaskFinalizers = (task) => {
|
|
114
|
+
const finalizer = task.finalize;
|
|
115
|
+
task.finalize = undefined;
|
|
116
|
+
if (finalizer !== undefined) {
|
|
117
|
+
try {
|
|
118
|
+
finalizer();
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
};
|
|
86
124
|
export var TaskIndex;
|
|
87
125
|
(function (TaskIndex) {
|
|
88
126
|
/**
|
|
@@ -186,6 +224,7 @@ const def = (_) => { };
|
|
|
186
224
|
const createTaskShell = () => {
|
|
187
225
|
const task = new Uint32Array(TaskIndex.Size);
|
|
188
226
|
task.value = null;
|
|
227
|
+
task.finalize = undefined;
|
|
189
228
|
task.resolve = def;
|
|
190
229
|
task.reject = def;
|
|
191
230
|
task[TASK_LOCAL_FLAGS_INDEX] = 0;
|
|
@@ -266,10 +305,12 @@ export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload,
|
|
|
266
305
|
lockSector: payloadLockRegion,
|
|
267
306
|
textCompat: resolvedTextCompat,
|
|
268
307
|
onPromise: (task, isRejected, value) => {
|
|
269
|
-
if ((task[TASK_LOCAL_FLAGS_INDEX] & TASK_LOCAL_PROMISE_TRACKED_FLAG) !==
|
|
308
|
+
if ((task[TASK_LOCAL_FLAGS_INDEX] & TASK_LOCAL_PROMISE_TRACKED_FLAG) !==
|
|
309
|
+
0 &&
|
|
270
310
|
pendingPromiseCount > 0) {
|
|
271
311
|
task[TASK_LOCAL_FLAGS_INDEX] =
|
|
272
|
-
(task[TASK_LOCAL_FLAGS_INDEX] & ~TASK_LOCAL_PROMISE_TRACKED_FLAG) >>>
|
|
312
|
+
(task[TASK_LOCAL_FLAGS_INDEX] & ~TASK_LOCAL_PROMISE_TRACKED_FLAG) >>>
|
|
313
|
+
0;
|
|
273
314
|
pendingPromiseCount = (pendingPromiseCount - 1) | 0;
|
|
274
315
|
}
|
|
275
316
|
promiseHandler(task, isRejected, value);
|
|
@@ -322,7 +363,8 @@ export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload,
|
|
|
322
363
|
const flags = task[TASK_LOCAL_FLAGS_INDEX];
|
|
323
364
|
if ((flags & TASK_LOCAL_PROMISE_TRACKED_FLAG) !== 0)
|
|
324
365
|
return;
|
|
325
|
-
task[TASK_LOCAL_FLAGS_INDEX] = (flags | TASK_LOCAL_PROMISE_TRACKED_FLAG) >>>
|
|
366
|
+
task[TASK_LOCAL_FLAGS_INDEX] = (flags | TASK_LOCAL_PROMISE_TRACKED_FLAG) >>>
|
|
367
|
+
0;
|
|
326
368
|
pendingPromiseCount = (pendingPromiseCount + 1) | 0;
|
|
327
369
|
};
|
|
328
370
|
const encodeTaskValue = (task, slotIndex) => encodeTask(task, slotIndex);
|
|
@@ -633,6 +675,7 @@ export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload,
|
|
|
633
675
|
if (recycled) {
|
|
634
676
|
fillTaskFrom(recycled, headersBuffer, off);
|
|
635
677
|
recycled.value = null;
|
|
678
|
+
recycled.finalize = undefined;
|
|
636
679
|
recycled.resolve = def;
|
|
637
680
|
recycled.reject = def;
|
|
638
681
|
task = recycled;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { beginPromisePayload, finishPromisePayload, getTaskSlotIndex, HEADER_BYTE_LENGTH, HEADER_SLOT_STRIDE_U32, HEADER_STATIC_PAYLOAD_U32, LockBound, PayloadBuffer, PayloadSignal, TaskIndex, } from "./lock.js";
|
|
1
|
+
import { attachPayloadTransportFinalizer, beginPromisePayload, finishPromisePayload, getTaskSlotIndex, HEADER_BYTE_LENGTH, HEADER_SLOT_STRIDE_U32, HEADER_STATIC_PAYLOAD_U32, LockBound, PayloadBuffer, PayloadSignal, TaskIndex, } from "./lock.js";
|
|
2
2
|
import { register } from "./regionRegistry.js";
|
|
3
3
|
import { createSharedDynamicBufferIO, createSharedStaticBufferIO, } from "./shared-buffer-io.js";
|
|
4
4
|
import { getStridedRegionSpanBytes } from "./byte-carpet.js";
|
|
5
5
|
import { encoderError, ErrorKnitting } from "../error.js";
|
|
6
6
|
import { Envelope } from "../common/envelope.js";
|
|
7
7
|
import { resolvePayloadBufferOptions, } from "./payload-config.js";
|
|
8
|
+
import { getSharedArrayBufferPayload, SHARED_ARRAY_BUFFER_CODEC_ID, SHARED_ARRAY_BUFFER_NUMERIC_TRANSFER, SHARED_ARRAY_BUFFER_NUMERIC_WORDS, } from "../connections/shared-array-buffer-payload.js";
|
|
9
|
+
const BUFFER_REFERENCE_NUMERIC_TRANSFER = Symbol.for("knitting.bufferReference.numericTransfer");
|
|
8
10
|
const memory = new ArrayBuffer(8);
|
|
9
11
|
const Float64View = new Float64Array(memory);
|
|
10
12
|
const BigInt64View = new BigInt64Array(memory);
|
|
@@ -28,6 +30,7 @@ const BIGINT64_MAX = (1n << 63n) - 1n;
|
|
|
28
30
|
const { parse: parseJSON, stringify: stringifyJSON } = JSON;
|
|
29
31
|
const { for: symbolFor, keyFor: symbolKeyFor } = Symbol;
|
|
30
32
|
const EXTERNAL_PAYLOAD_BRAND = symbolFor("knitting.payloadCodec");
|
|
33
|
+
const BUFFER_REFERENCE_CODEC_ID = "knitting.bufferReference";
|
|
31
34
|
const PROCESS_SHARED_BUFFER_CODEC_ID = "knitting.processSharedBuffer";
|
|
32
35
|
const externalPayloadGlobal = globalThis;
|
|
33
36
|
const objectGetPrototypeOf = Object.getPrototypeOf;
|
|
@@ -35,7 +38,8 @@ const objectHasOwn = Object.prototype.hasOwnProperty;
|
|
|
35
38
|
const arrayIsArray = Array.isArray;
|
|
36
39
|
const objectPrototype = Object.prototype;
|
|
37
40
|
const UNSUPPORTED_OBJECT_DETAIL = "Unsupported object type. Allowed: plain object, array, Error, Date, Envelope, Buffer, ArrayBuffer, DataView, typed arrays, and registered external payloads. Serialize it yourself.";
|
|
38
|
-
const ENVELOPE_PAYLOAD_DETAIL = "Envelope payload must be an ArrayBuffer
|
|
41
|
+
const ENVELOPE_PAYLOAD_DETAIL = "Envelope payload must be an ArrayBuffer, SharedArrayBuffer, " +
|
|
42
|
+
"ProcessSharedBuffer, or BufferReference.";
|
|
39
43
|
const ENVELOPE_HEADER_DETAIL = "Envelope header must be a JSON-like value or string.";
|
|
40
44
|
const ENVELOPE_PROMISE_DETAIL = "Envelope header cannot contain Promise values.";
|
|
41
45
|
const DYNAMIC_PAYLOAD_LIMIT_DETAIL = "Dynamic payload exceeds maxPayloadBytes.";
|
|
@@ -92,15 +96,18 @@ const decodeExternalPayload = (raw) => {
|
|
|
92
96
|
: { codec: codecId, metadata };
|
|
93
97
|
};
|
|
94
98
|
const PROCESS_SHARED_BUFFER_NUMERIC_WORDS = 8;
|
|
99
|
+
const BUFFER_REFERENCE_NUMERIC_WORDS = 8;
|
|
95
100
|
const NUMERIC_SENTINEL = 0xffffffff;
|
|
96
|
-
const
|
|
97
|
-
const codec = externalPayloadGlobal.__KNITTING_PAYLOAD_CODECS__?.[
|
|
98
|
-
if (typeof codec?.decodeNumeric === "function")
|
|
101
|
+
const decodeNumericExternalPayload = (codecId, words) => {
|
|
102
|
+
const codec = externalPayloadGlobal.__KNITTING_PAYLOAD_CODECS__?.[codecId];
|
|
103
|
+
if (typeof codec?.decodeNumeric === "function") {
|
|
99
104
|
return codec.decodeNumeric(words);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return { codec: PROCESS_SHARED_BUFFER_CODEC_ID, metadata: Array.from(words) };
|
|
105
|
+
}
|
|
106
|
+
return { codec: codecId, metadata: Array.from(words) };
|
|
103
107
|
};
|
|
108
|
+
const decodeProcessSharedBufferNumericWords = (words) => decodeNumericExternalPayload(PROCESS_SHARED_BUFFER_CODEC_ID, words);
|
|
109
|
+
const decodeBufferReferenceNumericWords = (words) => decodeNumericExternalPayload(BUFFER_REFERENCE_CODEC_ID, words);
|
|
110
|
+
const decodeSharedArrayBufferNumericWords = (words) => decodeNumericExternalPayload(SHARED_ARRAY_BUFFER_CODEC_ID, words);
|
|
104
111
|
const tryEncodePrimitiveTask = (task) => {
|
|
105
112
|
const value = task.value;
|
|
106
113
|
switch (typeof value) {
|
|
@@ -516,6 +523,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
516
523
|
return true;
|
|
517
524
|
};
|
|
518
525
|
const processSharedBufferWords = new Uint32Array(PROCESS_SHARED_BUFFER_NUMERIC_WORDS);
|
|
526
|
+
const sharedArrayBufferWords = new Uint32Array(SHARED_ARRAY_BUFFER_NUMERIC_WORDS);
|
|
519
527
|
const tryEncodeProcessSharedBufferNumeric = (task, slotIndex, value) => {
|
|
520
528
|
const descriptor = value.descriptor;
|
|
521
529
|
if (descriptor === undefined ||
|
|
@@ -549,6 +557,33 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
549
557
|
task.value = null;
|
|
550
558
|
return true;
|
|
551
559
|
};
|
|
560
|
+
const tryEncodeBufferReferenceNumeric = (task, slotIndex, value) => {
|
|
561
|
+
const words = value[BUFFER_REFERENCE_NUMERIC_TRANSFER]?.();
|
|
562
|
+
if (words === undefined)
|
|
563
|
+
return false;
|
|
564
|
+
task[TaskIndex.Type] = PayloadBuffer.BufferReference;
|
|
565
|
+
task[TaskIndex.PayloadLen] = writeStaticU32Words(words, BUFFER_REFERENCE_NUMERIC_WORDS, slotIndex);
|
|
566
|
+
attachPayloadTransportFinalizer(task, value);
|
|
567
|
+
task.value = null;
|
|
568
|
+
return true;
|
|
569
|
+
};
|
|
570
|
+
const tryEncodeSharedArrayBufferNumeric = (task, slotIndex, value) => {
|
|
571
|
+
const words = value[SHARED_ARRAY_BUFFER_NUMERIC_TRANSFER]?.(lockSector);
|
|
572
|
+
if (words === undefined)
|
|
573
|
+
return false;
|
|
574
|
+
sharedArrayBufferWords[0] = words[0] ?? 0;
|
|
575
|
+
sharedArrayBufferWords[1] = words[1] ?? 0;
|
|
576
|
+
sharedArrayBufferWords[2] = words[2] ?? 0;
|
|
577
|
+
sharedArrayBufferWords[3] = words[3] ?? 0;
|
|
578
|
+
sharedArrayBufferWords[4] = words[4] ?? 0;
|
|
579
|
+
sharedArrayBufferWords[5] = words[5] ?? 0;
|
|
580
|
+
sharedArrayBufferWords[6] = words[6] ?? 0;
|
|
581
|
+
sharedArrayBufferWords[7] = words[7] ?? 0;
|
|
582
|
+
task[TaskIndex.Type] = PayloadBuffer.SharedArrayBuffer;
|
|
583
|
+
task[TaskIndex.PayloadLen] = writeStaticU32Words(sharedArrayBufferWords, words.length, slotIndex);
|
|
584
|
+
task.value = null;
|
|
585
|
+
return true;
|
|
586
|
+
};
|
|
552
587
|
const encodeObjectExternalPayload = (task, slotIndex, externalPayload) => {
|
|
553
588
|
const codecId = readExternalPayloadCodecId(externalPayload);
|
|
554
589
|
if (codecId === undefined) {
|
|
@@ -559,6 +594,14 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
559
594
|
detail: UNSUPPORTED_OBJECT_DETAIL,
|
|
560
595
|
});
|
|
561
596
|
}
|
|
597
|
+
if (codecId === SHARED_ARRAY_BUFFER_CODEC_ID &&
|
|
598
|
+
tryEncodeSharedArrayBufferNumeric(task, slotIndex, externalPayload)) {
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
601
|
+
if (codecId === BUFFER_REFERENCE_CODEC_ID &&
|
|
602
|
+
tryEncodeBufferReferenceNumeric(task, slotIndex, externalPayload)) {
|
|
603
|
+
return true;
|
|
604
|
+
}
|
|
562
605
|
if (codecId === PROCESS_SHARED_BUFFER_CODEC_ID &&
|
|
563
606
|
tryEncodeProcessSharedBufferNumeric(task, slotIndex, externalPayload)) {
|
|
564
607
|
return true;
|
|
@@ -589,6 +632,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
589
632
|
if (written !== -1) {
|
|
590
633
|
task[TaskIndex.Type] = PayloadBuffer.StaticExternalPayload;
|
|
591
634
|
task[TaskIndex.PayloadLen] = written;
|
|
635
|
+
attachPayloadTransportFinalizer(task, externalPayload);
|
|
592
636
|
task.value = null;
|
|
593
637
|
return true;
|
|
594
638
|
}
|
|
@@ -603,6 +647,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
603
647
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
604
648
|
task[TaskIndex.PayloadLen] = written;
|
|
605
649
|
setSlotLength(reservedSlot, written);
|
|
650
|
+
attachPayloadTransportFinalizer(task, externalPayload);
|
|
606
651
|
task.value = null;
|
|
607
652
|
return true;
|
|
608
653
|
};
|
|
@@ -614,52 +659,50 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
614
659
|
task.value = null;
|
|
615
660
|
return true;
|
|
616
661
|
};
|
|
617
|
-
const
|
|
618
|
-
const header = envelope.header;
|
|
619
|
-
const payload = envelope.payload;
|
|
620
|
-
const headerIsString = typeof header === "string";
|
|
621
|
-
if (!(payload instanceof ArrayBuffer)) {
|
|
622
|
-
return encoderError({
|
|
623
|
-
task,
|
|
624
|
-
type: ErrorKnitting.Serializable,
|
|
625
|
-
onPromise,
|
|
626
|
-
detail: ENVELOPE_PAYLOAD_DETAIL,
|
|
627
|
-
});
|
|
628
|
-
}
|
|
662
|
+
const encodeEnvelopeHeaderText = (task, header, headerIsString) => {
|
|
629
663
|
if (hasPromiseInEnvelopeHeader(header)) {
|
|
630
|
-
|
|
664
|
+
encoderError({
|
|
631
665
|
task,
|
|
632
666
|
type: ErrorKnitting.Serializable,
|
|
633
667
|
onPromise,
|
|
634
668
|
detail: ENVELOPE_PROMISE_DETAIL,
|
|
635
669
|
});
|
|
670
|
+
return undefined;
|
|
636
671
|
}
|
|
672
|
+
if (headerIsString)
|
|
673
|
+
return header;
|
|
637
674
|
let headerText;
|
|
638
|
-
|
|
639
|
-
headerText = header;
|
|
675
|
+
try {
|
|
676
|
+
headerText = stringifyJSON(header);
|
|
640
677
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
catch (error) {
|
|
646
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
647
|
-
return encoderError({
|
|
648
|
-
task,
|
|
649
|
-
type: ErrorKnitting.Json,
|
|
650
|
-
onPromise,
|
|
651
|
-
detail,
|
|
652
|
-
});
|
|
653
|
-
}
|
|
678
|
+
catch (error) {
|
|
679
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
680
|
+
encoderError({ task, type: ErrorKnitting.Json, onPromise, detail });
|
|
681
|
+
return undefined;
|
|
654
682
|
}
|
|
655
683
|
if (typeof headerText !== "string") {
|
|
656
|
-
|
|
684
|
+
encoderError({
|
|
657
685
|
task,
|
|
658
686
|
type: ErrorKnitting.Serializable,
|
|
659
687
|
onPromise,
|
|
660
688
|
detail: ENVELOPE_HEADER_DETAIL,
|
|
661
689
|
});
|
|
690
|
+
return undefined;
|
|
691
|
+
}
|
|
692
|
+
return headerText;
|
|
693
|
+
};
|
|
694
|
+
const resolveEnvelopeExternalBody = (body) => {
|
|
695
|
+
if (body === null || typeof body !== "object")
|
|
696
|
+
return undefined;
|
|
697
|
+
const sharedArrayBuffer = getSharedArrayBufferPayload(body);
|
|
698
|
+
if (sharedArrayBuffer !== undefined)
|
|
699
|
+
return sharedArrayBuffer;
|
|
700
|
+
if (isExternalPayloadLike(body)) {
|
|
701
|
+
return body;
|
|
662
702
|
}
|
|
703
|
+
return undefined;
|
|
704
|
+
};
|
|
705
|
+
const encodeEnvelopeArrayBufferBody = (task, slotIndex, headerText, headerIsString, payload) => {
|
|
663
706
|
const payloadBytes = new Uint8Array(payload);
|
|
664
707
|
const payloadLength = payloadBytes.byteLength;
|
|
665
708
|
const payloadReserveBytes = payloadLength > 0 ? payloadLength : 1;
|
|
@@ -707,6 +750,107 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
707
750
|
task.value = null;
|
|
708
751
|
return true;
|
|
709
752
|
};
|
|
753
|
+
const encodeEnvelopeExternalBody = (task, slotIndex, headerText, headerIsString, externalBody) => {
|
|
754
|
+
const codecId = readExternalPayloadCodecId(externalBody);
|
|
755
|
+
if (codecId === undefined) {
|
|
756
|
+
return encoderError({
|
|
757
|
+
task,
|
|
758
|
+
type: ErrorKnitting.Serializable,
|
|
759
|
+
onPromise,
|
|
760
|
+
detail: ENVELOPE_PAYLOAD_DETAIL,
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
let bodyText;
|
|
764
|
+
try {
|
|
765
|
+
bodyText = stringifyJSON([codecId, externalBody.toMetadata()]);
|
|
766
|
+
}
|
|
767
|
+
catch (error) {
|
|
768
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
769
|
+
return encoderError({
|
|
770
|
+
task,
|
|
771
|
+
type: ErrorKnitting.Serializable,
|
|
772
|
+
onPromise,
|
|
773
|
+
detail,
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
if (typeof bodyText !== "string") {
|
|
777
|
+
return encoderError({
|
|
778
|
+
task,
|
|
779
|
+
type: ErrorKnitting.Serializable,
|
|
780
|
+
onPromise,
|
|
781
|
+
detail: "Envelope body metadata must be JSON serializable.",
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
const bodyBytes = textEncode.encode(bodyText);
|
|
785
|
+
const bodyLength = bodyBytes.byteLength;
|
|
786
|
+
const staticHeaderWritten = writeStaticUtf8(headerText, slotIndex);
|
|
787
|
+
if (staticHeaderWritten !== -1) {
|
|
788
|
+
if (!ensureWithinDynamicLimit(task, bodyLength, "EnvelopeStaticHeaderExternal"))
|
|
789
|
+
return false;
|
|
790
|
+
const reservedSlot = reserveDynamicObject(task, bodyLength);
|
|
791
|
+
task[TaskIndex.Type] = headerIsString
|
|
792
|
+
? PayloadBuffer.EnvelopeStaticHeaderStringExternal
|
|
793
|
+
: PayloadBuffer.EnvelopeStaticHeaderExternal;
|
|
794
|
+
task[TaskIndex.PayloadLen] = staticHeaderWritten;
|
|
795
|
+
task[TaskIndex.End] = bodyLength;
|
|
796
|
+
const bodyWritten = writeDynamicBinary(bodyBytes, task[TaskIndex.Start]);
|
|
797
|
+
if (bodyWritten < 0) {
|
|
798
|
+
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
799
|
+
}
|
|
800
|
+
setSlotLength(reservedSlot, bodyWritten);
|
|
801
|
+
attachPayloadTransportFinalizer(task, externalBody);
|
|
802
|
+
task.value = null;
|
|
803
|
+
return true;
|
|
804
|
+
}
|
|
805
|
+
const headerReserveBytes = dynamicUtf8ReserveBytesWithExtra(task, headerText, bodyLength, headerIsString
|
|
806
|
+
? "EnvelopeDynamicHeaderStringExternal"
|
|
807
|
+
: "EnvelopeDynamicHeaderExternal");
|
|
808
|
+
if (headerReserveBytes < 0)
|
|
809
|
+
return false;
|
|
810
|
+
task[TaskIndex.Type] = headerIsString
|
|
811
|
+
? PayloadBuffer.EnvelopeDynamicHeaderStringExternal
|
|
812
|
+
: PayloadBuffer.EnvelopeDynamicHeaderExternal;
|
|
813
|
+
const reservedSlot = reserveDynamicObject(task, headerReserveBytes + bodyLength);
|
|
814
|
+
const baseStart = task[TaskIndex.Start];
|
|
815
|
+
const writtenHeaderBytes = writeDynamicUtf8(headerText, baseStart, headerReserveBytes);
|
|
816
|
+
if (writtenHeaderBytes < 0) {
|
|
817
|
+
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
818
|
+
}
|
|
819
|
+
const bodyWritten = writeDynamicBinary(bodyBytes, baseStart + writtenHeaderBytes);
|
|
820
|
+
if (bodyWritten < 0) {
|
|
821
|
+
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
822
|
+
}
|
|
823
|
+
task[TaskIndex.PayloadLen] = writtenHeaderBytes;
|
|
824
|
+
task[TaskIndex.End] = bodyLength;
|
|
825
|
+
setSlotLength(reservedSlot, writtenHeaderBytes + bodyLength);
|
|
826
|
+
attachPayloadTransportFinalizer(task, externalBody);
|
|
827
|
+
task.value = null;
|
|
828
|
+
return true;
|
|
829
|
+
};
|
|
830
|
+
const encodeObjectEnvelope = (task, slotIndex, envelope) => {
|
|
831
|
+
const header = envelope.header;
|
|
832
|
+
const payload = envelope.payload;
|
|
833
|
+
const headerIsString = typeof header === "string";
|
|
834
|
+
if (payload instanceof ArrayBuffer) {
|
|
835
|
+
const headerText = encodeEnvelopeHeaderText(task, header, headerIsString);
|
|
836
|
+
if (headerText === undefined)
|
|
837
|
+
return false;
|
|
838
|
+
return encodeEnvelopeArrayBufferBody(task, slotIndex, headerText, headerIsString, payload);
|
|
839
|
+
}
|
|
840
|
+
const externalBody = resolveEnvelopeExternalBody(payload);
|
|
841
|
+
if (externalBody !== undefined) {
|
|
842
|
+
const headerText = encodeEnvelopeHeaderText(task, header, headerIsString);
|
|
843
|
+
if (headerText === undefined)
|
|
844
|
+
return false;
|
|
845
|
+
return encodeEnvelopeExternalBody(task, slotIndex, headerText, headerIsString, externalBody);
|
|
846
|
+
}
|
|
847
|
+
return encoderError({
|
|
848
|
+
task,
|
|
849
|
+
type: ErrorKnitting.Serializable,
|
|
850
|
+
onPromise,
|
|
851
|
+
detail: ENVELOPE_PAYLOAD_DETAIL,
|
|
852
|
+
});
|
|
853
|
+
};
|
|
710
854
|
const encodeObjectPromise = (task, promise) => {
|
|
711
855
|
if (beginPromisePayload(task)) {
|
|
712
856
|
promise.then((value) => {
|
|
@@ -769,6 +913,10 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
769
913
|
objectDynamicSlot = -1;
|
|
770
914
|
try {
|
|
771
915
|
const objectValue = args;
|
|
916
|
+
const sharedArrayBufferPayload = getSharedArrayBufferPayload(objectValue);
|
|
917
|
+
if (sharedArrayBufferPayload !== undefined) {
|
|
918
|
+
return encodeObjectExternalPayload(task, slotIndex, sharedArrayBufferPayload);
|
|
919
|
+
}
|
|
772
920
|
const objectProto = objectGetPrototypeOf(objectValue);
|
|
773
921
|
if (isRuntimeUint8Array(objectValue)) {
|
|
774
922
|
return encodeObjectUint8Array(task, slotIndex, objectValue);
|
|
@@ -963,6 +1111,8 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
963
1111
|
// decode is single-consumer and not re-entrant, and the words are consumed
|
|
964
1112
|
// synchronously when building the ProcessSharedBuffer.
|
|
965
1113
|
const processSharedBufferWords = new Uint32Array(PROCESS_SHARED_BUFFER_NUMERIC_WORDS);
|
|
1114
|
+
const sharedArrayBufferWords = new Uint32Array(SHARED_ARRAY_BUFFER_NUMERIC_WORDS);
|
|
1115
|
+
const bufferReferenceWords = new Uint32Array(BUFFER_REFERENCE_NUMERIC_WORDS);
|
|
966
1116
|
// TODO: remove slotIndex and make that all their callers
|
|
967
1117
|
// store the slot in their Task, to just get it when it comes
|
|
968
1118
|
// to the static versions of decoding
|
|
@@ -1037,6 +1187,33 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1037
1187
|
freeTaskSlot(task);
|
|
1038
1188
|
return;
|
|
1039
1189
|
}
|
|
1190
|
+
case PayloadBuffer.EnvelopeStaticHeaderExternal:
|
|
1191
|
+
case PayloadBuffer.EnvelopeStaticHeaderStringExternal: {
|
|
1192
|
+
const rawHeader = readStaticUtf8(0, task[TaskIndex.PayloadLen], slotIndex);
|
|
1193
|
+
const header = task[TaskIndex.Type] ===
|
|
1194
|
+
PayloadBuffer.EnvelopeStaticHeaderStringExternal
|
|
1195
|
+
? rawHeader
|
|
1196
|
+
: parseJSON(rawHeader);
|
|
1197
|
+
const bodyStart = task[TaskIndex.Start];
|
|
1198
|
+
const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]));
|
|
1199
|
+
task.value = new Envelope(header, body);
|
|
1200
|
+
freeTaskSlot(task);
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
case PayloadBuffer.EnvelopeDynamicHeaderExternal:
|
|
1204
|
+
case PayloadBuffer.EnvelopeDynamicHeaderStringExternal: {
|
|
1205
|
+
const headerStart = task[TaskIndex.Start];
|
|
1206
|
+
const bodyStart = headerStart + task[TaskIndex.PayloadLen];
|
|
1207
|
+
const rawHeader = readDynamicUtf8(headerStart, bodyStart);
|
|
1208
|
+
const header = task[TaskIndex.Type] ===
|
|
1209
|
+
PayloadBuffer.EnvelopeDynamicHeaderStringExternal
|
|
1210
|
+
? rawHeader
|
|
1211
|
+
: parseJSON(rawHeader);
|
|
1212
|
+
const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]));
|
|
1213
|
+
task.value = new Envelope(header, body);
|
|
1214
|
+
freeTaskSlot(task);
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1040
1217
|
case PayloadBuffer.BigInt:
|
|
1041
1218
|
task.value = decodeBigIntBinary(readDynamicBufferCopy(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]));
|
|
1042
1219
|
freeTaskSlot(task);
|
|
@@ -1113,6 +1290,12 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1113
1290
|
case PayloadBuffer.ProcessSharedBuffer:
|
|
1114
1291
|
task.value = decodeProcessSharedBufferNumericWords(readStaticU32Words(processSharedBufferWords, PROCESS_SHARED_BUFFER_NUMERIC_WORDS, slotIndex));
|
|
1115
1292
|
return;
|
|
1293
|
+
case PayloadBuffer.SharedArrayBuffer:
|
|
1294
|
+
task.value = decodeSharedArrayBufferNumericWords(readStaticU32Words(sharedArrayBufferWords, task[TaskIndex.PayloadLen] >>> 2, slotIndex));
|
|
1295
|
+
return;
|
|
1296
|
+
case PayloadBuffer.BufferReference:
|
|
1297
|
+
task.value = decodeBufferReferenceNumericWords(readStaticU32Words(bufferReferenceWords, BUFFER_REFERENCE_NUMERIC_WORDS, slotIndex));
|
|
1298
|
+
return;
|
|
1116
1299
|
case PayloadBuffer.Date:
|
|
1117
1300
|
Uint32View[0] = task[TaskIndex.Start];
|
|
1118
1301
|
Uint32View[1] = task[TaskIndex.End];
|
package/src/runtime/pool.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { lock2 } from "../memory/lock.js";
|
|
|
4
4
|
import type { DebugOptions, DispatcherSettings, WorkerContext, WorkerData, WorkerSettings } from "../types.js";
|
|
5
5
|
import "../worker/loop.js";
|
|
6
6
|
import { type PayloadBufferOptions } from "../memory/payload-config.js";
|
|
7
|
-
export declare const spawnWorkerContext: ({ list, ids, names, sab, thread, debug, totalNumberOfThread, source, at, workerOptions, workerExecArgv, permission, host, payload, payloadInitialBytes, payloadMaxBytes, bufferMode, maxPayloadBytes, abortSignalCapacity, usesAbortSignal, }: {
|
|
7
|
+
export declare const spawnWorkerContext: ({ list, ids, names, sab, thread, debug, totalNumberOfThread, source, at, workerOptions, workerExecArgv, permission, host, payload, bufferReferenceReturn, payloadInitialBytes, payloadMaxBytes, bufferMode, maxPayloadBytes, abortSignalCapacity, usesAbortSignal, }: {
|
|
8
8
|
list: string[];
|
|
9
9
|
ids: number[];
|
|
10
10
|
names: string[];
|
|
@@ -19,6 +19,7 @@ export declare const spawnWorkerContext: ({ list, ids, names, sab, thread, debug
|
|
|
19
19
|
permission?: WorkerData["permission"];
|
|
20
20
|
host?: DispatcherSettings;
|
|
21
21
|
payload?: PayloadBufferOptions;
|
|
22
|
+
bufferReferenceReturn?: "copy" | "borrow";
|
|
22
23
|
payloadInitialBytes?: number;
|
|
23
24
|
payloadMaxBytes?: number;
|
|
24
25
|
bufferMode?: PayloadBufferOptions["mode"];
|
package/src/runtime/pool.js
CHANGED
|
@@ -10,6 +10,7 @@ import { probeLockBufferTextCompat } from "../common/shared-buffer-text.js";
|
|
|
10
10
|
import { signalAbortFactory } from "../shared/abortSignal.js";
|
|
11
11
|
import { createLockControlCarpet } from "../memory/byte-carpet.js";
|
|
12
12
|
import { resolvePayloadBufferOptions, } from "../memory/payload-config.js";
|
|
13
|
+
import { createBufferReferenceReturnReleaseMessage } from "../connections/buffer-reference.js";
|
|
13
14
|
const WORKER_FATAL_MESSAGE_KEY = "__knittingWorkerFatal";
|
|
14
15
|
const isWorkerFatalMessage = (value) => !!value &&
|
|
15
16
|
typeof value === "object" &&
|
|
@@ -33,7 +34,7 @@ const withFixedPayloadConfig = (config) => ({
|
|
|
33
34
|
mode: "fixed",
|
|
34
35
|
payloadInitialBytes: config.payloadMaxByteLength,
|
|
35
36
|
});
|
|
36
|
-
export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, totalNumberOfThread, source, at, workerOptions, workerExecArgv, permission, host, payload, payloadInitialBytes, payloadMaxBytes, bufferMode, maxPayloadBytes, abortSignalCapacity, usesAbortSignal, }) => {
|
|
37
|
+
export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, totalNumberOfThread, source, at, workerOptions, workerExecArgv, permission, host, payload, bufferReferenceReturn, payloadInitialBytes, payloadMaxBytes, bufferMode, maxPayloadBytes, abortSignalCapacity, usesAbortSignal, }) => {
|
|
37
38
|
const tsFileUrl = new URL(import.meta.url);
|
|
38
39
|
const poliWorker = RUNTIME_WORKER;
|
|
39
40
|
const resolvedWorkerOptions = serializeWorkerBootstrapData(withDefaultWorkerTimers(workerOptions));
|
|
@@ -193,6 +194,11 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, total
|
|
|
193
194
|
lock,
|
|
194
195
|
returnLock,
|
|
195
196
|
abortSignals,
|
|
197
|
+
releaseBufferReferenceReturn: bufferReferenceReturn === "borrow"
|
|
198
|
+
? (token) => {
|
|
199
|
+
worker?.postMessage?.(createBufferReferenceReturnReleaseMessage(token));
|
|
200
|
+
}
|
|
201
|
+
: undefined,
|
|
196
202
|
});
|
|
197
203
|
const { enqueue, rejectAll, txIdle, } = queue;
|
|
198
204
|
const channelHandler = new ChannelHandler();
|
|
@@ -227,6 +233,7 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, total
|
|
|
227
233
|
lock: lockBuffers,
|
|
228
234
|
returnLock: returnLockBuffers,
|
|
229
235
|
payloadConfig: resolvedPayloadConfig,
|
|
236
|
+
bufferReferenceReturn,
|
|
230
237
|
permission,
|
|
231
238
|
};
|
|
232
239
|
const baseWorkerOptions = {
|
|
@@ -2,7 +2,7 @@ import { fileURLToPath as fileURLToPathCompat } from "node:url";
|
|
|
2
2
|
import { HEADER_SLOT_STRIDE_U32, LOCK_SECTOR_BYTE_LENGTH, LockBound, } from "../memory/lock.js";
|
|
3
3
|
import { createByteCarpet, getHeaderBlockByteLength, makeSharedBufferRegion, } from "../memory/byte-carpet.js";
|
|
4
4
|
import { RUNTIME } from "../common/runtime.js";
|
|
5
|
-
import { RUNTIME_PROCESS_WORKER_BOOT_ENV, RUNTIME_PROCESS_WORKER_BOOT_VERSION, RUNTIME_PROCESS_WORKER_ENV, } from "../common/worker-runtime.js";
|
|
5
|
+
import { RUNTIME_POOL_DEPTH, RUNTIME_POOL_DEPTH_ENV, RUNTIME_PROCESS_WORKER_BOOT_ENV, RUNTIME_PROCESS_WORKER_BOOT_VERSION, RUNTIME_PROCESS_WORKER_ENV, } from "../common/worker-runtime.js";
|
|
6
6
|
import { getNodeBuiltinModule, getNodeProcess } from "../common/node-compat.js";
|
|
7
7
|
import { toSharedBufferRegion, } from "../common/shared-buffer-region.js";
|
|
8
8
|
import { createBunConnectionPrimitives } from "../connections/bun.js";
|
|
@@ -100,6 +100,7 @@ const DEFAULT_NODE_BINARY = "node";
|
|
|
100
100
|
const DENO_PROCESS_WORKER_BOOT_ENV_ALLOW = [
|
|
101
101
|
RUNTIME_PROCESS_WORKER_ENV,
|
|
102
102
|
RUNTIME_PROCESS_WORKER_BOOT_ENV,
|
|
103
|
+
RUNTIME_POOL_DEPTH_ENV,
|
|
103
104
|
].join(",");
|
|
104
105
|
const DENO_PROCESS_WORKER_INTERNAL_FLAGS = [
|
|
105
106
|
`--allow-env=${DENO_PROCESS_WORKER_BOOT_ENV_ALLOW}`,
|
|
@@ -354,6 +355,7 @@ const currentProcessEnv = () => ({
|
|
|
354
355
|
const processWorkerEnv = (extra) => ({
|
|
355
356
|
...currentProcessEnv(),
|
|
356
357
|
[RUNTIME_PROCESS_WORKER_ENV]: "1",
|
|
358
|
+
[RUNTIME_POOL_DEPTH_ENV]: String(RUNTIME_POOL_DEPTH + 1),
|
|
357
359
|
...extra,
|
|
358
360
|
});
|
|
359
361
|
const processWorkerBootEnv = (bootPayload) => processWorkerEnv({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Lock2, type Task } from "../memory/lock.js";
|
|
2
2
|
import type { AbortSignalOption, TaskTimeout } from "../types.js";
|
|
3
3
|
import { type SignalAbortStore } from "../shared/abortSignal.js";
|
|
4
4
|
type RawArguments = unknown;
|
|
@@ -9,10 +9,11 @@ type CreateHostTxQueueArgs = {
|
|
|
9
9
|
max?: number;
|
|
10
10
|
lock: Lock2;
|
|
11
11
|
returnLock: Lock2;
|
|
12
|
+
releaseBufferReferenceReturn?: (token: bigint) => void;
|
|
12
13
|
abortSignals?: Pick<SignalAbortStore, "getSignal" | "setSignal" | "resetSignal" | "closeNow">;
|
|
13
14
|
now?: () => number;
|
|
14
15
|
};
|
|
15
|
-
export declare function createHostTxQueue({ max, lock, returnLock, abortSignals, now, }: CreateHostTxQueueArgs): {
|
|
16
|
+
export declare function createHostTxQueue({ max, lock, returnLock, releaseBufferReferenceReturn, abortSignals, now, }: CreateHostTxQueueArgs): {
|
|
16
17
|
rejectAll: (reason: string) => void;
|
|
17
18
|
hasPendingFrames: () => boolean;
|
|
18
19
|
txIdle: () => boolean;
|