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.
Files changed (121) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +632 -0
  3. package/knitting.d.ts +4 -0
  4. package/knitting.js +5 -0
  5. package/map.md +264 -0
  6. package/package.json +77 -0
  7. package/prebuilds/darwin-arm64-node-127/knitting_shared_memory.node +0 -0
  8. package/prebuilds/darwin-arm64-node-127/knitting_shm.node +0 -0
  9. package/prebuilds/darwin-arm64-node-137/knitting_shared_memory.node +0 -0
  10. package/prebuilds/darwin-arm64-node-137/knitting_shm.node +0 -0
  11. package/prebuilds/darwin-x64-node-127/knitting_shared_memory.node +0 -0
  12. package/prebuilds/darwin-x64-node-127/knitting_shm.node +0 -0
  13. package/prebuilds/darwin-x64-node-137/knitting_shared_memory.node +0 -0
  14. package/prebuilds/darwin-x64-node-137/knitting_shm.node +0 -0
  15. package/prebuilds/linux-x64-node-127/knitting_shared_memory.node +0 -0
  16. package/prebuilds/linux-x64-node-127/knitting_shm.node +0 -0
  17. package/prebuilds/linux-x64-node-137/knitting_shared_memory.node +0 -0
  18. package/prebuilds/linux-x64-node-137/knitting_shm.node +0 -0
  19. package/process-shared-buffer.d.ts +1 -0
  20. package/process-shared-buffer.js +1 -0
  21. package/scripts/build-native-addons.ts +295 -0
  22. package/src/api.d.ts +55 -0
  23. package/src/api.js +384 -0
  24. package/src/common/envelope.d.ts +11 -0
  25. package/src/common/envelope.js +8 -0
  26. package/src/common/module-url.d.ts +1 -0
  27. package/src/common/module-url.js +24 -0
  28. package/src/common/node-compat.d.ts +20 -0
  29. package/src/common/node-compat.js +24 -0
  30. package/src/common/path-canonical.d.ts +6 -0
  31. package/src/common/path-canonical.js +41 -0
  32. package/src/common/runtime.d.ts +15 -0
  33. package/src/common/runtime.js +91 -0
  34. package/src/common/shared-buffer-region.d.ts +11 -0
  35. package/src/common/shared-buffer-region.js +21 -0
  36. package/src/common/shared-buffer-text.d.ts +16 -0
  37. package/src/common/shared-buffer-text.js +65 -0
  38. package/src/common/task-source.d.ts +2 -0
  39. package/src/common/task-source.js +79 -0
  40. package/src/common/task-symbol.d.ts +1 -0
  41. package/src/common/task-symbol.js +1 -0
  42. package/src/common/with-resolvers.d.ts +9 -0
  43. package/src/common/with-resolvers.js +23 -0
  44. package/src/common/worker-runtime.d.ts +40 -0
  45. package/src/common/worker-runtime.js +52 -0
  46. package/src/connections/bun.d.ts +20 -0
  47. package/src/connections/bun.js +159 -0
  48. package/src/connections/deno.d.ts +20 -0
  49. package/src/connections/deno.js +150 -0
  50. package/src/connections/file-descriptor.d.ts +37 -0
  51. package/src/connections/file-descriptor.js +139 -0
  52. package/src/connections/index.d.ts +3 -0
  53. package/src/connections/index.js +3 -0
  54. package/src/connections/node-addons.d.ts +5 -0
  55. package/src/connections/node-addons.js +43 -0
  56. package/src/connections/node.d.ts +29 -0
  57. package/src/connections/node.js +59 -0
  58. package/src/connections/posix.d.ts +31 -0
  59. package/src/connections/posix.js +71 -0
  60. package/src/connections/process-shared-buffer.d.ts +67 -0
  61. package/src/connections/process-shared-buffer.js +267 -0
  62. package/src/connections/types.d.ts +48 -0
  63. package/src/connections/types.js +37 -0
  64. package/src/error.d.ts +13 -0
  65. package/src/error.js +49 -0
  66. package/src/ipc/tools/ring-queue.d.ts +33 -0
  67. package/src/ipc/tools/ring-queue.js +159 -0
  68. package/src/ipc/transport/shared-memory.d.ts +25 -0
  69. package/src/ipc/transport/shared-memory.js +35 -0
  70. package/src/knitting_shared_memory.cc +436 -0
  71. package/src/knitting_shm.cc +476 -0
  72. package/src/memory/byte-carpet.d.ts +73 -0
  73. package/src/memory/byte-carpet.js +157 -0
  74. package/src/memory/lock.d.ts +190 -0
  75. package/src/memory/lock.js +856 -0
  76. package/src/memory/payload-config.d.ts +22 -0
  77. package/src/memory/payload-config.js +67 -0
  78. package/src/memory/payloadCodec.d.ts +46 -0
  79. package/src/memory/payloadCodec.js +1157 -0
  80. package/src/memory/regionRegistry.d.ts +17 -0
  81. package/src/memory/regionRegistry.js +285 -0
  82. package/src/memory/shared-buffer-io.d.ts +53 -0
  83. package/src/memory/shared-buffer-io.js +380 -0
  84. package/src/permission/index.d.ts +2 -0
  85. package/src/permission/index.js +2 -0
  86. package/src/permission/protocol.d.ts +166 -0
  87. package/src/permission/protocol.js +640 -0
  88. package/src/runtime/balancer.d.ts +19 -0
  89. package/src/runtime/balancer.js +149 -0
  90. package/src/runtime/dispatcher.d.ts +34 -0
  91. package/src/runtime/dispatcher.js +142 -0
  92. package/src/runtime/inline-executor.d.ts +10 -0
  93. package/src/runtime/inline-executor.js +270 -0
  94. package/src/runtime/pool.d.ts +43 -0
  95. package/src/runtime/pool.js +922 -0
  96. package/src/runtime/tx-queue.d.ts +25 -0
  97. package/src/runtime/tx-queue.js +144 -0
  98. package/src/shared/abortSignal.d.ts +23 -0
  99. package/src/shared/abortSignal.js +126 -0
  100. package/src/types.d.ts +283 -0
  101. package/src/types.js +2 -0
  102. package/src/worker/composable-runners.d.ts +12 -0
  103. package/src/worker/composable-runners.js +105 -0
  104. package/src/worker/loop.d.ts +2 -0
  105. package/src/worker/loop.js +453 -0
  106. package/src/worker/rx-queue.d.ts +22 -0
  107. package/src/worker/rx-queue.js +124 -0
  108. package/src/worker/safety/index.d.ts +4 -0
  109. package/src/worker/safety/index.js +4 -0
  110. package/src/worker/safety/performance.d.ts +1 -0
  111. package/src/worker/safety/performance.js +17 -0
  112. package/src/worker/safety/process.d.ts +2 -0
  113. package/src/worker/safety/process.js +79 -0
  114. package/src/worker/safety/startup.d.ts +16 -0
  115. package/src/worker/safety/startup.js +30 -0
  116. package/src/worker/safety/worker-data.d.ts +2 -0
  117. package/src/worker/safety/worker-data.js +36 -0
  118. package/src/worker/task-loader.d.ts +26 -0
  119. package/src/worker/task-loader.js +66 -0
  120. package/src/worker/timers.d.ts +18 -0
  121. package/src/worker/timers.js +97 -0
