@dmop/puru 0.1.13 → 0.1.14
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/dist/index.cjs +142 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +142 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -56,6 +56,10 @@ var NATIVE_CODE_RE = /\[native code\]/;
|
|
|
56
56
|
var METHOD_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/;
|
|
57
57
|
var VALID_FN_START_RE = /^(?:function\b|async\s+function\b|async\s*\(|\(|[a-zA-Z_$][a-zA-Z0-9_$]*\s*=>|async\s+[a-zA-Z_$])/;
|
|
58
58
|
var serializeCache = /* @__PURE__ */ new WeakMap();
|
|
59
|
+
var functionRefCache = /* @__PURE__ */ new WeakMap();
|
|
60
|
+
var functionIdCache = /* @__PURE__ */ new Map();
|
|
61
|
+
var FUNCTION_ID_CACHE_MAX = 512;
|
|
62
|
+
var functionIdCounter = 0;
|
|
59
63
|
function serializeFunction(fn) {
|
|
60
64
|
if (typeof fn !== "function") {
|
|
61
65
|
throw new TypeError("Expected a function");
|
|
@@ -86,6 +90,25 @@ function serializeFunction(fn) {
|
|
|
86
90
|
serializeCache.set(fn, str);
|
|
87
91
|
return str;
|
|
88
92
|
}
|
|
93
|
+
function getSerializedFunctionRef(fn) {
|
|
94
|
+
const cached = functionRefCache.get(fn);
|
|
95
|
+
if (cached) return cached;
|
|
96
|
+
const fnStr = serializeFunction(fn);
|
|
97
|
+
let fnId = functionIdCache.get(fnStr);
|
|
98
|
+
if (fnId) {
|
|
99
|
+
functionIdCache.delete(fnStr);
|
|
100
|
+
} else {
|
|
101
|
+
fnId = `fn_${++functionIdCounter}`;
|
|
102
|
+
}
|
|
103
|
+
functionIdCache.set(fnStr, fnId);
|
|
104
|
+
if (functionIdCache.size > FUNCTION_ID_CACHE_MAX) {
|
|
105
|
+
const oldestFnStr = functionIdCache.keys().next().value;
|
|
106
|
+
if (oldestFnStr !== void 0) functionIdCache.delete(oldestFnStr);
|
|
107
|
+
}
|
|
108
|
+
const ref = { fnId, fnStr };
|
|
109
|
+
functionRefCache.set(fn, ref);
|
|
110
|
+
return ref;
|
|
111
|
+
}
|
|
89
112
|
|
|
90
113
|
// src/configure.ts
|
|
91
114
|
var import_node_os = require("os");
|
|
@@ -207,12 +230,15 @@ function __buildChannelProxies(channels) {
|
|
|
207
230
|
const __fnCache = new Map();
|
|
208
231
|
const __FN_CACHE_MAX = 1000;
|
|
209
232
|
|
|
210
|
-
function __execFn(fnStr, channels, args) {
|
|
211
|
-
let parsedFn = __fnCache.get(
|
|
233
|
+
function __execFn(fnId, fnStr, channels, args) {
|
|
234
|
+
let parsedFn = __fnCache.get(fnId);
|
|
212
235
|
if (!parsedFn) {
|
|
236
|
+
if (!fnStr) {
|
|
237
|
+
throw new Error('Worker function was not registered on this worker');
|
|
238
|
+
}
|
|
213
239
|
parsedFn = (new Function('return (' + fnStr + ')'))();
|
|
214
240
|
if (__fnCache.size >= __FN_CACHE_MAX) __fnCache.delete(__fnCache.keys().next().value);
|
|
215
|
-
__fnCache.set(
|
|
241
|
+
__fnCache.set(fnId, parsedFn);
|
|
216
242
|
}
|
|
217
243
|
if (args) {
|
|
218
244
|
return parsedFn(...args);
|
|
@@ -246,7 +272,7 @@ parentPort.on('message', async (msg) => {
|
|
|
246
272
|
return;
|
|
247
273
|
}
|
|
248
274
|
try {
|
|
249
|
-
const result = await __execFn(msg.fnStr, msg.channels, msg.args);
|
|
275
|
+
const result = await __execFn(msg.fnId, msg.fnStr, msg.channels, msg.args);
|
|
250
276
|
if (!cancelledTasks.has(msg.taskId)) {
|
|
251
277
|
parentPort.postMessage({ type: 'result', taskId: msg.taskId, value: result });
|
|
252
278
|
}
|
|
@@ -265,7 +291,7 @@ parentPort.on('message', async (msg) => {
|
|
|
265
291
|
})();
|
|
266
292
|
} else {
|
|
267
293
|
try {
|
|
268
|
-
const result = await __execFn(msg.fnStr, msg.channels, msg.args);
|
|
294
|
+
const result = await __execFn(msg.fnId, msg.fnStr, msg.channels, msg.args);
|
|
269
295
|
parentPort.postMessage({ type: 'result', taskId: msg.taskId, value: result });
|
|
270
296
|
} catch (error) {
|
|
271
297
|
parentPort.postMessage({
|
|
@@ -306,7 +332,7 @@ self.onmessage = async (event) => {
|
|
|
306
332
|
return;
|
|
307
333
|
}
|
|
308
334
|
try {
|
|
309
|
-
const result = await __execFn(msg.fnStr, msg.channels, msg.args);
|
|
335
|
+
const result = await __execFn(msg.fnId, msg.fnStr, msg.channels, msg.args);
|
|
310
336
|
if (!cancelledTasks.has(msg.taskId)) {
|
|
311
337
|
self.postMessage({ type: 'result', taskId: msg.taskId, value: result });
|
|
312
338
|
}
|
|
@@ -325,7 +351,7 @@ self.onmessage = async (event) => {
|
|
|
325
351
|
})();
|
|
326
352
|
} else {
|
|
327
353
|
try {
|
|
328
|
-
const result = await __execFn(msg.fnStr, msg.channels, msg.args);
|
|
354
|
+
const result = await __execFn(msg.fnId, msg.fnStr, msg.channels, msg.args);
|
|
329
355
|
self.postMessage({ type: 'result', taskId: msg.taskId, value: result });
|
|
330
356
|
} catch (error) {
|
|
331
357
|
self.postMessage({
|
|
@@ -441,8 +467,80 @@ var BunWorkerAdapter = class {
|
|
|
441
467
|
}
|
|
442
468
|
};
|
|
443
469
|
|
|
470
|
+
// src/queue.ts
|
|
471
|
+
var RingBuffer = class {
|
|
472
|
+
items;
|
|
473
|
+
head = 0;
|
|
474
|
+
tail = 0;
|
|
475
|
+
size = 0;
|
|
476
|
+
cap;
|
|
477
|
+
constructor(capacity) {
|
|
478
|
+
this.cap = capacity;
|
|
479
|
+
this.items = new Array(capacity);
|
|
480
|
+
}
|
|
481
|
+
get length() {
|
|
482
|
+
return this.size;
|
|
483
|
+
}
|
|
484
|
+
push(value) {
|
|
485
|
+
this.items[this.tail] = value;
|
|
486
|
+
this.tail = (this.tail + 1) % this.cap;
|
|
487
|
+
this.size++;
|
|
488
|
+
}
|
|
489
|
+
shift() {
|
|
490
|
+
if (this.size === 0) return void 0;
|
|
491
|
+
const value = this.items[this.head];
|
|
492
|
+
this.items[this.head] = void 0;
|
|
493
|
+
this.head = (this.head + 1) % this.cap;
|
|
494
|
+
this.size--;
|
|
495
|
+
return value;
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
var FifoQueue = class {
|
|
499
|
+
head = null;
|
|
500
|
+
tail = null;
|
|
501
|
+
size = 0;
|
|
502
|
+
get length() {
|
|
503
|
+
return this.size;
|
|
504
|
+
}
|
|
505
|
+
push(value) {
|
|
506
|
+
const node = { value, next: null };
|
|
507
|
+
if (this.tail) {
|
|
508
|
+
this.tail.next = node;
|
|
509
|
+
} else {
|
|
510
|
+
this.head = node;
|
|
511
|
+
}
|
|
512
|
+
this.tail = node;
|
|
513
|
+
this.size++;
|
|
514
|
+
}
|
|
515
|
+
shift() {
|
|
516
|
+
if (!this.head) return void 0;
|
|
517
|
+
const node = this.head;
|
|
518
|
+
this.head = node.next;
|
|
519
|
+
if (!this.head) this.tail = null;
|
|
520
|
+
this.size--;
|
|
521
|
+
const value = node.value;
|
|
522
|
+
node.value = void 0;
|
|
523
|
+
node.next = null;
|
|
524
|
+
return value;
|
|
525
|
+
}
|
|
526
|
+
clear() {
|
|
527
|
+
this.head = null;
|
|
528
|
+
this.tail = null;
|
|
529
|
+
this.size = 0;
|
|
530
|
+
}
|
|
531
|
+
*[Symbol.iterator]() {
|
|
532
|
+
let current = this.head;
|
|
533
|
+
while (current) {
|
|
534
|
+
yield current.value;
|
|
535
|
+
current = current.next;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
|
|
444
540
|
// src/channel.ts
|
|
445
541
|
var CLOSED = /* @__PURE__ */ Symbol("puru.channel.closed");
|
|
542
|
+
var RESOLVED_VOID = Promise.resolve();
|
|
543
|
+
var RESOLVED_NULL = Promise.resolve(null);
|
|
446
544
|
var channelIdCounter = 0;
|
|
447
545
|
var channelRegistry = /* @__PURE__ */ new Map();
|
|
448
546
|
var ChannelImpl = class {
|
|
@@ -451,14 +549,15 @@ var ChannelImpl = class {
|
|
|
451
549
|
_id;
|
|
452
550
|
/** @internal — true once the channel ID has been sent to a worker */
|
|
453
551
|
_shared = false;
|
|
454
|
-
buffer
|
|
552
|
+
buffer;
|
|
455
553
|
capacity;
|
|
456
554
|
closed = false;
|
|
457
|
-
recvQueue =
|
|
458
|
-
sendQueue =
|
|
555
|
+
recvQueue = new FifoQueue();
|
|
556
|
+
sendQueue = new FifoQueue();
|
|
459
557
|
constructor(capacity) {
|
|
460
558
|
this._id = `__ch_${++channelIdCounter}`;
|
|
461
559
|
this.capacity = capacity;
|
|
560
|
+
this.buffer = new RingBuffer(capacity);
|
|
462
561
|
channelRegistry.set(this._id, this);
|
|
463
562
|
}
|
|
464
563
|
get len() {
|
|
@@ -474,11 +573,11 @@ var ChannelImpl = class {
|
|
|
474
573
|
const receiver = this.recvQueue.shift();
|
|
475
574
|
if (receiver) {
|
|
476
575
|
receiver.resolve(value);
|
|
477
|
-
return
|
|
576
|
+
return RESOLVED_VOID;
|
|
478
577
|
}
|
|
479
578
|
if (this.buffer.length < this.capacity) {
|
|
480
579
|
this.buffer.push(value);
|
|
481
|
-
return
|
|
580
|
+
return RESOLVED_VOID;
|
|
482
581
|
}
|
|
483
582
|
return new Promise((resolve, reject) => {
|
|
484
583
|
this.sendQueue.push({ value, resolve, reject });
|
|
@@ -502,7 +601,7 @@ var ChannelImpl = class {
|
|
|
502
601
|
}
|
|
503
602
|
if (this.closed) {
|
|
504
603
|
this.maybeUnregister();
|
|
505
|
-
return
|
|
604
|
+
return RESOLVED_NULL;
|
|
506
605
|
}
|
|
507
606
|
return new Promise((resolve) => {
|
|
508
607
|
this.recvQueue.push({
|
|
@@ -516,11 +615,11 @@ var ChannelImpl = class {
|
|
|
516
615
|
for (const receiver of this.recvQueue) {
|
|
517
616
|
receiver.resolve(CLOSED);
|
|
518
617
|
}
|
|
519
|
-
this.recvQueue
|
|
618
|
+
this.recvQueue.clear();
|
|
520
619
|
for (const sender of this.sendQueue) {
|
|
521
620
|
sender.reject(new Error("send on closed channel"));
|
|
522
621
|
}
|
|
523
|
-
this.sendQueue
|
|
622
|
+
this.sendQueue.clear();
|
|
524
623
|
}
|
|
525
624
|
maybeUnregister() {
|
|
526
625
|
if (!this._shared && this.closed && this.buffer.length === 0 && this.recvQueue.length === 0) {
|
|
@@ -603,7 +702,7 @@ var InlineManagedWorker = class {
|
|
|
603
702
|
postMessage(msg) {
|
|
604
703
|
if (this.terminated) return;
|
|
605
704
|
if (msg.type === "execute") {
|
|
606
|
-
this.executeTask(msg.taskId, msg.fnStr, msg.concurrent, msg.channels, msg.args);
|
|
705
|
+
this.executeTask(msg.taskId, msg.fnId, msg.fnStr, msg.concurrent, msg.channels, msg.args);
|
|
607
706
|
} else if (msg.type === "cancel") {
|
|
608
707
|
this.cancelledTasks.add(msg.taskId);
|
|
609
708
|
} else if (msg.type === "channel-result") {
|
|
@@ -664,7 +763,7 @@ var InlineManagedWorker = class {
|
|
|
664
763
|
}
|
|
665
764
|
return proxies;
|
|
666
765
|
}
|
|
667
|
-
executeTask(taskId, fnStr, concurrent, channels, args) {
|
|
766
|
+
executeTask(taskId, fnId, fnStr, concurrent, channels, args) {
|
|
668
767
|
queueMicrotask(async () => {
|
|
669
768
|
if (this.terminated) return;
|
|
670
769
|
if (concurrent && this.cancelledTasks.has(taskId)) {
|
|
@@ -672,11 +771,14 @@ var InlineManagedWorker = class {
|
|
|
672
771
|
return;
|
|
673
772
|
}
|
|
674
773
|
try {
|
|
675
|
-
let parsedFn = this.fnCache.get(
|
|
774
|
+
let parsedFn = this.fnCache.get(fnId);
|
|
676
775
|
if (!parsedFn) {
|
|
776
|
+
if (!fnStr) {
|
|
777
|
+
throw new Error("Worker function was not registered on this worker");
|
|
778
|
+
}
|
|
677
779
|
parsedFn = new Function("return (" + fnStr + ")")();
|
|
678
780
|
if (this.fnCache.size >= 1e3) this.fnCache.clear();
|
|
679
|
-
this.fnCache.set(
|
|
781
|
+
this.fnCache.set(fnId, parsedFn);
|
|
680
782
|
}
|
|
681
783
|
let result;
|
|
682
784
|
if (args) {
|
|
@@ -738,6 +840,7 @@ var WorkerPool = class {
|
|
|
738
840
|
totalCompleted = 0;
|
|
739
841
|
totalFailed = 0;
|
|
740
842
|
taskMap = /* @__PURE__ */ new Map();
|
|
843
|
+
workerFunctionIds = /* @__PURE__ */ new Map();
|
|
741
844
|
// Per-worker deques for work-stealing strategy
|
|
742
845
|
workerDeques = /* @__PURE__ */ new Map();
|
|
743
846
|
constructor(config, adapter) {
|
|
@@ -949,7 +1052,8 @@ var WorkerPool = class {
|
|
|
949
1052
|
const msg = {
|
|
950
1053
|
type: "execute",
|
|
951
1054
|
taskId: task2.id,
|
|
952
|
-
|
|
1055
|
+
fnId: task2.fnId,
|
|
1056
|
+
fnStr: this.getWorkerFunctionString(worker, task2),
|
|
953
1057
|
args: task2.args,
|
|
954
1058
|
concurrent: false,
|
|
955
1059
|
channels: task2.channels
|
|
@@ -971,13 +1075,24 @@ var WorkerPool = class {
|
|
|
971
1075
|
const msg = {
|
|
972
1076
|
type: "execute",
|
|
973
1077
|
taskId: task2.id,
|
|
974
|
-
|
|
1078
|
+
fnId: task2.fnId,
|
|
1079
|
+
fnStr: this.getWorkerFunctionString(worker, task2),
|
|
975
1080
|
args: task2.args,
|
|
976
1081
|
concurrent: true,
|
|
977
1082
|
channels: task2.channels
|
|
978
1083
|
};
|
|
979
1084
|
worker.postMessage(msg);
|
|
980
1085
|
}
|
|
1086
|
+
getWorkerFunctionString(worker, task2) {
|
|
1087
|
+
let knownFunctions = this.workerFunctionIds.get(worker);
|
|
1088
|
+
if (!knownFunctions) {
|
|
1089
|
+
knownFunctions = /* @__PURE__ */ new Set();
|
|
1090
|
+
this.workerFunctionIds.set(worker, knownFunctions);
|
|
1091
|
+
}
|
|
1092
|
+
if (knownFunctions.has(task2.fnId)) return void 0;
|
|
1093
|
+
knownFunctions.add(task2.fnId);
|
|
1094
|
+
return task2.fnStr;
|
|
1095
|
+
}
|
|
981
1096
|
// --- Task completion ---
|
|
982
1097
|
handleWorkerMessage(worker, response) {
|
|
983
1098
|
if (response.type === "channel-op") {
|
|
@@ -1242,6 +1357,7 @@ var WorkerPool = class {
|
|
|
1242
1357
|
this.sharedWorkers.clear();
|
|
1243
1358
|
this.allWorkers.clear();
|
|
1244
1359
|
this.idleTimers.clear();
|
|
1360
|
+
this.workerFunctionIds.clear();
|
|
1245
1361
|
}
|
|
1246
1362
|
resize(maxThreads) {
|
|
1247
1363
|
this.config = { ...this.config, maxThreads };
|
|
@@ -1301,6 +1417,7 @@ var WorkerPool = class {
|
|
|
1301
1417
|
});
|
|
1302
1418
|
worker.on("exit", (_code) => {
|
|
1303
1419
|
this.allWorkers.delete(worker);
|
|
1420
|
+
this.workerFunctionIds.delete(worker);
|
|
1304
1421
|
const timer = this.idleTimers.get(worker);
|
|
1305
1422
|
if (timer) {
|
|
1306
1423
|
clearTimeout(timer);
|
|
@@ -1402,7 +1519,7 @@ async function shutdown() {
|
|
|
1402
1519
|
// src/spawn.ts
|
|
1403
1520
|
var taskCounter = 0;
|
|
1404
1521
|
function spawn(fn, opts) {
|
|
1405
|
-
const fnStr =
|
|
1522
|
+
const { fnId, fnStr } = getSerializedFunctionRef(fn);
|
|
1406
1523
|
const taskId = String(++taskCounter);
|
|
1407
1524
|
const spawnError = new Error();
|
|
1408
1525
|
let resolveFn;
|
|
@@ -1421,6 +1538,7 @@ function spawn(fn, opts) {
|
|
|
1421
1538
|
}
|
|
1422
1539
|
const task2 = {
|
|
1423
1540
|
id: taskId,
|
|
1541
|
+
fnId,
|
|
1424
1542
|
fnStr,
|
|
1425
1543
|
priority: opts?.priority ?? "normal",
|
|
1426
1544
|
concurrent: opts?.concurrent ?? false,
|
|
@@ -2070,7 +2188,7 @@ var Timer = class {
|
|
|
2070
2188
|
// src/registry.ts
|
|
2071
2189
|
var taskCounter2 = 0;
|
|
2072
2190
|
function task(fn) {
|
|
2073
|
-
const fnStr =
|
|
2191
|
+
const { fnId, fnStr } = getSerializedFunctionRef(fn);
|
|
2074
2192
|
return (...args) => {
|
|
2075
2193
|
for (const a of args) {
|
|
2076
2194
|
if (JSON.stringify(a) === void 0) {
|
|
@@ -2089,6 +2207,7 @@ function task(fn) {
|
|
|
2089
2207
|
});
|
|
2090
2208
|
const taskObj = {
|
|
2091
2209
|
id: taskId,
|
|
2210
|
+
fnId,
|
|
2092
2211
|
fnStr,
|
|
2093
2212
|
args,
|
|
2094
2213
|
priority: "normal",
|