knitting 0.1.46
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/LICENSE +202 -0
- package/README.md +632 -0
- package/knitting.d.ts +4 -0
- package/knitting.js +5 -0
- package/map.md +264 -0
- package/package.json +77 -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_shared_memory.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_shm.node +0 -0
- package/prebuilds/darwin-x64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/darwin-x64-node-127/knitting_shm.node +0 -0
- package/prebuilds/darwin-x64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/darwin-x64-node-137/knitting_shm.node +0 -0
- package/prebuilds/linux-x64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/linux-x64-node-127/knitting_shm.node +0 -0
- package/prebuilds/linux-x64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/linux-x64-node-137/knitting_shm.node +0 -0
- package/process-shared-buffer.d.ts +1 -0
- package/process-shared-buffer.js +1 -0
- package/scripts/build-native-addons.ts +295 -0
- package/src/api.d.ts +55 -0
- package/src/api.js +384 -0
- package/src/common/envelope.d.ts +11 -0
- package/src/common/envelope.js +8 -0
- package/src/common/module-url.d.ts +1 -0
- package/src/common/module-url.js +24 -0
- package/src/common/node-compat.d.ts +20 -0
- package/src/common/node-compat.js +24 -0
- package/src/common/path-canonical.d.ts +6 -0
- package/src/common/path-canonical.js +41 -0
- package/src/common/runtime.d.ts +15 -0
- package/src/common/runtime.js +91 -0
- package/src/common/shared-buffer-region.d.ts +11 -0
- package/src/common/shared-buffer-region.js +21 -0
- package/src/common/shared-buffer-text.d.ts +16 -0
- package/src/common/shared-buffer-text.js +65 -0
- package/src/common/task-source.d.ts +2 -0
- package/src/common/task-source.js +79 -0
- package/src/common/task-symbol.d.ts +1 -0
- package/src/common/task-symbol.js +1 -0
- package/src/common/with-resolvers.d.ts +9 -0
- package/src/common/with-resolvers.js +23 -0
- package/src/common/worker-runtime.d.ts +40 -0
- package/src/common/worker-runtime.js +52 -0
- package/src/connections/bun.d.ts +20 -0
- package/src/connections/bun.js +159 -0
- package/src/connections/deno.d.ts +20 -0
- package/src/connections/deno.js +150 -0
- package/src/connections/file-descriptor.d.ts +37 -0
- package/src/connections/file-descriptor.js +139 -0
- package/src/connections/index.d.ts +3 -0
- package/src/connections/index.js +3 -0
- package/src/connections/node-addons.d.ts +5 -0
- package/src/connections/node-addons.js +43 -0
- package/src/connections/node.d.ts +29 -0
- package/src/connections/node.js +59 -0
- package/src/connections/posix.d.ts +31 -0
- package/src/connections/posix.js +71 -0
- package/src/connections/process-shared-buffer.d.ts +67 -0
- package/src/connections/process-shared-buffer.js +267 -0
- package/src/connections/types.d.ts +48 -0
- package/src/connections/types.js +37 -0
- package/src/error.d.ts +13 -0
- package/src/error.js +49 -0
- package/src/ipc/tools/ring-queue.d.ts +33 -0
- package/src/ipc/tools/ring-queue.js +159 -0
- package/src/ipc/transport/shared-memory.d.ts +25 -0
- package/src/ipc/transport/shared-memory.js +35 -0
- package/src/knitting_shared_memory.cc +436 -0
- package/src/knitting_shm.cc +476 -0
- package/src/memory/byte-carpet.d.ts +73 -0
- package/src/memory/byte-carpet.js +157 -0
- package/src/memory/lock.d.ts +190 -0
- package/src/memory/lock.js +856 -0
- package/src/memory/payload-config.d.ts +22 -0
- package/src/memory/payload-config.js +67 -0
- package/src/memory/payloadCodec.d.ts +46 -0
- package/src/memory/payloadCodec.js +1157 -0
- package/src/memory/regionRegistry.d.ts +17 -0
- package/src/memory/regionRegistry.js +285 -0
- package/src/memory/shared-buffer-io.d.ts +53 -0
- package/src/memory/shared-buffer-io.js +380 -0
- package/src/permission/index.d.ts +2 -0
- package/src/permission/index.js +2 -0
- package/src/permission/protocol.d.ts +166 -0
- package/src/permission/protocol.js +640 -0
- package/src/runtime/balancer.d.ts +19 -0
- package/src/runtime/balancer.js +149 -0
- package/src/runtime/dispatcher.d.ts +34 -0
- package/src/runtime/dispatcher.js +142 -0
- package/src/runtime/inline-executor.d.ts +10 -0
- package/src/runtime/inline-executor.js +270 -0
- package/src/runtime/pool.d.ts +43 -0
- package/src/runtime/pool.js +922 -0
- package/src/runtime/tx-queue.d.ts +25 -0
- package/src/runtime/tx-queue.js +144 -0
- package/src/shared/abortSignal.d.ts +23 -0
- package/src/shared/abortSignal.js +126 -0
- package/src/types.d.ts +283 -0
- package/src/types.js +2 -0
- package/src/worker/composable-runners.d.ts +12 -0
- package/src/worker/composable-runners.js +105 -0
- package/src/worker/loop.d.ts +2 -0
- package/src/worker/loop.js +453 -0
- package/src/worker/rx-queue.d.ts +22 -0
- package/src/worker/rx-queue.js +124 -0
- package/src/worker/safety/index.d.ts +4 -0
- package/src/worker/safety/index.js +4 -0
- package/src/worker/safety/performance.d.ts +1 -0
- package/src/worker/safety/performance.js +17 -0
- package/src/worker/safety/process.d.ts +2 -0
- package/src/worker/safety/process.js +79 -0
- package/src/worker/safety/startup.d.ts +16 -0
- package/src/worker/safety/startup.js +30 -0
- package/src/worker/safety/worker-data.d.ts +2 -0
- package/src/worker/safety/worker-data.js +36 -0
- package/src/worker/task-loader.d.ts +26 -0
- package/src/worker/task-loader.js +66 -0
- package/src/worker/timers.d.ts +18 -0
- package/src/worker/timers.js +97 -0
|
@@ -0,0 +1,856 @@
|
|
|
1
|
+
import RingQueue from "../ipc/tools/ring-queue.js";
|
|
2
|
+
import { decodePayload, encodePayload } from "./payloadCodec.js";
|
|
3
|
+
import { createSharedArrayBuffer, createWasmSharedArrayBuffer, } from "../common/runtime.js";
|
|
4
|
+
import { toSharedBufferRegion, } from "../common/shared-buffer-region.js";
|
|
5
|
+
import { probeLockBufferTextCompat, } from "../common/shared-buffer-text.js";
|
|
6
|
+
import { resolvePayloadBufferOptions, } from "./payload-config.js";
|
|
7
|
+
/**
|
|
8
|
+
* TODO: Compose all the instance where the array is passed as argument
|
|
9
|
+
*/
|
|
10
|
+
export var PayloadSignal;
|
|
11
|
+
(function (PayloadSignal) {
|
|
12
|
+
PayloadSignal[PayloadSignal["UNREACHABLE"] = 0] = "UNREACHABLE";
|
|
13
|
+
PayloadSignal[PayloadSignal["BigInt"] = 2] = "BigInt";
|
|
14
|
+
PayloadSignal[PayloadSignal["True"] = 3] = "True";
|
|
15
|
+
PayloadSignal[PayloadSignal["False"] = 4] = "False";
|
|
16
|
+
PayloadSignal[PayloadSignal["Undefined"] = 5] = "Undefined";
|
|
17
|
+
PayloadSignal[PayloadSignal["NaN"] = 6] = "NaN";
|
|
18
|
+
PayloadSignal[PayloadSignal["Float64"] = 9] = "Float64";
|
|
19
|
+
PayloadSignal[PayloadSignal["Null"] = 10] = "Null";
|
|
20
|
+
})(PayloadSignal || (PayloadSignal = {}));
|
|
21
|
+
export var PayloadBuffer;
|
|
22
|
+
(function (PayloadBuffer) {
|
|
23
|
+
PayloadBuffer[PayloadBuffer["BORDER_SIGNAL_BUFFER"] = 11] = "BORDER_SIGNAL_BUFFER";
|
|
24
|
+
PayloadBuffer[PayloadBuffer["String"] = 11] = "String";
|
|
25
|
+
PayloadBuffer[PayloadBuffer["Json"] = 12] = "Json";
|
|
26
|
+
PayloadBuffer[PayloadBuffer["StaticString"] = 15] = "StaticString";
|
|
27
|
+
PayloadBuffer[PayloadBuffer["StaticJson"] = 16] = "StaticJson";
|
|
28
|
+
PayloadBuffer[PayloadBuffer["Binary"] = 17] = "Binary";
|
|
29
|
+
PayloadBuffer[PayloadBuffer["StaticBinary"] = 18] = "StaticBinary";
|
|
30
|
+
PayloadBuffer[PayloadBuffer["Int32Array"] = 19] = "Int32Array";
|
|
31
|
+
PayloadBuffer[PayloadBuffer["Float64Array"] = 20] = "Float64Array";
|
|
32
|
+
PayloadBuffer[PayloadBuffer["BigInt64Array"] = 21] = "BigInt64Array";
|
|
33
|
+
PayloadBuffer[PayloadBuffer["BigUint64Array"] = 22] = "BigUint64Array";
|
|
34
|
+
PayloadBuffer[PayloadBuffer["DataView"] = 23] = "DataView";
|
|
35
|
+
PayloadBuffer[PayloadBuffer["Error"] = 24] = "Error";
|
|
36
|
+
PayloadBuffer[PayloadBuffer["Date"] = 25] = "Date";
|
|
37
|
+
PayloadBuffer[PayloadBuffer["Symbol"] = 26] = "Symbol";
|
|
38
|
+
PayloadBuffer[PayloadBuffer["StaticSymbol"] = 27] = "StaticSymbol";
|
|
39
|
+
PayloadBuffer[PayloadBuffer["BigInt"] = 28] = "BigInt";
|
|
40
|
+
PayloadBuffer[PayloadBuffer["StaticBigInt"] = 29] = "StaticBigInt";
|
|
41
|
+
PayloadBuffer[PayloadBuffer["StaticInt32Array"] = 31] = "StaticInt32Array";
|
|
42
|
+
PayloadBuffer[PayloadBuffer["StaticFloat64Array"] = 32] = "StaticFloat64Array";
|
|
43
|
+
PayloadBuffer[PayloadBuffer["StaticBigInt64Array"] = 33] = "StaticBigInt64Array";
|
|
44
|
+
PayloadBuffer[PayloadBuffer["StaticBigUint64Array"] = 34] = "StaticBigUint64Array";
|
|
45
|
+
PayloadBuffer[PayloadBuffer["StaticDataView"] = 35] = "StaticDataView";
|
|
46
|
+
PayloadBuffer[PayloadBuffer["ArrayBuffer"] = 36] = "ArrayBuffer";
|
|
47
|
+
PayloadBuffer[PayloadBuffer["StaticArrayBuffer"] = 37] = "StaticArrayBuffer";
|
|
48
|
+
PayloadBuffer[PayloadBuffer["Buffer"] = 38] = "Buffer";
|
|
49
|
+
PayloadBuffer[PayloadBuffer["StaticBuffer"] = 39] = "StaticBuffer";
|
|
50
|
+
PayloadBuffer[PayloadBuffer["EnvelopeStaticHeader"] = 40] = "EnvelopeStaticHeader";
|
|
51
|
+
PayloadBuffer[PayloadBuffer["EnvelopeDynamicHeader"] = 41] = "EnvelopeDynamicHeader";
|
|
52
|
+
PayloadBuffer[PayloadBuffer["EnvelopeStaticHeaderString"] = 42] = "EnvelopeStaticHeaderString";
|
|
53
|
+
PayloadBuffer[PayloadBuffer["EnvelopeDynamicHeaderString"] = 43] = "EnvelopeDynamicHeaderString";
|
|
54
|
+
PayloadBuffer[PayloadBuffer["ExternalPayload"] = 44] = "ExternalPayload";
|
|
55
|
+
PayloadBuffer[PayloadBuffer["StaticExternalPayload"] = 45] = "StaticExternalPayload";
|
|
56
|
+
PayloadBuffer[PayloadBuffer["ProcessSharedBuffer"] = 46] = "ProcessSharedBuffer";
|
|
57
|
+
})(PayloadBuffer || (PayloadBuffer = {}));
|
|
58
|
+
export var LockBound;
|
|
59
|
+
(function (LockBound) {
|
|
60
|
+
LockBound[LockBound["paddingLock"] = 0] = "paddingLock";
|
|
61
|
+
LockBound[LockBound["padding"] = 0] = "padding";
|
|
62
|
+
LockBound[LockBound["slots"] = 32] = "slots";
|
|
63
|
+
LockBound[LockBound["header"] = 0] = "header";
|
|
64
|
+
})(LockBound || (LockBound = {}));
|
|
65
|
+
export const LOCK_CACHE_LINE_BYTES = 64;
|
|
66
|
+
export const LOCK_SECTOR_BYTES = 256;
|
|
67
|
+
export const PromisePayloadMarker = Symbol.for("knitting.promise.payload");
|
|
68
|
+
const TASK_LOCAL_FLAGS_INDEX = 7;
|
|
69
|
+
const TASK_LOCAL_PROMISE_PENDING_FLAG = 1 << 0;
|
|
70
|
+
const TASK_LOCAL_PROMISE_TRACKED_FLAG = 1 << 1;
|
|
71
|
+
export const beginPromisePayload = (task) => {
|
|
72
|
+
const flags = task[TASK_LOCAL_FLAGS_INDEX];
|
|
73
|
+
if ((flags & TASK_LOCAL_PROMISE_PENDING_FLAG) !== 0)
|
|
74
|
+
return false;
|
|
75
|
+
task[TASK_LOCAL_FLAGS_INDEX] = (flags | TASK_LOCAL_PROMISE_PENDING_FLAG) >>> 0;
|
|
76
|
+
return true;
|
|
77
|
+
};
|
|
78
|
+
export const finishPromisePayload = (task) => {
|
|
79
|
+
task[TASK_LOCAL_FLAGS_INDEX] =
|
|
80
|
+
(task[TASK_LOCAL_FLAGS_INDEX] & ~TASK_LOCAL_PROMISE_PENDING_FLAG) >>> 0;
|
|
81
|
+
};
|
|
82
|
+
export const isPromisePayloadPending = (task) => (task[TASK_LOCAL_FLAGS_INDEX] & TASK_LOCAL_PROMISE_PENDING_FLAG) !== 0;
|
|
83
|
+
export const resetTaskLocalFlags = (task) => {
|
|
84
|
+
task[TASK_LOCAL_FLAGS_INDEX] = 0;
|
|
85
|
+
};
|
|
86
|
+
export var TaskIndex;
|
|
87
|
+
(function (TaskIndex) {
|
|
88
|
+
/**
|
|
89
|
+
* Worker -> host response flags word.
|
|
90
|
+
*/
|
|
91
|
+
TaskIndex[TaskIndex["FlagsToHost"] = 0] = "FlagsToHost";
|
|
92
|
+
/**
|
|
93
|
+
* Host -> worker request function id (low 16 bits).
|
|
94
|
+
* High 16 bits are reserved for caller metadata on request path.
|
|
95
|
+
* NOTE: shares the same storage word as `FlagsToHost`.
|
|
96
|
+
*/
|
|
97
|
+
TaskIndex[TaskIndex["FunctionID"] = 0] = "FunctionID";
|
|
98
|
+
TaskIndex[TaskIndex["ID"] = 1] = "ID";
|
|
99
|
+
TaskIndex[TaskIndex["Type"] = 2] = "Type";
|
|
100
|
+
TaskIndex[TaskIndex["Start"] = 3] = "Start";
|
|
101
|
+
TaskIndex[TaskIndex["End"] = 4] = "End";
|
|
102
|
+
TaskIndex[TaskIndex["PayloadLen"] = 5] = "PayloadLen";
|
|
103
|
+
/**
|
|
104
|
+
* Low 5 bits: region slot index (0..31).
|
|
105
|
+
* High 27 bits: reserved for caller metadata (e.g. enqueue timing).
|
|
106
|
+
*/
|
|
107
|
+
TaskIndex[TaskIndex["slotBuffer"] = 6] = "slotBuffer";
|
|
108
|
+
TaskIndex[TaskIndex["Size"] = 8] = "Size";
|
|
109
|
+
/**
|
|
110
|
+
* Total slot length in Uint32 words, including the task header.
|
|
111
|
+
*/
|
|
112
|
+
TaskIndex[TaskIndex["TotalBuff"] = 144] = "TotalBuff";
|
|
113
|
+
})(TaskIndex || (TaskIndex = {}));
|
|
114
|
+
export const TASK_SLOT_INDEX_BITS = 5;
|
|
115
|
+
export const TASK_SLOT_INDEX_MASK = (1 << TASK_SLOT_INDEX_BITS) - 1;
|
|
116
|
+
export const TASK_SLOT_META_BITS = 32 - TASK_SLOT_INDEX_BITS;
|
|
117
|
+
export const TASK_SLOT_META_VALUE_MASK = 0xFFFFFFFF >>> TASK_SLOT_INDEX_BITS;
|
|
118
|
+
const TASK_SLOT_META_PACKED_MASK = (~TASK_SLOT_INDEX_MASK) >>> 0;
|
|
119
|
+
export const TASK_FUNCTION_ID_BITS = 16;
|
|
120
|
+
export const TASK_FUNCTION_ID_MASK = (1 << TASK_FUNCTION_ID_BITS) - 1;
|
|
121
|
+
export const TASK_FUNCTION_META_BITS = 32 - TASK_FUNCTION_ID_BITS;
|
|
122
|
+
export const TASK_FUNCTION_META_VALUE_MASK = 0xFFFFFFFF >>>
|
|
123
|
+
TASK_FUNCTION_ID_BITS;
|
|
124
|
+
const TASK_FUNCTION_META_PACKED_MASK = (~TASK_FUNCTION_ID_MASK) >>> 0;
|
|
125
|
+
export const getTaskFunctionID = (task) => task[TaskIndex.FunctionID] & TASK_FUNCTION_ID_MASK;
|
|
126
|
+
export const setTaskFunctionID = (task, functionID) => {
|
|
127
|
+
task[TaskIndex.FunctionID] = ((task[TaskIndex.FunctionID] & TASK_FUNCTION_META_PACKED_MASK) |
|
|
128
|
+
(functionID & TASK_FUNCTION_ID_MASK)) >>> 0;
|
|
129
|
+
};
|
|
130
|
+
export const getTaskFunctionMeta = (task) => (task[TaskIndex.FunctionID] >>> TASK_FUNCTION_ID_BITS) &
|
|
131
|
+
TASK_FUNCTION_META_VALUE_MASK;
|
|
132
|
+
export const setTaskFunctionMeta = (task, value) => {
|
|
133
|
+
const encodedMeta = ((value & TASK_FUNCTION_META_VALUE_MASK) << TASK_FUNCTION_ID_BITS) >>> 0;
|
|
134
|
+
task[TaskIndex.FunctionID] =
|
|
135
|
+
((task[TaskIndex.FunctionID] & TASK_FUNCTION_ID_MASK) | encodedMeta) >>> 0;
|
|
136
|
+
};
|
|
137
|
+
export const getTaskSlotIndex = (task) => task[TaskIndex.slotBuffer] & TASK_SLOT_INDEX_MASK;
|
|
138
|
+
export const setTaskSlotIndex = (task, slotIndex) => {
|
|
139
|
+
task[TaskIndex.slotBuffer] = ((task[TaskIndex.slotBuffer] & TASK_SLOT_META_PACKED_MASK) |
|
|
140
|
+
(slotIndex & TASK_SLOT_INDEX_MASK)) >>> 0;
|
|
141
|
+
};
|
|
142
|
+
export const getTaskSlotMeta = (task) => (task[TaskIndex.slotBuffer] >>> TASK_SLOT_INDEX_BITS) &
|
|
143
|
+
TASK_SLOT_META_VALUE_MASK;
|
|
144
|
+
export const setTaskSlotMeta = (task, value) => {
|
|
145
|
+
const encodedMeta = ((value & TASK_SLOT_META_VALUE_MASK) << TASK_SLOT_INDEX_BITS) >>> 0;
|
|
146
|
+
task[TaskIndex.slotBuffer] =
|
|
147
|
+
((task[TaskIndex.slotBuffer] & TASK_SLOT_INDEX_MASK) | encodedMeta) >>> 0;
|
|
148
|
+
};
|
|
149
|
+
export var TaskFlag;
|
|
150
|
+
(function (TaskFlag) {
|
|
151
|
+
TaskFlag[TaskFlag["Reject"] = 1] = "Reject";
|
|
152
|
+
})(TaskFlag || (TaskFlag = {}));
|
|
153
|
+
// Main queue lock layout in bytes.
|
|
154
|
+
// The queue protocol uses two Int32 signal words, each on its own cache line:
|
|
155
|
+
// - hostBits at byte 0
|
|
156
|
+
// - workerBits at byte 64
|
|
157
|
+
// A slot is free when both words agree on that bit (XOR = 0), and in use when
|
|
158
|
+
// they differ (XOR = 1). The 32-bit mask supports up to 32 concurrent slots.
|
|
159
|
+
export const LOCK_WORD_BYTES = Int32Array.BYTES_PER_ELEMENT;
|
|
160
|
+
export const LOCK_HOST_BITS_OFFSET_BYTES = LockBound.paddingLock;
|
|
161
|
+
export const LOCK_WORKER_BITS_OFFSET_BYTES = LOCK_CACHE_LINE_BYTES;
|
|
162
|
+
export const LOCK_SECTOR_BYTE_LENGTH = LOCK_SECTOR_BYTES;
|
|
163
|
+
// Payload allocator lock layout in bytes.
|
|
164
|
+
// Share the same SAB as the main queue lock, but give each word its own line.
|
|
165
|
+
export const PAYLOAD_LOCK_HOST_BITS_OFFSET_BYTES = LOCK_CACHE_LINE_BYTES * 2;
|
|
166
|
+
export const PAYLOAD_LOCK_WORKER_BITS_OFFSET_BYTES = LOCK_CACHE_LINE_BYTES * 3;
|
|
167
|
+
export const PAYLOAD_LOCK_SECTOR_BYTE_LENGTH = LOCK_SECTOR_BYTES;
|
|
168
|
+
// Header layout in Uint32 units.
|
|
169
|
+
// Each slot stores aligned static payload bytes first, then pads to the next
|
|
170
|
+
// cache line so the task header has a dedicated 64-byte line.
|
|
171
|
+
export const HEADER_SLOT_STRIDE_U32 = LockBound.header + TaskIndex.TotalBuff;
|
|
172
|
+
export const HEADER_SLOT_STRIDE_BYTES = HEADER_SLOT_STRIDE_U32 *
|
|
173
|
+
Uint32Array.BYTES_PER_ELEMENT;
|
|
174
|
+
export const HEADER_TASK_LINE_U32 = LOCK_CACHE_LINE_BYTES /
|
|
175
|
+
Uint32Array.BYTES_PER_ELEMENT;
|
|
176
|
+
export const HEADER_STATIC_PAYLOAD_U32 = TaskIndex.TotalBuff -
|
|
177
|
+
HEADER_TASK_LINE_U32;
|
|
178
|
+
export const HEADER_TASK_OFFSET_IN_SLOT_U32 = HEADER_STATIC_PAYLOAD_U32;
|
|
179
|
+
export const HEADER_U32_LENGTH = LockBound.header +
|
|
180
|
+
(HEADER_SLOT_STRIDE_U32 * LockBound.slots);
|
|
181
|
+
export const HEADER_BYTE_LENGTH = HEADER_U32_LENGTH *
|
|
182
|
+
Uint32Array.BYTES_PER_ELEMENT;
|
|
183
|
+
let INDEX_ID = 0;
|
|
184
|
+
const INIT_VAL = PayloadSignal.UNREACHABLE;
|
|
185
|
+
const def = (_) => { };
|
|
186
|
+
const createTaskShell = () => {
|
|
187
|
+
const task = new Uint32Array(TaskIndex.Size);
|
|
188
|
+
task.value = null;
|
|
189
|
+
task.resolve = def;
|
|
190
|
+
task.reject = def;
|
|
191
|
+
task[TASK_LOCAL_FLAGS_INDEX] = 0;
|
|
192
|
+
return task;
|
|
193
|
+
};
|
|
194
|
+
export const makeTask = () => {
|
|
195
|
+
const task = createTaskShell();
|
|
196
|
+
task[TaskIndex.ID] = INDEX_ID++;
|
|
197
|
+
return task;
|
|
198
|
+
};
|
|
199
|
+
const fillTaskFrom = (task, array, at) => {
|
|
200
|
+
task[0] = array[at];
|
|
201
|
+
task[1] = array[at + 1];
|
|
202
|
+
task[2] = array[at + 2];
|
|
203
|
+
task[3] = array[at + 3];
|
|
204
|
+
task[4] = array[at + 4];
|
|
205
|
+
task[5] = array[at + 5];
|
|
206
|
+
task[6] = array[at + 6];
|
|
207
|
+
// Task word 7 is local-only scratch state; never restore it from shared memory.
|
|
208
|
+
task[TASK_LOCAL_FLAGS_INDEX] = 0;
|
|
209
|
+
};
|
|
210
|
+
const makeTaskFrom = (array, at) => {
|
|
211
|
+
const task = createTaskShell();
|
|
212
|
+
fillTaskFrom(task, array, at);
|
|
213
|
+
return task;
|
|
214
|
+
};
|
|
215
|
+
// could be inlined
|
|
216
|
+
const settleTask = (task) => {
|
|
217
|
+
if (task[TaskIndex["FlagsToHost"]] === 0) {
|
|
218
|
+
task.resolve(task.value);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
task.reject(task.value);
|
|
222
|
+
// restarting the flag
|
|
223
|
+
task[TaskIndex["FlagsToHost"]] = 0;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload, payloadConfig, payloadSector, textCompat, resultList, toSentList, recycleList, }) => {
|
|
227
|
+
// Layout within `lockSectorRegion`:
|
|
228
|
+
// - hostBits starts at byte 0
|
|
229
|
+
// - workerBits starts at byte 64
|
|
230
|
+
// These queue signal words are intentionally placed on separate cache lines.
|
|
231
|
+
// The remaining two cache lines in the 256-byte sector are reserved for the
|
|
232
|
+
// payload allocator lock (`PAYLOAD_LOCK_*` at bytes 128 and 192).
|
|
233
|
+
//
|
|
234
|
+
// Important: encode() always toggles `hostBits` and decode/resolveHost always
|
|
235
|
+
// toggles `workerBits`, regardless of which thread calls them. This is why
|
|
236
|
+
// the "return lock" (worker->host responses) still publishes into `hostBits`.
|
|
237
|
+
const lockSectorRegion = toSharedBufferRegion(LockBoundSector ??
|
|
238
|
+
createWasmSharedArrayBuffer(LOCK_SECTOR_BYTE_LENGTH));
|
|
239
|
+
const LockBoundSAB = lockSectorRegion.sab;
|
|
240
|
+
const hostBits = new Int32Array(LockBoundSAB, lockSectorRegion.byteOffset + LOCK_HOST_BITS_OFFSET_BYTES, 1);
|
|
241
|
+
const workerBits = new Int32Array(LockBoundSAB, lockSectorRegion.byteOffset + LOCK_WORKER_BITS_OFFSET_BYTES, 1);
|
|
242
|
+
const headersRegion = toSharedBufferRegion(headers ?? createWasmSharedArrayBuffer(HEADER_BYTE_LENGTH));
|
|
243
|
+
const headersBuffer = new Uint32Array(headersRegion.sab, headersRegion.byteOffset, headersRegion.byteLength >>> 2);
|
|
244
|
+
const headersSlotStride = headerSlotStrideU32 ?? HEADER_SLOT_STRIDE_U32;
|
|
245
|
+
const resolvedPayloadConfig = resolvePayloadBufferOptions({
|
|
246
|
+
sab: payload,
|
|
247
|
+
options: payloadConfig,
|
|
248
|
+
});
|
|
249
|
+
const payloadSAB = payload ??
|
|
250
|
+
(resolvedPayloadConfig.mode === "growable"
|
|
251
|
+
? createSharedArrayBuffer(resolvedPayloadConfig.payloadInitialBytes, resolvedPayloadConfig.payloadMaxByteLength)
|
|
252
|
+
: createSharedArrayBuffer(resolvedPayloadConfig.payloadInitialBytes));
|
|
253
|
+
const payloadLockRegion = toSharedBufferRegion(payloadSector ?? lockSectorRegion);
|
|
254
|
+
const resolvedTextCompat = textCompat ?? probeLockBufferTextCompat({
|
|
255
|
+
headers: headersRegion,
|
|
256
|
+
payload: payloadSAB,
|
|
257
|
+
});
|
|
258
|
+
let promiseHandler;
|
|
259
|
+
const encodeTask = encodePayload({
|
|
260
|
+
payload: {
|
|
261
|
+
sab: payloadSAB,
|
|
262
|
+
config: resolvedPayloadConfig,
|
|
263
|
+
},
|
|
264
|
+
headersBuffer,
|
|
265
|
+
headerSlotStrideU32: headersSlotStride,
|
|
266
|
+
lockSector: payloadLockRegion,
|
|
267
|
+
textCompat: resolvedTextCompat,
|
|
268
|
+
onPromise: (task, isRejected, value) => {
|
|
269
|
+
if ((task[TASK_LOCAL_FLAGS_INDEX] & TASK_LOCAL_PROMISE_TRACKED_FLAG) !== 0 &&
|
|
270
|
+
pendingPromiseCount > 0) {
|
|
271
|
+
task[TASK_LOCAL_FLAGS_INDEX] =
|
|
272
|
+
(task[TASK_LOCAL_FLAGS_INDEX] & ~TASK_LOCAL_PROMISE_TRACKED_FLAG) >>> 0;
|
|
273
|
+
pendingPromiseCount = (pendingPromiseCount - 1) | 0;
|
|
274
|
+
}
|
|
275
|
+
promiseHandler(task, isRejected, value);
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
const decodeTask = decodePayload({
|
|
279
|
+
payload: {
|
|
280
|
+
sab: payloadSAB,
|
|
281
|
+
config: resolvedPayloadConfig,
|
|
282
|
+
},
|
|
283
|
+
headersBuffer,
|
|
284
|
+
headerSlotStrideU32: headersSlotStride,
|
|
285
|
+
lockSector: payloadLockRegion,
|
|
286
|
+
textCompat: resolvedTextCompat,
|
|
287
|
+
});
|
|
288
|
+
let LastLocal = 0 | 0;
|
|
289
|
+
let LastWorker = 0 | 0;
|
|
290
|
+
let lastTake = 32 | 0;
|
|
291
|
+
const toBeSent = toSentList ?? new RingQueue();
|
|
292
|
+
const recyclecList = recycleList ?? new RingQueue();
|
|
293
|
+
const resolved = resultList ?? new RingQueue();
|
|
294
|
+
let deferredCount = 0 | 0;
|
|
295
|
+
let pendingPromiseCount = 0 | 0;
|
|
296
|
+
// Atomics aliases (hot path)
|
|
297
|
+
const a_load = Atomics.load;
|
|
298
|
+
const a_store = Atomics.store;
|
|
299
|
+
// Sender-side cached shadow of the receiver-owned queue word. Under the XSC
|
|
300
|
+
// false-busy-only sender-side staleness property, this may hide newly freed
|
|
301
|
+
// lanes but cannot make a genuinely pending lane appear free. Refresh only
|
|
302
|
+
// when the cached free set is exhausted.
|
|
303
|
+
let workerShadow = a_load(workerBits, 0) | 0;
|
|
304
|
+
const refreshWorkerShadow = () => workerShadow = a_load(workerBits, 0) | 0;
|
|
305
|
+
const ensureSenderStateHasFree = (state) => (~state) !== 0 ? state : (LastLocal ^ refreshWorkerShadow()) | 0;
|
|
306
|
+
// RingQueue method aliases (hot path)
|
|
307
|
+
const toBeSentPush = (task) => toBeSent.push(task);
|
|
308
|
+
const toBeSentShift = () => toBeSent.shiftNoClear();
|
|
309
|
+
const toBeSentUnshift = (task) => toBeSent.unshift(task);
|
|
310
|
+
const recycleShift = () => recyclecList.shiftNoClear();
|
|
311
|
+
const resolvedPush = (task) => resolved.push(task);
|
|
312
|
+
const clz32 = Math.clz32;
|
|
313
|
+
const slotBaseU32 = LockBound.header + HEADER_TASK_OFFSET_IN_SLOT_U32;
|
|
314
|
+
const takeTask = ({ queue }) => (at) => {
|
|
315
|
+
const off = (at * headersSlotStride) + slotBaseU32;
|
|
316
|
+
const task = queue[headersBuffer[off + TaskIndex.ID]];
|
|
317
|
+
fillTaskFrom(task, headersBuffer, off);
|
|
318
|
+
return task;
|
|
319
|
+
};
|
|
320
|
+
const enlist = (task) => toBeSentPush(task);
|
|
321
|
+
const trackDeferredTask = (task) => {
|
|
322
|
+
const flags = task[TASK_LOCAL_FLAGS_INDEX];
|
|
323
|
+
if ((flags & TASK_LOCAL_PROMISE_TRACKED_FLAG) !== 0)
|
|
324
|
+
return;
|
|
325
|
+
task[TASK_LOCAL_FLAGS_INDEX] = (flags | TASK_LOCAL_PROMISE_TRACKED_FLAG) >>> 0;
|
|
326
|
+
pendingPromiseCount = (pendingPromiseCount + 1) | 0;
|
|
327
|
+
};
|
|
328
|
+
const encodeTaskValue = (task, slotIndex) => encodeTask(task, slotIndex);
|
|
329
|
+
let selectedSlotIndex = 0 | 0, selectedSlotBit = 0 >>> 0;
|
|
330
|
+
const encodeWithState = (task, state) => {
|
|
331
|
+
const free = ~state;
|
|
332
|
+
if (free === 0)
|
|
333
|
+
return 0;
|
|
334
|
+
if (!encodeTaskValue(task, selectedSlotIndex = 31 - clz32(free)))
|
|
335
|
+
return 0;
|
|
336
|
+
encodeAt(task, selectedSlotIndex, selectedSlotBit = 1 << selectedSlotIndex);
|
|
337
|
+
return selectedSlotBit;
|
|
338
|
+
};
|
|
339
|
+
const encodeManyFrom = (list) => {
|
|
340
|
+
let state = ensureSenderStateHasFree((LastLocal ^ workerShadow) | 0);
|
|
341
|
+
let encoded = 0 | 0;
|
|
342
|
+
if (list === toBeSent) {
|
|
343
|
+
while (true) {
|
|
344
|
+
const task = toBeSentShift();
|
|
345
|
+
if (!task)
|
|
346
|
+
break;
|
|
347
|
+
state = ensureSenderStateHasFree(state);
|
|
348
|
+
const bit = encodeWithState(task, state) | 0;
|
|
349
|
+
if (bit === 0) {
|
|
350
|
+
toBeSentUnshift(task);
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
state = (state ^ bit) | 0;
|
|
354
|
+
encoded = (encoded + 1) | 0;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
while (true) {
|
|
359
|
+
const task = list.shiftNoClear();
|
|
360
|
+
if (!task)
|
|
361
|
+
break;
|
|
362
|
+
state = ensureSenderStateHasFree(state);
|
|
363
|
+
const bit = encodeWithState(task, state) | 0;
|
|
364
|
+
if (bit === 0) {
|
|
365
|
+
list.unshift(task);
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
state = (state ^ bit) | 0;
|
|
369
|
+
encoded = (encoded + 1) | 0;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return encoded;
|
|
373
|
+
};
|
|
374
|
+
const encodeManyTrackedFrom = (list) => {
|
|
375
|
+
let state = ensureSenderStateHasFree((LastLocal ^ workerShadow) | 0);
|
|
376
|
+
let encoded = 0 | 0;
|
|
377
|
+
deferredCount = 0 | 0;
|
|
378
|
+
if (list === toBeSent) {
|
|
379
|
+
while (true) {
|
|
380
|
+
const task = toBeSentShift();
|
|
381
|
+
if (!task)
|
|
382
|
+
break;
|
|
383
|
+
state = ensureSenderStateHasFree(state);
|
|
384
|
+
const bit = encodeWithState(task, state) | 0;
|
|
385
|
+
if (bit === 0) {
|
|
386
|
+
if (isPromisePayloadPending(task)) {
|
|
387
|
+
deferredCount = (deferredCount + 1) | 0;
|
|
388
|
+
trackDeferredTask(task);
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
toBeSentUnshift(task);
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
state = (state ^ bit) | 0;
|
|
395
|
+
encoded = (encoded + 1) | 0;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
while (true) {
|
|
400
|
+
const task = list.shiftNoClear();
|
|
401
|
+
if (!task)
|
|
402
|
+
break;
|
|
403
|
+
state = ensureSenderStateHasFree(state);
|
|
404
|
+
const bit = encodeWithState(task, state) | 0;
|
|
405
|
+
if (bit === 0) {
|
|
406
|
+
if (isPromisePayloadPending(task)) {
|
|
407
|
+
deferredCount = (deferredCount + 1) | 0;
|
|
408
|
+
trackDeferredTask(task);
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
list.unshift(task);
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
state = (state ^ bit) | 0;
|
|
415
|
+
encoded = (encoded + 1) | 0;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return encoded;
|
|
419
|
+
};
|
|
420
|
+
const encodeAll = () => {
|
|
421
|
+
if (toBeSent.isEmpty)
|
|
422
|
+
return true;
|
|
423
|
+
encodeManyTrackedFrom(toBeSent);
|
|
424
|
+
deferredCount = 0 | 0;
|
|
425
|
+
return toBeSent.isEmpty;
|
|
426
|
+
};
|
|
427
|
+
const storeHost = (bit) => a_store(hostBits, 0, LastLocal = (LastLocal ^ bit) | 0);
|
|
428
|
+
const storeWorker = (bit) => a_store(workerBits, 0, LastWorker = (LastWorker ^ bit) | 0);
|
|
429
|
+
const encode = (task, state = (LastLocal ^ workerShadow) | 0) => {
|
|
430
|
+
state = ensureSenderStateHasFree(state);
|
|
431
|
+
const free = ~state;
|
|
432
|
+
if (free === 0)
|
|
433
|
+
return false;
|
|
434
|
+
if (!encodeTaskValue(task, selectedSlotIndex = 31 - clz32(free))) {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
return encodeAt(task, selectedSlotIndex, selectedSlotBit = 1 << selectedSlotIndex);
|
|
438
|
+
};
|
|
439
|
+
const encodeTracked = (task, state = (LastLocal ^ workerShadow) | 0) => {
|
|
440
|
+
deferredCount = 0 | 0;
|
|
441
|
+
state = ensureSenderStateHasFree(state);
|
|
442
|
+
const free = ~state;
|
|
443
|
+
if (free === 0)
|
|
444
|
+
return false;
|
|
445
|
+
if (!encodeTaskValue(task, selectedSlotIndex = 31 - clz32(free))) {
|
|
446
|
+
if (isPromisePayloadPending(task)) {
|
|
447
|
+
deferredCount = 1;
|
|
448
|
+
trackDeferredTask(task);
|
|
449
|
+
}
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
return encodeAt(task, selectedSlotIndex, selectedSlotBit = 1 << selectedSlotIndex);
|
|
453
|
+
};
|
|
454
|
+
const encodeAt = (task, at, bit) => {
|
|
455
|
+
const off = (at * headersSlotStride) + slotBaseU32;
|
|
456
|
+
headersBuffer[off] = task[0];
|
|
457
|
+
headersBuffer[off + 1] = task[1];
|
|
458
|
+
headersBuffer[off + 2] = task[2];
|
|
459
|
+
headersBuffer[off + 3] = task[3];
|
|
460
|
+
headersBuffer[off + 4] = task[4];
|
|
461
|
+
headersBuffer[off + 5] = task[5];
|
|
462
|
+
headersBuffer[off + 6] = task[6];
|
|
463
|
+
headersBuffer[off + TASK_LOCAL_FLAGS_INDEX] = 0;
|
|
464
|
+
storeHost(bit);
|
|
465
|
+
return true;
|
|
466
|
+
};
|
|
467
|
+
const hasSpace = () => (hostBits[0] ^ LastWorker) !== 0;
|
|
468
|
+
/**
|
|
469
|
+
* WORKER SIDE: decode
|
|
470
|
+
*/
|
|
471
|
+
const decode = () => {
|
|
472
|
+
let diff = (a_load(hostBits, 0) ^ LastWorker) | 0;
|
|
473
|
+
if (diff === 0)
|
|
474
|
+
return false;
|
|
475
|
+
let last = lastTake;
|
|
476
|
+
let consumedBits = 0 | 0;
|
|
477
|
+
try {
|
|
478
|
+
if (last === 32) {
|
|
479
|
+
decodeAt(selectedSlotIndex = 31 - clz32(diff));
|
|
480
|
+
selectedSlotBit = 1 << (last = selectedSlotIndex);
|
|
481
|
+
diff ^= selectedSlotBit;
|
|
482
|
+
consumedBits = (consumedBits ^ selectedSlotBit) | 0;
|
|
483
|
+
}
|
|
484
|
+
while (diff !== 0) {
|
|
485
|
+
let pick = diff & ((1 << last) - 1);
|
|
486
|
+
if (pick === 0)
|
|
487
|
+
pick = diff;
|
|
488
|
+
decodeAt(selectedSlotIndex = 31 - clz32(pick));
|
|
489
|
+
selectedSlotBit = 1 << (last = selectedSlotIndex);
|
|
490
|
+
diff ^= selectedSlotBit;
|
|
491
|
+
consumedBits = (consumedBits ^ selectedSlotBit) | 0;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
finally {
|
|
495
|
+
if (consumedBits !== 0)
|
|
496
|
+
storeWorker(consumedBits);
|
|
497
|
+
}
|
|
498
|
+
lastTake = last;
|
|
499
|
+
return true;
|
|
500
|
+
};
|
|
501
|
+
/**
|
|
502
|
+
* HOST SIDE: decode version
|
|
503
|
+
*/
|
|
504
|
+
const resolveHost = ({ queue, onResolved, shouldSettle, activeRejectPlaceholder, }) => {
|
|
505
|
+
const getTask = takeTask({ queue });
|
|
506
|
+
let lastResolved = 32;
|
|
507
|
+
if (activeRejectPlaceholder !== undefined && onResolved) {
|
|
508
|
+
const onResolvedTask = onResolved;
|
|
509
|
+
const inactiveReject = activeRejectPlaceholder;
|
|
510
|
+
return () => {
|
|
511
|
+
let diff = (a_load(hostBits, 0) ^ LastWorker) | 0;
|
|
512
|
+
if (diff === 0)
|
|
513
|
+
return 0;
|
|
514
|
+
let modified = 0;
|
|
515
|
+
let consumedBits = 0 | 0;
|
|
516
|
+
let last = lastResolved;
|
|
517
|
+
if (last === 32) {
|
|
518
|
+
const idx = 31 - clz32(diff);
|
|
519
|
+
const selectedBit = 1 << idx;
|
|
520
|
+
const task = getTask(idx);
|
|
521
|
+
decodeTask(task, idx);
|
|
522
|
+
consumedBits = (consumedBits ^ selectedBit) | 0;
|
|
523
|
+
if (task.reject !== inactiveReject) {
|
|
524
|
+
settleTask(task);
|
|
525
|
+
onResolvedTask(task);
|
|
526
|
+
}
|
|
527
|
+
diff ^= selectedBit;
|
|
528
|
+
modified++;
|
|
529
|
+
if ((modified & 7) === 0 && consumedBits !== 0) {
|
|
530
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
531
|
+
a_store(workerBits, 0, LastWorker);
|
|
532
|
+
consumedBits = 0 | 0;
|
|
533
|
+
}
|
|
534
|
+
last = idx;
|
|
535
|
+
}
|
|
536
|
+
while (diff !== 0) {
|
|
537
|
+
const lowerMask = last === 31 ? 0x7fffffff : ((1 << last) - 1);
|
|
538
|
+
let pick = diff & lowerMask;
|
|
539
|
+
if (pick === 0)
|
|
540
|
+
pick = diff;
|
|
541
|
+
const idx = 31 - clz32(pick);
|
|
542
|
+
const selectedBit = 1 << idx;
|
|
543
|
+
const task = getTask(idx);
|
|
544
|
+
decodeTask(task, idx);
|
|
545
|
+
consumedBits = (consumedBits ^ selectedBit) | 0;
|
|
546
|
+
if (task.reject !== inactiveReject) {
|
|
547
|
+
settleTask(task);
|
|
548
|
+
onResolvedTask(task);
|
|
549
|
+
}
|
|
550
|
+
diff ^= selectedBit;
|
|
551
|
+
modified++;
|
|
552
|
+
if ((modified & 7) === 0 && consumedBits !== 0) {
|
|
553
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
554
|
+
a_store(workerBits, 0, LastWorker);
|
|
555
|
+
consumedBits = 0 | 0;
|
|
556
|
+
}
|
|
557
|
+
last = idx;
|
|
558
|
+
}
|
|
559
|
+
if (consumedBits !== 0) {
|
|
560
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
561
|
+
a_store(workerBits, 0, LastWorker);
|
|
562
|
+
}
|
|
563
|
+
lastResolved = last;
|
|
564
|
+
return modified;
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
if (!shouldSettle) {
|
|
568
|
+
if (!onResolved) {
|
|
569
|
+
return () => {
|
|
570
|
+
let diff = (a_load(hostBits, 0) ^ LastWorker) | 0;
|
|
571
|
+
if (diff === 0)
|
|
572
|
+
return 0;
|
|
573
|
+
let modified = 0;
|
|
574
|
+
let consumedBits = 0 | 0;
|
|
575
|
+
let last = lastResolved;
|
|
576
|
+
if (last === 32) {
|
|
577
|
+
const idx = 31 - clz32(diff);
|
|
578
|
+
const selectedBit = 1 << idx;
|
|
579
|
+
const task = getTask(idx);
|
|
580
|
+
decodeTask(task, idx);
|
|
581
|
+
consumedBits = (consumedBits ^ selectedBit) | 0;
|
|
582
|
+
settleTask(task);
|
|
583
|
+
diff ^= selectedBit;
|
|
584
|
+
modified++;
|
|
585
|
+
if ((modified & 7) === 0 && consumedBits !== 0) {
|
|
586
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
587
|
+
a_store(workerBits, 0, LastWorker);
|
|
588
|
+
consumedBits = 0 | 0;
|
|
589
|
+
}
|
|
590
|
+
last = idx;
|
|
591
|
+
}
|
|
592
|
+
while (diff !== 0) {
|
|
593
|
+
const lowerMask = last === 31 ? 0x7fffffff : ((1 << last) - 1);
|
|
594
|
+
let pick = diff & lowerMask;
|
|
595
|
+
if (pick === 0)
|
|
596
|
+
pick = diff;
|
|
597
|
+
const idx = 31 - clz32(pick);
|
|
598
|
+
const selectedBit = 1 << idx;
|
|
599
|
+
const task = getTask(idx);
|
|
600
|
+
decodeTask(task, idx);
|
|
601
|
+
consumedBits = (consumedBits ^ selectedBit) | 0;
|
|
602
|
+
settleTask(task);
|
|
603
|
+
diff ^= selectedBit;
|
|
604
|
+
modified++;
|
|
605
|
+
if ((modified & 7) === 0 && consumedBits !== 0) {
|
|
606
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
607
|
+
a_store(workerBits, 0, LastWorker);
|
|
608
|
+
consumedBits = 0 | 0;
|
|
609
|
+
}
|
|
610
|
+
last = idx;
|
|
611
|
+
}
|
|
612
|
+
if (consumedBits !== 0) {
|
|
613
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
614
|
+
a_store(workerBits, 0, LastWorker);
|
|
615
|
+
}
|
|
616
|
+
lastResolved = last;
|
|
617
|
+
return modified;
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
const onResolvedTask = onResolved;
|
|
621
|
+
return () => {
|
|
622
|
+
let diff = (a_load(hostBits, 0) ^ LastWorker) | 0;
|
|
623
|
+
if (diff === 0)
|
|
624
|
+
return 0;
|
|
625
|
+
let modified = 0;
|
|
626
|
+
let consumedBits = 0 | 0;
|
|
627
|
+
let last = lastResolved;
|
|
628
|
+
if (last === 32) {
|
|
629
|
+
const idx = 31 - clz32(diff);
|
|
630
|
+
const selectedBit = 1 << idx;
|
|
631
|
+
const task = getTask(idx);
|
|
632
|
+
decodeTask(task, idx);
|
|
633
|
+
consumedBits = (consumedBits ^ selectedBit) | 0;
|
|
634
|
+
settleTask(task);
|
|
635
|
+
onResolvedTask(task);
|
|
636
|
+
diff ^= selectedBit;
|
|
637
|
+
modified++;
|
|
638
|
+
if ((modified & 7) === 0 && consumedBits !== 0) {
|
|
639
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
640
|
+
a_store(workerBits, 0, LastWorker);
|
|
641
|
+
consumedBits = 0 | 0;
|
|
642
|
+
}
|
|
643
|
+
last = idx;
|
|
644
|
+
}
|
|
645
|
+
while (diff !== 0) {
|
|
646
|
+
const lowerMask = last === 31 ? 0x7fffffff : ((1 << last) - 1);
|
|
647
|
+
let pick = diff & lowerMask;
|
|
648
|
+
if (pick === 0)
|
|
649
|
+
pick = diff;
|
|
650
|
+
const idx = 31 - clz32(pick);
|
|
651
|
+
const selectedBit = 1 << idx;
|
|
652
|
+
const task = getTask(idx);
|
|
653
|
+
decodeTask(task, idx);
|
|
654
|
+
consumedBits = (consumedBits ^ selectedBit) | 0;
|
|
655
|
+
settleTask(task);
|
|
656
|
+
onResolvedTask(task);
|
|
657
|
+
diff ^= selectedBit;
|
|
658
|
+
modified++;
|
|
659
|
+
if ((modified & 7) === 0 && consumedBits !== 0) {
|
|
660
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
661
|
+
a_store(workerBits, 0, LastWorker);
|
|
662
|
+
consumedBits = 0 | 0;
|
|
663
|
+
}
|
|
664
|
+
last = idx;
|
|
665
|
+
}
|
|
666
|
+
if (consumedBits !== 0) {
|
|
667
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
668
|
+
a_store(workerBits, 0, LastWorker);
|
|
669
|
+
}
|
|
670
|
+
lastResolved = last;
|
|
671
|
+
return modified;
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
const shouldSettleTask = shouldSettle;
|
|
675
|
+
if (!onResolved) {
|
|
676
|
+
return () => {
|
|
677
|
+
let diff = (a_load(hostBits, 0) ^ LastWorker) | 0;
|
|
678
|
+
if (diff === 0)
|
|
679
|
+
return 0;
|
|
680
|
+
let modified = 0;
|
|
681
|
+
let consumedBits = 0 | 0;
|
|
682
|
+
let last = lastResolved;
|
|
683
|
+
if (last === 32) {
|
|
684
|
+
const idx = 31 - clz32(diff);
|
|
685
|
+
const selectedBit = 1 << idx;
|
|
686
|
+
const task = getTask(idx);
|
|
687
|
+
decodeTask(task, idx);
|
|
688
|
+
consumedBits = (consumedBits ^ selectedBit) | 0;
|
|
689
|
+
if (shouldSettleTask(task)) {
|
|
690
|
+
settleTask(task);
|
|
691
|
+
}
|
|
692
|
+
diff ^= selectedBit;
|
|
693
|
+
modified++;
|
|
694
|
+
if ((modified & 7) === 0 && consumedBits !== 0) {
|
|
695
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
696
|
+
a_store(workerBits, 0, LastWorker);
|
|
697
|
+
consumedBits = 0 | 0;
|
|
698
|
+
}
|
|
699
|
+
last = idx;
|
|
700
|
+
}
|
|
701
|
+
while (diff !== 0) {
|
|
702
|
+
const lowerMask = last === 31 ? 0x7fffffff : ((1 << last) - 1);
|
|
703
|
+
let pick = diff & lowerMask;
|
|
704
|
+
if (pick === 0)
|
|
705
|
+
pick = diff;
|
|
706
|
+
const idx = 31 - clz32(pick);
|
|
707
|
+
const selectedBit = 1 << idx;
|
|
708
|
+
const task = getTask(idx);
|
|
709
|
+
decodeTask(task, idx);
|
|
710
|
+
consumedBits = (consumedBits ^ selectedBit) | 0;
|
|
711
|
+
if (shouldSettleTask(task)) {
|
|
712
|
+
settleTask(task);
|
|
713
|
+
}
|
|
714
|
+
diff ^= selectedBit;
|
|
715
|
+
modified++;
|
|
716
|
+
if ((modified & 7) === 0 && consumedBits !== 0) {
|
|
717
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
718
|
+
a_store(workerBits, 0, LastWorker);
|
|
719
|
+
consumedBits = 0 | 0;
|
|
720
|
+
}
|
|
721
|
+
last = idx;
|
|
722
|
+
}
|
|
723
|
+
if (consumedBits !== 0) {
|
|
724
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
725
|
+
a_store(workerBits, 0, LastWorker);
|
|
726
|
+
}
|
|
727
|
+
lastResolved = last;
|
|
728
|
+
return modified;
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
const onResolvedTask = onResolved;
|
|
732
|
+
return () => {
|
|
733
|
+
let diff = (a_load(hostBits, 0) ^ LastWorker) | 0;
|
|
734
|
+
if (diff === 0)
|
|
735
|
+
return 0;
|
|
736
|
+
let modified = 0;
|
|
737
|
+
let consumedBits = 0 | 0;
|
|
738
|
+
let last = lastResolved;
|
|
739
|
+
if (last === 32) {
|
|
740
|
+
const idx = 31 - clz32(diff);
|
|
741
|
+
const selectedBit = 1 << idx;
|
|
742
|
+
const task = getTask(idx);
|
|
743
|
+
decodeTask(task, idx);
|
|
744
|
+
consumedBits = (consumedBits ^ selectedBit) | 0;
|
|
745
|
+
if (shouldSettleTask(task)) {
|
|
746
|
+
settleTask(task);
|
|
747
|
+
onResolvedTask(task);
|
|
748
|
+
}
|
|
749
|
+
diff ^= selectedBit;
|
|
750
|
+
modified++;
|
|
751
|
+
if ((modified & 7) === 0 && consumedBits !== 0) {
|
|
752
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
753
|
+
a_store(workerBits, 0, LastWorker);
|
|
754
|
+
consumedBits = 0 | 0;
|
|
755
|
+
}
|
|
756
|
+
last = idx;
|
|
757
|
+
}
|
|
758
|
+
while (diff !== 0) {
|
|
759
|
+
const lowerMask = last === 31 ? 0x7fffffff : ((1 << last) - 1);
|
|
760
|
+
let pick = diff & lowerMask;
|
|
761
|
+
if (pick === 0)
|
|
762
|
+
pick = diff;
|
|
763
|
+
const idx = 31 - clz32(pick);
|
|
764
|
+
const selectedBit = 1 << idx;
|
|
765
|
+
const task = getTask(idx);
|
|
766
|
+
decodeTask(task, idx);
|
|
767
|
+
consumedBits = (consumedBits ^ selectedBit) | 0;
|
|
768
|
+
if (shouldSettleTask(task)) {
|
|
769
|
+
settleTask(task);
|
|
770
|
+
onResolvedTask(task);
|
|
771
|
+
}
|
|
772
|
+
diff ^= selectedBit;
|
|
773
|
+
modified++;
|
|
774
|
+
if ((modified & 7) === 0 && consumedBits !== 0) {
|
|
775
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
776
|
+
a_store(workerBits, 0, LastWorker);
|
|
777
|
+
consumedBits = 0 | 0;
|
|
778
|
+
}
|
|
779
|
+
last = idx;
|
|
780
|
+
}
|
|
781
|
+
if (consumedBits !== 0) {
|
|
782
|
+
LastWorker = (LastWorker ^ consumedBits) | 0;
|
|
783
|
+
a_store(workerBits, 0, LastWorker);
|
|
784
|
+
}
|
|
785
|
+
lastResolved = last;
|
|
786
|
+
return modified;
|
|
787
|
+
};
|
|
788
|
+
};
|
|
789
|
+
const decodeAt = (at) => {
|
|
790
|
+
const off = (at * headersSlotStride) + slotBaseU32;
|
|
791
|
+
const recycled = recycleShift();
|
|
792
|
+
let task;
|
|
793
|
+
if (recycled) {
|
|
794
|
+
fillTaskFrom(recycled, headersBuffer, off);
|
|
795
|
+
recycled.value = null;
|
|
796
|
+
recycled.resolve = def;
|
|
797
|
+
recycled.reject = def;
|
|
798
|
+
task = recycled;
|
|
799
|
+
}
|
|
800
|
+
else {
|
|
801
|
+
task = makeTaskFrom(headersBuffer, off);
|
|
802
|
+
}
|
|
803
|
+
decodeTask(task, at);
|
|
804
|
+
resolvedPush(task);
|
|
805
|
+
return true;
|
|
806
|
+
};
|
|
807
|
+
const publish = (task) => {
|
|
808
|
+
if (encodeTracked(task))
|
|
809
|
+
return true;
|
|
810
|
+
if ((deferredCount | 0) !== 0) {
|
|
811
|
+
deferredCount = 0 | 0;
|
|
812
|
+
return false;
|
|
813
|
+
}
|
|
814
|
+
toBeSentPush(task);
|
|
815
|
+
return false;
|
|
816
|
+
};
|
|
817
|
+
const flushPending = () => {
|
|
818
|
+
if (toBeSent.isEmpty)
|
|
819
|
+
return false;
|
|
820
|
+
const encoded = encodeManyTrackedFrom(toBeSent) | 0;
|
|
821
|
+
deferredCount = 0 | 0;
|
|
822
|
+
return encoded !== 0;
|
|
823
|
+
};
|
|
824
|
+
const resetPendingState = () => {
|
|
825
|
+
toBeSent.clear();
|
|
826
|
+
deferredCount = 0 | 0;
|
|
827
|
+
pendingPromiseCount = 0 | 0;
|
|
828
|
+
};
|
|
829
|
+
return {
|
|
830
|
+
enlist,
|
|
831
|
+
encode,
|
|
832
|
+
encodeManyFrom,
|
|
833
|
+
encodeAll,
|
|
834
|
+
publish,
|
|
835
|
+
flushPending,
|
|
836
|
+
decode,
|
|
837
|
+
hasSpace,
|
|
838
|
+
resolved,
|
|
839
|
+
hostBits,
|
|
840
|
+
workerBits,
|
|
841
|
+
recyclecList,
|
|
842
|
+
resolveHost,
|
|
843
|
+
hasPendingFrames: () => toBeSent.size !== 0,
|
|
844
|
+
getPendingFrameCount: () => toBeSent.size | 0,
|
|
845
|
+
getPendingPromiseCount: () => pendingPromiseCount | 0,
|
|
846
|
+
resetPendingState,
|
|
847
|
+
takeDeferredCount: () => {
|
|
848
|
+
const count = deferredCount | 0;
|
|
849
|
+
deferredCount = 0 | 0;
|
|
850
|
+
return count;
|
|
851
|
+
},
|
|
852
|
+
setPromiseHandler: (handler) => {
|
|
853
|
+
promiseHandler = handler;
|
|
854
|
+
},
|
|
855
|
+
};
|
|
856
|
+
};
|