@@ -0,0 +1,149 @@
1
+ const selectStrategy = (contexts, handlers, strategy) => {
2
+ switch (strategy ?? "roundRobin") {
3
+ case "roundRobin":
4
+ case "robinRound": // legacy alias
5
+ return roundRobin(contexts)(handlers)(handlers.length);
6
+ case "firstIdle":
7
+ return firstIdle(contexts)(handlers)(handlers.length);
8
+ case "randomLane":
9
+ return randomLane(contexts)(handlers)(handlers.length);
10
+ case "firstIdleOrRandom":
11
+ return firstIdleRandom(contexts)(handlers)(handlers.length);
12
+ }
13
+ // Unreachable code, but guarded for safety.
14
+ throw new Error(`Unknown balancer: ${strategy}`);
15
+ };
16
+ export const managerMethod = ({ contexts, balancer, handlers, inlinerGate, }) => {
17
+ const strategy = typeof balancer === "object" && balancer != null
18
+ ? balancer.strategy
19
+ : balancer;
20
+ if (contexts.length < 2) {
21
+ throw new Error(contexts.length === 0
22
+ ? "No threads available."
23
+ : "Cannot rotate with a single thread.");
24
+ }
25
+ if (handlers.length === 0) {
26
+ throw new Error("No handlers provided.");
27
+ }
28
+ const allInvoker = selectStrategy(contexts, handlers, strategy);
29
+ if (!inlinerGate) {
30
+ return allInvoker;
31
+ }
32
+ const inlinerIndex = inlinerGate.index | 0;
33
+ const threshold = Number.isFinite(inlinerGate.threshold)
34
+ ? Math.max(1, Math.floor(inlinerGate.threshold))
35
+ : 1;
36
+ if (threshold <= 1 || inlinerIndex < 0 || inlinerIndex >= handlers.length) {
37
+ return allInvoker;
38
+ }
39
+ const workerLaneCount = handlers.length - 1;
40
+ if (workerLaneCount <= 0) {
41
+ return allInvoker;
42
+ }
43
+ const workerHandlers = new Array(workerLaneCount);
44
+ const workerContexts = new Array(workerLaneCount);
45
+ for (let source = 0, lane = 0; source < handlers.length; source += 1) {
46
+ if (source === inlinerIndex)
47
+ continue;
48
+ workerHandlers[lane] = handlers[source];
49
+ workerContexts[lane] = contexts[source];
50
+ lane += 1;
51
+ }
52
+ const workerOnlyInvoker = selectStrategy(workerContexts, workerHandlers, strategy);
53
+ let inFlight = 0;
54
+ const releaseResolved = (value) => {
55
+ inFlight -= 1;
56
+ return value;
57
+ };
58
+ const releaseRejected = (error) => {
59
+ inFlight -= 1;
60
+ throw error;
61
+ };
62
+ return (args) => {
63
+ inFlight += 1;
64
+ const invoker = inFlight >= threshold ? allInvoker : workerOnlyInvoker;
65
+ try {
66
+ return invoker(args).then(releaseResolved, releaseRejected);
67
+ }
68
+ catch (error) {
69
+ inFlight -= 1;
70
+ throw error;
71
+ }
72
+ };
73
+ };
74
+ export function roundRobin(_contexts) {
75
+ return (handlers) => {
76
+ return (max) => {
77
+ const top = Math.min(max, handlers.length);
78
+ if (top <= 1) {
79
+ return (args) => handlers[0](args);
80
+ }
81
+ let rrCursor = 0;
82
+ return (args) => {
83
+ const lane = rrCursor;
84
+ rrCursor += 1;
85
+ if (rrCursor === top)
86
+ rrCursor = 0;
87
+ return handlers[lane](args);
88
+ };
89
+ };
90
+ };
91
+ }
92
+ export function firstIdle(contexts) {
93
+ const isSolved = contexts.map((ctx) => ctx.txIdle);
94
+ return (handlers) => {
95
+ return (max) => {
96
+ const laneCount = Math.min(max, handlers.length);
97
+ if (laneCount <= 1) {
98
+ return (args) => handlers[0](args);
99
+ }
100
+ let rrCursor = 0;
101
+ return (args) => {
102
+ for (let lane = 0; lane < laneCount; lane += 1) {
103
+ if (isSolved[lane]()) {
104
+ return handlers[lane](args);
105
+ }
106
+ }
107
+ const fallback = rrCursor;
108
+ rrCursor += 1;
109
+ if (rrCursor === laneCount)
110
+ rrCursor = 0;
111
+ return handlers[fallback](args);
112
+ };
113
+ };
114
+ };
115
+ }
116
+ export const randomLane = (_) => {
117
+ return (handlers) => {
118
+ return (max) => {
119
+ const laneCount = Math.min(max, handlers.length);
120
+ if (laneCount <= 1) {
121
+ return (args) => handlers[0](args);
122
+ }
123
+ return (args) => {
124
+ const lane = (Math.random() * laneCount) | 0;
125
+ return handlers[lane](args);
126
+ };
127
+ };
128
+ };
129
+ };
130
+ export function firstIdleRandom(contexts) {
131
+ const isSolved = contexts.map((ctx) => ctx.txIdle);
132
+ return (handlers) => {
133
+ return (max) => {
134
+ const laneCount = Math.min(max, handlers.length);
135
+ if (laneCount <= 1) {
136
+ return (args) => handlers[0](args);
137
+ }
138
+ return (args) => {
139
+ for (let lane = 0; lane < laneCount; lane += 1) {
140
+ if (isSolved[lane]()) {
141
+ return handlers[lane](args);
142
+ }
143
+ }
144
+ const fallback = (Math.random() * laneCount) | 0;
145
+ return handlers[fallback](args);
146
+ };
147
+ };
148
+ };
149
+ }
@@ -0,0 +1,34 @@
1
+ import { type MultiQueue } from "./tx-queue.js";
2
+ import { type MainSignal } from "../ipc/transport/shared-memory.js";
3
+ import { type RuntimeMessageChannelLike, type RuntimeMessagePortLike } from "../common/worker-runtime.js";
4
+ import type { DispatcherSettings } from "../types.js";
5
+ export declare const hostDispatcherLoop: ({ signalBox: { opView, txStatus, rxStatus, }, queue: { completeFrame, hasPendingFrames, flushToWorker, txIdle, }, channelHandler, dispatcherOptions, notifySignal, }: {
6
+ queue: MultiQueue;
7
+ signalBox: MainSignal;
8
+ channelHandler: ChannelHandler;
9
+ dispatcherOptions?: DispatcherSettings;
10
+ notifySignal?: () => void;
11
+ }) => {
12
+ check: {
13
+ (): void;
14
+ isRunning: boolean;
15
+ rerun: boolean;
16
+ };
17
+ };
18
+ export declare class ChannelHandler {
19
+ #private;
20
+ channel: RuntimeMessageChannelLike;
21
+ port1: RuntimeMessagePortLike;
22
+ port2: RuntimeMessagePortLike;
23
+ constructor();
24
+ notify(): void;
25
+ /**
26
+ * Opens the channel (if not already open) and sets the onmessage handler.
27
+ * This is the setup so `notify` can send a message to the port 1.
28
+ */
29
+ open(f: () => void): void;
30
+ /**
31
+ * Closes the channel if it is open.
32
+ */
33
+ close(): void;
34
+ }
@@ -0,0 +1,142 @@
1
+ import { createRuntimeMessageChannel, } from "../common/worker-runtime.js";
2
+ export const hostDispatcherLoop = ({ signalBox: { opView, txStatus, rxStatus, }, queue: { completeFrame, hasPendingFrames, flushToWorker, txIdle, }, channelHandler, dispatcherOptions, notifySignal, }) => {
3
+ const a_load = Atomics.load;
4
+ const a_store = Atomics.store;
5
+ const a_notify = Atomics.notify;
6
+ const canNotifySignal = opView.buffer instanceof SharedArrayBuffer;
7
+ const wakeSignal = notifySignal ??
8
+ (() => {
9
+ if (canNotifySignal)
10
+ a_notify(opView, 0, 1);
11
+ });
12
+ const notify = () => channelHandler.notify();
13
+ let stallCount = 0 | 0;
14
+ const STALL_FREE_LOOPS = Math.max(0, (dispatcherOptions?.stallFreeLoops ?? 128) | 0);
15
+ const MAX_BACKOFF_MS = Math.max(0, (dispatcherOptions?.maxBackoffMs ?? 10) | 0);
16
+ let backoffTimer;
17
+ // inFlight prevents re-entrancy when pool.ts fires check() concurrently
18
+ // from both send() and the channel callback. Cheaper than try/finally.
19
+ let inFlight = false;
20
+ const check = () => {
21
+ if (inFlight) {
22
+ // Another check() is already mid-drain; mark that a re-run is needed
23
+ // so the active invocation loops again before yielding.
24
+ check.rerun = true;
25
+ return;
26
+ }
27
+ inFlight = true;
28
+ if (backoffTimer !== undefined) {
29
+ clearTimeout(backoffTimer);
30
+ backoffTimer = undefined;
31
+ }
32
+ do {
33
+ check.rerun = false;
34
+ txStatus[0] = 1;
35
+ // Wake the worker before draining so it can start processing while we flush.
36
+ if (a_load(rxStatus, 0) === 0) {
37
+ a_store(opView, 0, 1);
38
+ wakeSignal();
39
+ }
40
+ // Drain loop: local vars so V8 keeps them as unboxed int32.
41
+ let anyProgressed = false;
42
+ let progressed = true;
43
+ while (progressed) {
44
+ progressed = false;
45
+ if (completeFrame() > 0) {
46
+ progressed = true;
47
+ anyProgressed = true;
48
+ }
49
+ while (hasPendingFrames()) {
50
+ if (!flushToWorker())
51
+ break;
52
+ progressed = true;
53
+ anyProgressed = true;
54
+ }
55
+ }
56
+ txStatus[0] = 0;
57
+ if (!txIdle()) {
58
+ if (anyProgressed || hasPendingFrames()) {
59
+ stallCount = 0 | 0;
60
+ }
61
+ else {
62
+ stallCount = (stallCount + 1) | 0;
63
+ }
64
+ inFlight = false;
65
+ scheduleNotify();
66
+ return;
67
+ }
68
+ // Queue is fully drained.
69
+ stallCount = 0 | 0;
70
+ } while (check.rerun);
71
+ check.isRunning = false;
72
+ inFlight = false;
73
+ };
74
+ check.isRunning = false;
75
+ check.rerun = false;
76
+ const scheduleNotify = () => {
77
+ if (stallCount <= STALL_FREE_LOOPS) {
78
+ notify();
79
+ return;
80
+ }
81
+ // One delayed wakeup at a time; fresh send() calls preempt via check directly.
82
+ if (backoffTimer !== undefined)
83
+ return;
84
+ let delay = (stallCount - STALL_FREE_LOOPS - 1) | 0;
85
+ if (delay < 0)
86
+ delay = 0;
87
+ else if (delay > MAX_BACKOFF_MS)
88
+ delay = MAX_BACKOFF_MS;
89
+ // Release isRunning during backoff so pool.ts send() can restart the loop.
90
+ check.isRunning = false;
91
+ backoffTimer = setTimeout(() => {
92
+ backoffTimer = undefined;
93
+ if (!check.isRunning) {
94
+ check.isRunning = true;
95
+ check();
96
+ }
97
+ }, delay);
98
+ };
99
+ return { check };
100
+ };
101
+ export class ChannelHandler {
102
+ channel;
103
+ port1;
104
+ port2;
105
+ #post2;
106
+ constructor() {
107
+ this.channel = createRuntimeMessageChannel();
108
+ this.port1 = this.channel.port1;
109
+ this.port2 = this.channel.port2;
110
+ this.#post2 = (message) => this.port2.postMessage(message);
111
+ }
112
+ notify() {
113
+ this.#post2(null);
114
+ }
115
+ /**
116
+ * Opens the channel (if not already open) and sets the onmessage handler.
117
+ * This is the setup so `notify` can send a message to the port 1.
118
+ */
119
+ open(f) {
120
+ const port1 = this.port1;
121
+ if (typeof port1.on === "function") {
122
+ port1.on("message", f);
123
+ }
124
+ else {
125
+ // @ts-ignore
126
+ port1.onmessage = f;
127
+ }
128
+ this.port1.start?.();
129
+ this.port2.start?.();
130
+ }
131
+ /**
132
+ * Closes the channel if it is open.
133
+ */
134
+ close() {
135
+ //@ts-ignore
136
+ this.port1.onmessage = null;
137
+ //@ts-ignore
138
+ this.port2.onmessage = null;
139
+ this.port1.close?.();
140
+ this.port2.close?.();
141
+ }
142
+ }
@@ -0,0 +1,10 @@
1
+ import type { WorkerCall, tasks } from "../types.js";
2
+ export declare const createInlineExecutor: ({ tasks, genTaskID, batchSize, }: {
3
+ tasks: tasks;
4
+ genTaskID: () => number;
5
+ batchSize?: number;
6
+ }) => {
7
+ readonly kills: () => Promise<void>;
8
+ readonly call: ({ fnNumber }: WorkerCall) => (args: unknown) => import("../common/with-resolvers.js").PromiseWithMaybeReject<unknown>;
9
+ readonly txIdle: () => boolean;
10
+ };
@@ -0,0 +1,270 @@
1
+ import { withResolvers } from "../common/with-resolvers.js";
2
+ import RingQueue from "../ipc/tools/ring-queue.js";
3
+ import { createRuntimeMessageChannel } from "../common/worker-runtime.js";
4
+ const normalizeTimeout = (timeout) => {
5
+ if (timeout == null)
6
+ return undefined;
7
+ if (typeof timeout === "number") {
8
+ return timeout >= 0
9
+ ? { ms: timeout, kind: 0 /* TimeoutKind.Reject */, value: new Error("Task timeout") }
10
+ : undefined;
11
+ }
12
+ const ms = timeout.time;
13
+ if (!(ms >= 0))
14
+ return undefined;
15
+ if ("default" in timeout) {
16
+ return { ms, kind: 1 /* TimeoutKind.Resolve */, value: timeout.default };
17
+ }
18
+ if (timeout.maybe === true) {
19
+ return { ms, kind: 1 /* TimeoutKind.Resolve */, value: undefined };
20
+ }
21
+ if ("error" in timeout) {
22
+ return { ms, kind: 0 /* TimeoutKind.Reject */, value: timeout.error };
23
+ }
24
+ return { ms, kind: 0 /* TimeoutKind.Reject */, value: new Error("Task timeout") };
25
+ };
26
+ const raceTimeout = (promise, spec) => new Promise((resolve, reject) => {
27
+ let done = false;
28
+ const timer = setTimeout(() => {
29
+ if (done)
30
+ return;
31
+ done = true;
32
+ if (spec.kind === 1 /* TimeoutKind.Resolve */) {
33
+ resolve(spec.value);
34
+ }
35
+ else {
36
+ reject(spec.value);
37
+ }
38
+ }, spec.ms);
39
+ promise.then((value) => {
40
+ if (done)
41
+ return;
42
+ done = true;
43
+ clearTimeout(timer);
44
+ resolve(value);
45
+ }, (err) => {
46
+ if (done)
47
+ return;
48
+ done = true;
49
+ clearTimeout(timer);
50
+ reject(err);
51
+ });
52
+ });
53
+ const INLINE_ABORT_TOOLKIT = (() => {
54
+ const hasAborted = () => false;
55
+ return {
56
+ hasAborted,
57
+ };
58
+ })();
59
+ const composeInlineCallable = (fn, timeout, useAbortToolkit = false) => {
60
+ const normalized = normalizeTimeout(timeout);
61
+ const run = useAbortToolkit
62
+ ? (args) => fn(args, INLINE_ABORT_TOOLKIT)
63
+ : fn;
64
+ if (!normalized)
65
+ return run;
66
+ return (args) => {
67
+ const result = run(args);
68
+ return result instanceof Promise ? raceTimeout(result, normalized) : result;
69
+ };
70
+ };
71
+ export const createInlineExecutor = ({ tasks, genTaskID, batchSize, }) => {
72
+ const entries = Object.values(tasks)
73
+ .sort((a, b) => a.id - b.id);
74
+ const runners = entries.map((entry) => composeInlineCallable(entry.f, entry.timeout, entry.abortSignal !== undefined));
75
+ const initCap = 16;
76
+ let fnByIndex = new Int32Array(initCap);
77
+ let stateByIndex = new Int8Array(initCap).fill(-1 /* SlotStateMacro.Free */);
78
+ let argsByIndex = new Array(initCap);
79
+ let taskIdByIndex = new Array(initCap).fill(-1);
80
+ let deferredByIndex = new Array(initCap);
81
+ const freeStack = new Array(initCap);
82
+ let freeTop = initCap;
83
+ for (let i = 0; i < initCap; i++)
84
+ freeStack[i] = initCap - 1 - i;
85
+ const pendingQueue = new RingQueue(initCap);
86
+ let working = 0;
87
+ let isInMacro = false;
88
+ let isInMicro = false;
89
+ const batchLimit = Number.isFinite(batchSize)
90
+ ? Math.max(1, Math.floor(batchSize ?? 1))
91
+ : Number.POSITIVE_INFINITY;
92
+ const channel = createRuntimeMessageChannel();
93
+ const port1 = channel.port1;
94
+ const port2 = channel.port2;
95
+ const post2 = (message) => port2.postMessage(message);
96
+ const hasPending = () => pendingQueue.isEmpty === false;
97
+ const queueMicro = typeof queueMicrotask === "function"
98
+ ? queueMicrotask
99
+ : (callback) => Promise.resolve().then(callback);
100
+ const scheduleMacro = () => {
101
+ if (working === 0 || isInMacro)
102
+ return;
103
+ isInMacro = true;
104
+ post2(null);
105
+ };
106
+ const send = () => {
107
+ if (working === 0 || isInMacro || isInMicro)
108
+ return;
109
+ isInMicro = true;
110
+ queueMicro(runMicroLoop);
111
+ };
112
+ const enqueue = (index) => {
113
+ pendingQueue.push(index);
114
+ send();
115
+ };
116
+ const enqueueIfCurrent = (index, taskID) => {
117
+ if (stateByIndex[index] !== 0 /* SlotStateMacro.Pending */ ||
118
+ taskIdByIndex[index] !== taskID)
119
+ return;
120
+ enqueue(index);
121
+ };
122
+ const settleIfCurrent = (index, taskID, isError, value) => {
123
+ if (stateByIndex[index] !== 0 /* SlotStateMacro.Pending */ ||
124
+ taskIdByIndex[index] !== taskID)
125
+ return;
126
+ const deferred = deferredByIndex[index];
127
+ if (deferred) {
128
+ if (isError)
129
+ deferred.reject(value);
130
+ else
131
+ deferred.resolve(value);
132
+ }
133
+ cleanup(index);
134
+ };
135
+ function allocIndex() {
136
+ if (freeTop > 0)
137
+ return freeStack[--freeTop];
138
+ const oldCap = fnByIndex.length;
139
+ const newCap = oldCap << 1;
140
+ const nextFnByIndex = new Int32Array(newCap);
141
+ nextFnByIndex.set(fnByIndex);
142
+ fnByIndex = nextFnByIndex;
143
+ const nextStateByIndex = new Int8Array(newCap);
144
+ nextStateByIndex.fill(-1 /* SlotStateMacro.Free */);
145
+ nextStateByIndex.set(stateByIndex);
146
+ stateByIndex = nextStateByIndex;
147
+ argsByIndex.length = newCap;
148
+ taskIdByIndex.length = newCap;
149
+ taskIdByIndex.fill(-1, oldCap);
150
+ deferredByIndex.length = newCap;
151
+ for (let i = newCap - 1; i >= oldCap; --i) {
152
+ freeStack[freeTop++] = i;
153
+ }
154
+ return freeStack[--freeTop];
155
+ }
156
+ function processLoop(fromMicro = false) {
157
+ let processed = 0;
158
+ while (processed < batchLimit) {
159
+ const maybeIndex = pendingQueue.shiftNoClear();
160
+ if (maybeIndex === undefined)
161
+ break;
162
+ const index = maybeIndex | 0;
163
+ if (stateByIndex[index] !== 0 /* SlotStateMacro.Pending */)
164
+ continue;
165
+ const taskID = taskIdByIndex[index];
166
+ try {
167
+ const args = argsByIndex[index];
168
+ const fnId = fnByIndex[index];
169
+ const res = runners[fnId](args);
170
+ if (!(res instanceof Promise)) {
171
+ settleIfCurrent(index, taskID, false, res);
172
+ processed++;
173
+ continue;
174
+ }
175
+ res.then((value) => settleIfCurrent(index, taskID, false, value), (err) => settleIfCurrent(index, taskID, true, err));
176
+ processed++;
177
+ }
178
+ catch (err) {
179
+ settleIfCurrent(index, taskID, true, err);
180
+ processed++;
181
+ }
182
+ }
183
+ if (hasPending()) {
184
+ if (fromMicro) {
185
+ scheduleMacro();
186
+ }
187
+ else {
188
+ post2(null);
189
+ }
190
+ return;
191
+ }
192
+ if (!fromMicro) {
193
+ isInMacro = false;
194
+ }
195
+ }
196
+ function runMicroLoop() {
197
+ if (!isInMicro)
198
+ return;
199
+ processLoop(true);
200
+ isInMicro = false;
201
+ }
202
+ function cleanup(index) {
203
+ working--;
204
+ stateByIndex[index] = -1 /* SlotStateMacro.Free */;
205
+ fnByIndex[index] = 0;
206
+ taskIdByIndex[index] = -1;
207
+ argsByIndex[index] = undefined;
208
+ deferredByIndex[index] = undefined;
209
+ freeStack[freeTop++] = index;
210
+ if (working === 0)
211
+ isInMacro = false;
212
+ }
213
+ const call = ({ fnNumber }) => (args) => {
214
+ const taskID = genTaskID();
215
+ const deferred = withResolvers();
216
+ const index = allocIndex();
217
+ taskIdByIndex[index] = taskID;
218
+ argsByIndex[index] = args;
219
+ fnByIndex[index] = fnNumber | 0;
220
+ deferredByIndex[index] = deferred;
221
+ stateByIndex[index] = 0 /* SlotStateMacro.Pending */;
222
+ working++;
223
+ if (args instanceof Promise) {
224
+ args.then((value) => {
225
+ if (taskIdByIndex[index] !== taskID)
226
+ return;
227
+ argsByIndex[index] = value;
228
+ enqueueIfCurrent(index, taskID);
229
+ }, (err) => settleIfCurrent(index, taskID, true, err));
230
+ }
231
+ else {
232
+ enqueue(index);
233
+ }
234
+ return deferred.promise;
235
+ };
236
+ //@ts-ignore
237
+ port1.onmessage = () => processLoop(false);
238
+ return {
239
+ kills: async () => {
240
+ for (let index = 0; index < stateByIndex.length; index++) {
241
+ if (stateByIndex[index] !== 0 /* SlotStateMacro.Pending */)
242
+ continue;
243
+ try {
244
+ deferredByIndex[index]?.reject("Thread closed");
245
+ }
246
+ catch {
247
+ }
248
+ }
249
+ //@ts-ignore
250
+ port1.onmessage = null;
251
+ port1.close?.();
252
+ //@ts-ignore
253
+ port2.onmessage = null;
254
+ port2.close?.();
255
+ pendingQueue.clear();
256
+ freeTop = 0;
257
+ freeStack.length = 0;
258
+ argsByIndex.fill(undefined);
259
+ taskIdByIndex.fill(-1);
260
+ deferredByIndex.fill(undefined);
261
+ fnByIndex.fill(0);
262
+ stateByIndex.fill(-1 /* SlotStateMacro.Free */);
263
+ working = 0;
264
+ isInMacro = false;
265
+ isInMicro = false;
266
+ },
267
+ call,
268
+ txIdle: () => working === 0,
269
+ };
270
+ };
@@ -0,0 +1,43 @@
1
+ import { type Sab } from "../ipc/transport/shared-memory.js";
2
+ import { lock2 } from "../memory/lock.js";
3
+ import type { DebugOptions, DispatcherSettings, WorkerContext, WorkerData, WorkerSettings } from "../types.js";
4
+ import "../worker/loop.js";
5
+ import { type PayloadBufferOptions } from "../memory/payload-config.js";
6
+ type ProcessSharedMemoryNativeMapping = {
7
+ sab: SharedArrayBuffer;
8
+ fd: number;
9
+ size: number;
10
+ baseAddressMod64?: number;
11
+ };
12
+ type ProcessSharedMemoryBacking = ProcessSharedMemoryNativeMapping & {
13
+ runtime: "node";
14
+ buffer: SharedArrayBuffer;
15
+ kind: "shared-array-buffer";
16
+ byteLength: number;
17
+ };
18
+ export declare const spawnWorkerContext: ({ list, ids, sab, thread, debug, totalNumberOfThread, source, at, workerOptions, workerExecArgv, permission, host, payload, payloadInitialBytes, payloadMaxBytes, bufferMode, maxPayloadBytes, abortSignalCapacity, usesAbortSignal, }: {
19
+ list: string[];
20
+ ids: number[];
21
+ at: number[];
22
+ sab?: Sab;
23
+ thread: number;
24
+ debug?: DebugOptions;
25
+ totalNumberOfThread: number;
26
+ source?: string;
27
+ workerOptions?: WorkerSettings;
28
+ workerExecArgv?: string[];
29
+ permission?: WorkerData["permission"];
30
+ host?: DispatcherSettings;
31
+ payload?: PayloadBufferOptions;
32
+ payloadInitialBytes?: number;
33
+ payloadMaxBytes?: number;
34
+ bufferMode?: PayloadBufferOptions["mode"];
35
+ maxPayloadBytes?: number;
36
+ abortSignalCapacity?: number;
37
+ usesAbortSignal?: boolean;
38
+ }) => WorkerContext & {
39
+ lock: ReturnType<typeof lock2>;
40
+ processSharedMemoryBackings?: readonly ProcessSharedMemoryBacking[];
41
+ };
42
+ export type CreateContext = WorkerContext;
43
+ export {};