@xylabs/threads 4.7.4 → 4.7.5
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/browser/index-browser.mjs +1022 -0
- package/dist/browser/index-browser.mjs.map +1 -0
- package/dist/node/index-node.mjs +1022 -0
- package/dist/node/index-node.mjs.map +1 -0
- package/dist/types/index-browser.d.ts +9 -0
- package/dist/types/index-browser.d.ts.map +1 -0
- package/dist/types/{index.d.ts → index-node.d.ts} +2 -1
- package/dist/types/index-node.d.ts.map +1 -0
- package/package.json +11 -5
- package/dist/neutral/index.mjs +0 -75
- package/dist/neutral/index.mjs.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
|
@@ -0,0 +1,1022 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/serializers.ts
|
|
14
|
+
function extendSerializer(extend, implementation4) {
|
|
15
|
+
const fallbackDeserializer = extend.deserialize.bind(extend);
|
|
16
|
+
const fallbackSerializer = extend.serialize.bind(extend);
|
|
17
|
+
return {
|
|
18
|
+
deserialize(message) {
|
|
19
|
+
return implementation4.deserialize(message, fallbackDeserializer);
|
|
20
|
+
},
|
|
21
|
+
serialize(input) {
|
|
22
|
+
return implementation4.serialize(input, fallbackSerializer);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
var DefaultErrorSerializer = {
|
|
27
|
+
deserialize(message) {
|
|
28
|
+
return Object.assign(new Error(message.message), {
|
|
29
|
+
name: message.name,
|
|
30
|
+
stack: message.stack
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
serialize(error) {
|
|
34
|
+
return {
|
|
35
|
+
__error_marker: "$$error",
|
|
36
|
+
message: error.message,
|
|
37
|
+
name: error.name,
|
|
38
|
+
stack: error.stack
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var isSerializedError = (thing) => thing && typeof thing === "object" && "__error_marker" in thing && thing.__error_marker === "$$error";
|
|
43
|
+
var DefaultSerializer = {
|
|
44
|
+
deserialize(message) {
|
|
45
|
+
return isSerializedError(message) ? DefaultErrorSerializer.deserialize(message) : message;
|
|
46
|
+
},
|
|
47
|
+
serialize(input) {
|
|
48
|
+
return input instanceof Error ? DefaultErrorSerializer.serialize(input) : input;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// src/common.ts
|
|
53
|
+
globalThis.registeredSerializer = globalThis.registeredSerializer ?? DefaultSerializer;
|
|
54
|
+
function registerSerializer(serializer) {
|
|
55
|
+
globalThis.registeredSerializer = extendSerializer(globalThis.registeredSerializer, serializer);
|
|
56
|
+
}
|
|
57
|
+
function deserialize(message) {
|
|
58
|
+
return globalThis.registeredSerializer.deserialize(message);
|
|
59
|
+
}
|
|
60
|
+
function serialize(input) {
|
|
61
|
+
return globalThis.registeredSerializer.serialize(input);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/master/implementation.browser.ts
|
|
65
|
+
var implementation_browser_exports = {};
|
|
66
|
+
__export(implementation_browser_exports, {
|
|
67
|
+
defaultPoolSize: () => defaultPoolSize,
|
|
68
|
+
getWorkerImplementation: () => getWorkerImplementation,
|
|
69
|
+
isWorkerRuntime: () => isWorkerRuntime
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// src/master/get-bundle-url.browser.ts
|
|
73
|
+
var bundleURL;
|
|
74
|
+
function getBundleURLCached() {
|
|
75
|
+
if (!bundleURL) {
|
|
76
|
+
bundleURL = getBundleURL();
|
|
77
|
+
}
|
|
78
|
+
return bundleURL;
|
|
79
|
+
}
|
|
80
|
+
function getBundleURL() {
|
|
81
|
+
try {
|
|
82
|
+
throw new Error("getBundleURL failed");
|
|
83
|
+
} catch (ex) {
|
|
84
|
+
const err = ex;
|
|
85
|
+
const matches = ("" + err.stack).match(/(https?|file|ftp|chrome-extension|moz-extension):\/\/[^\n)]+/g);
|
|
86
|
+
if (matches) {
|
|
87
|
+
return getBaseURL(matches[0]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return "/";
|
|
91
|
+
}
|
|
92
|
+
function getBaseURL(url) {
|
|
93
|
+
return ("" + url).replace(/^((?:https?|file|ftp|chrome-extension|moz-extension):\/\/.+)?\/[^/]+(?:\?.*)?$/, "$1") + "/";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/master/implementation.browser.ts
|
|
97
|
+
var defaultPoolSize = typeof navigator !== "undefined" && navigator.hardwareConcurrency ? navigator.hardwareConcurrency : 4;
|
|
98
|
+
var isAbsoluteURL = (value) => /^[A-Za-z][\d+.A-Za-z\-]*:/.test(value);
|
|
99
|
+
function createSourceBlobURL(code) {
|
|
100
|
+
const blob = new Blob([code], { type: "application/javascript" });
|
|
101
|
+
return URL.createObjectURL(blob);
|
|
102
|
+
}
|
|
103
|
+
function selectWorkerImplementation() {
|
|
104
|
+
if (typeof Worker === "undefined") {
|
|
105
|
+
return class NoWebWorker {
|
|
106
|
+
constructor() {
|
|
107
|
+
throw new Error(
|
|
108
|
+
"No web worker implementation available. You might have tried to spawn a worker within a worker in a browser that doesn't support workers in workers."
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
class WebWorker extends Worker {
|
|
114
|
+
constructor(url, options) {
|
|
115
|
+
if (typeof url === "string" && options && options._baseURL) {
|
|
116
|
+
url = new URL(url, options._baseURL);
|
|
117
|
+
} else if (typeof url === "string" && !isAbsoluteURL(url) && /^file:\/\//i.test(getBundleURLCached())) {
|
|
118
|
+
url = new URL(url, getBundleURLCached().replace(/\/[^/]+$/, "/"));
|
|
119
|
+
if (options?.CORSWorkaround ?? true) {
|
|
120
|
+
url = createSourceBlobURL(`importScripts(${JSON.stringify(url)});`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (typeof url === "string" && isAbsoluteURL(url) && (options?.CORSWorkaround ?? true)) {
|
|
124
|
+
url = createSourceBlobURL(`importScripts(${JSON.stringify(url)});`);
|
|
125
|
+
}
|
|
126
|
+
super(url, options);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
class BlobWorker2 extends WebWorker {
|
|
130
|
+
constructor(blob, options) {
|
|
131
|
+
const url = globalThis.URL.createObjectURL(blob);
|
|
132
|
+
super(url, options);
|
|
133
|
+
}
|
|
134
|
+
static fromText(source, options) {
|
|
135
|
+
const blob = new globalThis.Blob([source], { type: "text/javascript" });
|
|
136
|
+
return new BlobWorker2(blob, options);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
blob: BlobWorker2,
|
|
141
|
+
default: WebWorker
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
var implementation;
|
|
145
|
+
function getWorkerImplementation() {
|
|
146
|
+
if (!implementation) {
|
|
147
|
+
implementation = selectWorkerImplementation();
|
|
148
|
+
}
|
|
149
|
+
return implementation;
|
|
150
|
+
}
|
|
151
|
+
function isWorkerRuntime() {
|
|
152
|
+
const isWindowContext = typeof globalThis !== "undefined" && typeof Window !== "undefined" && globalThis instanceof Window;
|
|
153
|
+
return typeof globalThis !== "undefined" && self["postMessage"] && !isWindowContext ? true : false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/master/pool.ts
|
|
157
|
+
import DebugLogger from "debug";
|
|
158
|
+
import {
|
|
159
|
+
multicast,
|
|
160
|
+
Observable,
|
|
161
|
+
Subject
|
|
162
|
+
} from "observable-fns";
|
|
163
|
+
|
|
164
|
+
// src/ponyfills.ts
|
|
165
|
+
function allSettled(values) {
|
|
166
|
+
return Promise.all(
|
|
167
|
+
values.map((item) => {
|
|
168
|
+
const onFulfill = (value) => {
|
|
169
|
+
return { status: "fulfilled", value };
|
|
170
|
+
};
|
|
171
|
+
const onReject = (reason) => {
|
|
172
|
+
return { reason, status: "rejected" };
|
|
173
|
+
};
|
|
174
|
+
const itemPromise = Promise.resolve(item);
|
|
175
|
+
try {
|
|
176
|
+
return itemPromise.then(onFulfill, onReject);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
return Promise.reject(error);
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/master/implementation.node.ts
|
|
185
|
+
var implementation_node_exports = {};
|
|
186
|
+
__export(implementation_node_exports, {
|
|
187
|
+
defaultPoolSize: () => defaultPoolSize2,
|
|
188
|
+
getWorkerImplementation: () => getWorkerImplementation2,
|
|
189
|
+
isWorkerRuntime: () => isWorkerRuntime2
|
|
190
|
+
});
|
|
191
|
+
import { EventEmitter } from "node:events";
|
|
192
|
+
import { cpus } from "node:os";
|
|
193
|
+
import path from "node:path";
|
|
194
|
+
import { cwd } from "node:process";
|
|
195
|
+
import { Worker as NativeWorker } from "node:worker_threads";
|
|
196
|
+
var defaultPoolSize2 = cpus().length;
|
|
197
|
+
function resolveScriptPath(scriptPath, baseURL) {
|
|
198
|
+
const makeAbsolute = (filePath) => {
|
|
199
|
+
return path.isAbsolute(filePath) ? filePath : path.join(baseURL ?? cwd(), filePath);
|
|
200
|
+
};
|
|
201
|
+
const absolutePath = makeAbsolute(scriptPath);
|
|
202
|
+
return absolutePath;
|
|
203
|
+
}
|
|
204
|
+
function initWorkerThreadsWorker() {
|
|
205
|
+
let allWorkers = [];
|
|
206
|
+
class Worker3 extends NativeWorker {
|
|
207
|
+
mappedEventListeners;
|
|
208
|
+
constructor(scriptPath, options) {
|
|
209
|
+
const resolvedScriptPath = options && options.fromSource ? null : resolveScriptPath(scriptPath, (options ?? {})._baseURL);
|
|
210
|
+
if (resolvedScriptPath) {
|
|
211
|
+
super(resolvedScriptPath, options);
|
|
212
|
+
} else {
|
|
213
|
+
const sourceCode = scriptPath;
|
|
214
|
+
super(sourceCode, { ...options, eval: true });
|
|
215
|
+
}
|
|
216
|
+
this.mappedEventListeners = /* @__PURE__ */ new WeakMap();
|
|
217
|
+
allWorkers.push(this);
|
|
218
|
+
}
|
|
219
|
+
addEventListener(eventName, rawListener) {
|
|
220
|
+
const listener = (message) => {
|
|
221
|
+
rawListener({ data: message });
|
|
222
|
+
};
|
|
223
|
+
this.mappedEventListeners.set(rawListener, listener);
|
|
224
|
+
this.on(eventName, listener);
|
|
225
|
+
}
|
|
226
|
+
removeEventListener(eventName, rawListener) {
|
|
227
|
+
const listener = this.mappedEventListeners.get(rawListener) || rawListener;
|
|
228
|
+
this.off(eventName, listener);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const terminateWorkersAndMaster = () => {
|
|
232
|
+
Promise.all(allWorkers.map((worker) => worker.terminate())).then(
|
|
233
|
+
() => process.exit(0),
|
|
234
|
+
() => process.exit(1)
|
|
235
|
+
);
|
|
236
|
+
allWorkers = [];
|
|
237
|
+
};
|
|
238
|
+
process.on("SIGINT", () => terminateWorkersAndMaster());
|
|
239
|
+
process.on("SIGTERM", () => terminateWorkersAndMaster());
|
|
240
|
+
class BlobWorker2 extends Worker3 {
|
|
241
|
+
constructor(blob, options) {
|
|
242
|
+
super(Buffer.from(blob).toString("utf-8"), { ...options, fromSource: true });
|
|
243
|
+
}
|
|
244
|
+
static fromText(source, options) {
|
|
245
|
+
return new Worker3(source, { ...options, fromSource: true });
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
blob: BlobWorker2,
|
|
250
|
+
default: Worker3
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function initTinyWorker() {
|
|
254
|
+
const TinyWorker = __require("tiny-worker");
|
|
255
|
+
let allWorkers = [];
|
|
256
|
+
class Worker3 extends TinyWorker {
|
|
257
|
+
emitter;
|
|
258
|
+
constructor(scriptPath, options) {
|
|
259
|
+
const resolvedScriptPath = options && options.fromSource ? null : process.platform === "win32" ? `file:///${resolveScriptPath(scriptPath).replaceAll("\\", "/")}` : resolveScriptPath(scriptPath);
|
|
260
|
+
if (resolvedScriptPath) {
|
|
261
|
+
super(resolvedScriptPath, [], { esm: true });
|
|
262
|
+
} else {
|
|
263
|
+
const sourceCode = scriptPath;
|
|
264
|
+
super(new Function(sourceCode), [], { esm: true });
|
|
265
|
+
}
|
|
266
|
+
allWorkers.push(this);
|
|
267
|
+
this.emitter = new EventEmitter();
|
|
268
|
+
this.onerror = (error) => this.emitter.emit("error", error);
|
|
269
|
+
this.onmessage = (message) => this.emitter.emit("message", message);
|
|
270
|
+
}
|
|
271
|
+
addEventListener(eventName, listener) {
|
|
272
|
+
this.emitter.addListener(eventName, listener);
|
|
273
|
+
}
|
|
274
|
+
removeEventListener(eventName, listener) {
|
|
275
|
+
this.emitter.removeListener(eventName, listener);
|
|
276
|
+
}
|
|
277
|
+
terminate() {
|
|
278
|
+
allWorkers = allWorkers.filter((worker) => worker !== this);
|
|
279
|
+
return super.terminate();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const terminateWorkersAndMaster = () => {
|
|
283
|
+
Promise.all(allWorkers.map((worker) => worker.terminate())).then(
|
|
284
|
+
() => process.exit(0),
|
|
285
|
+
() => process.exit(1)
|
|
286
|
+
);
|
|
287
|
+
allWorkers = [];
|
|
288
|
+
};
|
|
289
|
+
process.on("SIGINT", () => terminateWorkersAndMaster());
|
|
290
|
+
process.on("SIGTERM", () => terminateWorkersAndMaster());
|
|
291
|
+
class BlobWorker2 extends Worker3 {
|
|
292
|
+
constructor(blob, options) {
|
|
293
|
+
super(Buffer.from(blob).toString("utf-8"), { ...options, fromSource: true });
|
|
294
|
+
}
|
|
295
|
+
static fromText(source, options) {
|
|
296
|
+
return new Worker3(source, { ...options, fromSource: true });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
blob: BlobWorker2,
|
|
301
|
+
default: Worker3
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
var implementation2;
|
|
305
|
+
var isTinyWorker;
|
|
306
|
+
function selectWorkerImplementation2() {
|
|
307
|
+
try {
|
|
308
|
+
isTinyWorker = false;
|
|
309
|
+
return initWorkerThreadsWorker();
|
|
310
|
+
} catch (ex) {
|
|
311
|
+
console.error(ex);
|
|
312
|
+
console.debug("Node worker_threads not available. Trying to fall back to tiny-worker polyfill...");
|
|
313
|
+
isTinyWorker = true;
|
|
314
|
+
return initTinyWorker();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function getWorkerImplementation2() {
|
|
318
|
+
if (!implementation2) {
|
|
319
|
+
implementation2 = selectWorkerImplementation2();
|
|
320
|
+
}
|
|
321
|
+
return implementation2;
|
|
322
|
+
}
|
|
323
|
+
function isWorkerRuntime2() {
|
|
324
|
+
if (isTinyWorker) {
|
|
325
|
+
return globalThis !== void 0 && self["postMessage"] ? true : false;
|
|
326
|
+
} else {
|
|
327
|
+
const isMainThread = typeof __non_webpack_require__ === "function" ? __non_webpack_require__("worker_threads").isMainThread : eval("require")("worker_threads").isMainThread;
|
|
328
|
+
return !isMainThread;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// src/master/implementation.ts
|
|
333
|
+
var runningInNode = typeof process !== "undefined" && process.arch !== "browser" && "pid" in process;
|
|
334
|
+
var implementation3 = runningInNode ? implementation_node_exports : implementation_browser_exports;
|
|
335
|
+
var defaultPoolSize3 = implementation3.defaultPoolSize;
|
|
336
|
+
var getWorkerImplementation3 = implementation3.getWorkerImplementation;
|
|
337
|
+
var isWorkerRuntime3 = implementation3.isWorkerRuntime;
|
|
338
|
+
|
|
339
|
+
// src/master/pool-types.ts
|
|
340
|
+
var PoolEventType = /* @__PURE__ */ ((PoolEventType2) => {
|
|
341
|
+
PoolEventType2["initialized"] = "initialized";
|
|
342
|
+
PoolEventType2["taskCanceled"] = "taskCanceled";
|
|
343
|
+
PoolEventType2["taskCompleted"] = "taskCompleted";
|
|
344
|
+
PoolEventType2["taskFailed"] = "taskFailed";
|
|
345
|
+
PoolEventType2["taskQueued"] = "taskQueued";
|
|
346
|
+
PoolEventType2["taskQueueDrained"] = "taskQueueDrained";
|
|
347
|
+
PoolEventType2["taskStart"] = "taskStart";
|
|
348
|
+
PoolEventType2["terminated"] = "terminated";
|
|
349
|
+
return PoolEventType2;
|
|
350
|
+
})(PoolEventType || {});
|
|
351
|
+
|
|
352
|
+
// src/symbols.ts
|
|
353
|
+
var $errors = Symbol("thread.errors");
|
|
354
|
+
var $events = Symbol("thread.events");
|
|
355
|
+
var $terminate = Symbol("thread.terminate");
|
|
356
|
+
var $transferable = Symbol("thread.transferable");
|
|
357
|
+
var $worker = Symbol("thread.worker");
|
|
358
|
+
|
|
359
|
+
// src/master/thread.ts
|
|
360
|
+
function fail(message) {
|
|
361
|
+
throw new Error(message);
|
|
362
|
+
}
|
|
363
|
+
var Thread = {
|
|
364
|
+
/** Return an observable that can be used to subscribe to all errors happening in the thread. */
|
|
365
|
+
errors(thread) {
|
|
366
|
+
return thread[$errors] || fail("Error observable not found. Make sure to pass a thread instance as returned by the spawn() promise.");
|
|
367
|
+
},
|
|
368
|
+
/** Return an observable that can be used to subscribe to internal events happening in the thread. Useful for debugging. */
|
|
369
|
+
events(thread) {
|
|
370
|
+
return thread[$events] || fail("Events observable not found. Make sure to pass a thread instance as returned by the spawn() promise.");
|
|
371
|
+
},
|
|
372
|
+
/** Terminate a thread. Remember to terminate every thread when you are done using it. */
|
|
373
|
+
terminate(thread) {
|
|
374
|
+
return thread[$terminate]();
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// src/master/pool.ts
|
|
379
|
+
var nextPoolID = 1;
|
|
380
|
+
function createArray(size) {
|
|
381
|
+
const array = [];
|
|
382
|
+
for (let index = 0; index < size; index++) {
|
|
383
|
+
array.push(index);
|
|
384
|
+
}
|
|
385
|
+
return array;
|
|
386
|
+
}
|
|
387
|
+
function delay(ms) {
|
|
388
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
389
|
+
}
|
|
390
|
+
function flatMap(array, mapper) {
|
|
391
|
+
return array.reduce((flattened, element) => [...flattened, ...mapper(element)], []);
|
|
392
|
+
}
|
|
393
|
+
function slugify(text) {
|
|
394
|
+
return text.replaceAll(/\W/g, " ").trim().replaceAll(/\s+/g, "-");
|
|
395
|
+
}
|
|
396
|
+
function spawnWorkers(spawnWorker, count) {
|
|
397
|
+
return createArray(count).map(
|
|
398
|
+
() => ({
|
|
399
|
+
init: spawnWorker(),
|
|
400
|
+
runningTasks: []
|
|
401
|
+
})
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
var WorkerPool = class {
|
|
405
|
+
static EventType = PoolEventType;
|
|
406
|
+
debug;
|
|
407
|
+
eventObservable;
|
|
408
|
+
options;
|
|
409
|
+
workers;
|
|
410
|
+
eventSubject = new Subject();
|
|
411
|
+
initErrors = [];
|
|
412
|
+
isClosing = false;
|
|
413
|
+
nextTaskID = 1;
|
|
414
|
+
taskQueue = [];
|
|
415
|
+
constructor(spawnWorker, optionsOrSize) {
|
|
416
|
+
const options = typeof optionsOrSize === "number" ? { size: optionsOrSize } : optionsOrSize || {};
|
|
417
|
+
const { size = defaultPoolSize3 } = options;
|
|
418
|
+
this.debug = DebugLogger(`threads:pool:${slugify(options.name || String(nextPoolID++))}`);
|
|
419
|
+
this.options = options;
|
|
420
|
+
this.workers = spawnWorkers(spawnWorker, size);
|
|
421
|
+
this.eventObservable = multicast(Observable.from(this.eventSubject));
|
|
422
|
+
Promise.all(this.workers.map((worker) => worker.init)).then(
|
|
423
|
+
() => this.eventSubject.next({
|
|
424
|
+
size: this.workers.length,
|
|
425
|
+
type: "initialized" /* initialized */
|
|
426
|
+
}),
|
|
427
|
+
(error) => {
|
|
428
|
+
this.debug("Error while initializing pool worker:", error);
|
|
429
|
+
this.eventSubject.error(error);
|
|
430
|
+
this.initErrors.push(error);
|
|
431
|
+
}
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
findIdlingWorker() {
|
|
435
|
+
const { concurrency = 1 } = this.options;
|
|
436
|
+
return this.workers.find((worker) => worker.runningTasks.length < concurrency);
|
|
437
|
+
}
|
|
438
|
+
async runPoolTask(worker, task) {
|
|
439
|
+
const workerID = this.workers.indexOf(worker) + 1;
|
|
440
|
+
this.debug(`Running task #${task.id} on worker #${workerID}...`);
|
|
441
|
+
this.eventSubject.next({
|
|
442
|
+
taskID: task.id,
|
|
443
|
+
type: "taskStart" /* taskStart */,
|
|
444
|
+
workerID
|
|
445
|
+
});
|
|
446
|
+
try {
|
|
447
|
+
const returnValue = await task.run(await worker.init);
|
|
448
|
+
this.debug(`Task #${task.id} completed successfully`);
|
|
449
|
+
this.eventSubject.next({
|
|
450
|
+
returnValue,
|
|
451
|
+
taskID: task.id,
|
|
452
|
+
type: "taskCompleted" /* taskCompleted */,
|
|
453
|
+
workerID
|
|
454
|
+
});
|
|
455
|
+
} catch (ex) {
|
|
456
|
+
const error = ex;
|
|
457
|
+
this.debug(`Task #${task.id} failed`);
|
|
458
|
+
this.eventSubject.next({
|
|
459
|
+
error,
|
|
460
|
+
taskID: task.id,
|
|
461
|
+
type: "taskFailed" /* taskFailed */,
|
|
462
|
+
workerID
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
run(worker, task) {
|
|
467
|
+
const runPromise = (async () => {
|
|
468
|
+
const removeTaskFromWorkersRunningTasks = () => {
|
|
469
|
+
worker.runningTasks = worker.runningTasks.filter((someRunPromise) => someRunPromise !== runPromise);
|
|
470
|
+
};
|
|
471
|
+
await delay(0);
|
|
472
|
+
try {
|
|
473
|
+
await this.runPoolTask(worker, task);
|
|
474
|
+
} finally {
|
|
475
|
+
removeTaskFromWorkersRunningTasks();
|
|
476
|
+
if (!this.isClosing) {
|
|
477
|
+
this.scheduleWork();
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
})();
|
|
481
|
+
worker.runningTasks.push(runPromise);
|
|
482
|
+
}
|
|
483
|
+
scheduleWork() {
|
|
484
|
+
this.debug("Attempt de-queueing a task in order to run it...");
|
|
485
|
+
const availableWorker = this.findIdlingWorker();
|
|
486
|
+
if (!availableWorker) return;
|
|
487
|
+
const nextTask = this.taskQueue.shift();
|
|
488
|
+
if (!nextTask) {
|
|
489
|
+
this.debug("Task queue is empty");
|
|
490
|
+
this.eventSubject.next({ type: "taskQueueDrained" /* taskQueueDrained */ });
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
this.run(availableWorker, nextTask);
|
|
494
|
+
}
|
|
495
|
+
taskCompletion(taskID) {
|
|
496
|
+
return new Promise((resolve, reject) => {
|
|
497
|
+
const eventSubscription = this.events().subscribe((event) => {
|
|
498
|
+
if (event.type === "taskCompleted" /* taskCompleted */ && event.taskID === taskID) {
|
|
499
|
+
eventSubscription.unsubscribe();
|
|
500
|
+
resolve(event.returnValue);
|
|
501
|
+
} else if (event.type === "taskFailed" /* taskFailed */ && event.taskID === taskID) {
|
|
502
|
+
eventSubscription.unsubscribe();
|
|
503
|
+
reject(event.error);
|
|
504
|
+
} else if (event.type === "terminated" /* terminated */) {
|
|
505
|
+
eventSubscription.unsubscribe();
|
|
506
|
+
reject(new Error("Pool has been terminated before task was run."));
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
async settled(allowResolvingImmediately = false) {
|
|
512
|
+
const getCurrentlyRunningTasks = () => flatMap(this.workers, (worker) => worker.runningTasks);
|
|
513
|
+
const taskFailures = [];
|
|
514
|
+
const failureSubscription = this.eventObservable.subscribe((event) => {
|
|
515
|
+
if (event.type === "taskFailed" /* taskFailed */) {
|
|
516
|
+
taskFailures.push(event.error);
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
if (this.initErrors.length > 0) {
|
|
520
|
+
throw this.initErrors[0];
|
|
521
|
+
}
|
|
522
|
+
if (allowResolvingImmediately && this.taskQueue.length === 0) {
|
|
523
|
+
await allSettled(getCurrentlyRunningTasks());
|
|
524
|
+
return taskFailures;
|
|
525
|
+
}
|
|
526
|
+
await new Promise((resolve, reject) => {
|
|
527
|
+
const subscription = this.eventObservable.subscribe({
|
|
528
|
+
error: reject,
|
|
529
|
+
next(event) {
|
|
530
|
+
if (event.type === "taskQueueDrained" /* taskQueueDrained */) {
|
|
531
|
+
subscription.unsubscribe();
|
|
532
|
+
resolve(void 0);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
// make a pool-wide error reject the completed() result promise
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
await allSettled(getCurrentlyRunningTasks());
|
|
539
|
+
failureSubscription.unsubscribe();
|
|
540
|
+
return taskFailures;
|
|
541
|
+
}
|
|
542
|
+
async completed(allowResolvingImmediately = false) {
|
|
543
|
+
const settlementPromise = this.settled(allowResolvingImmediately);
|
|
544
|
+
const earlyExitPromise = new Promise((resolve, reject) => {
|
|
545
|
+
const subscription = this.eventObservable.subscribe({
|
|
546
|
+
error: reject,
|
|
547
|
+
next(event) {
|
|
548
|
+
if (event.type === "taskQueueDrained" /* taskQueueDrained */) {
|
|
549
|
+
subscription.unsubscribe();
|
|
550
|
+
resolve(settlementPromise);
|
|
551
|
+
} else if (event.type === "taskFailed" /* taskFailed */) {
|
|
552
|
+
subscription.unsubscribe();
|
|
553
|
+
reject(event.error);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
// make a pool-wide error reject the completed() result promise
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
const errors = await Promise.race([settlementPromise, earlyExitPromise]);
|
|
560
|
+
if (errors.length > 0) {
|
|
561
|
+
throw errors[0];
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
events() {
|
|
565
|
+
return this.eventObservable;
|
|
566
|
+
}
|
|
567
|
+
queue(taskFunction) {
|
|
568
|
+
const { maxQueuedJobs = Number.POSITIVE_INFINITY } = this.options;
|
|
569
|
+
if (this.isClosing) {
|
|
570
|
+
throw new Error("Cannot schedule pool tasks after terminate() has been called.");
|
|
571
|
+
}
|
|
572
|
+
if (this.initErrors.length > 0) {
|
|
573
|
+
throw this.initErrors[0];
|
|
574
|
+
}
|
|
575
|
+
const taskID = this.nextTaskID++;
|
|
576
|
+
const taskCompletion = this.taskCompletion(taskID);
|
|
577
|
+
taskCompletion.catch((error) => {
|
|
578
|
+
this.debug(`Task #${taskID} errored:`, error);
|
|
579
|
+
});
|
|
580
|
+
const task = {
|
|
581
|
+
cancel: () => {
|
|
582
|
+
if (!this.taskQueue.includes(task)) return;
|
|
583
|
+
this.taskQueue = this.taskQueue.filter((someTask) => someTask !== task);
|
|
584
|
+
this.eventSubject.next({
|
|
585
|
+
taskID: task.id,
|
|
586
|
+
type: "taskCanceled" /* taskCanceled */
|
|
587
|
+
});
|
|
588
|
+
},
|
|
589
|
+
id: taskID,
|
|
590
|
+
run: taskFunction,
|
|
591
|
+
then: taskCompletion.then.bind(taskCompletion)
|
|
592
|
+
};
|
|
593
|
+
if (this.taskQueue.length >= maxQueuedJobs) {
|
|
594
|
+
throw new Error(
|
|
595
|
+
"Maximum number of pool tasks queued. Refusing to queue another one.\nThis usually happens for one of two reasons: We are either at peak workload right now or some tasks just won't finish, thus blocking the pool."
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
this.debug(`Queueing task #${task.id}...`);
|
|
599
|
+
this.taskQueue.push(task);
|
|
600
|
+
this.eventSubject.next({
|
|
601
|
+
taskID: task.id,
|
|
602
|
+
type: "taskQueued" /* taskQueued */
|
|
603
|
+
});
|
|
604
|
+
this.scheduleWork();
|
|
605
|
+
return task;
|
|
606
|
+
}
|
|
607
|
+
async terminate(force) {
|
|
608
|
+
this.isClosing = true;
|
|
609
|
+
if (!force) {
|
|
610
|
+
await this.completed(true);
|
|
611
|
+
}
|
|
612
|
+
this.eventSubject.next({
|
|
613
|
+
remainingQueue: [...this.taskQueue],
|
|
614
|
+
type: "terminated" /* terminated */
|
|
615
|
+
});
|
|
616
|
+
this.eventSubject.complete();
|
|
617
|
+
await Promise.all(this.workers.map(async (worker) => Thread.terminate(await worker.init)));
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
function PoolConstructor(spawnWorker, optionsOrSize) {
|
|
621
|
+
return new WorkerPool(spawnWorker, optionsOrSize);
|
|
622
|
+
}
|
|
623
|
+
PoolConstructor.EventType = PoolEventType;
|
|
624
|
+
var Pool = PoolConstructor;
|
|
625
|
+
|
|
626
|
+
// src/master/spawn.ts
|
|
627
|
+
import DebugLogger3 from "debug";
|
|
628
|
+
import { Observable as Observable4 } from "observable-fns";
|
|
629
|
+
|
|
630
|
+
// src/promise.ts
|
|
631
|
+
var doNothing = () => void 0;
|
|
632
|
+
function createPromiseWithResolver() {
|
|
633
|
+
let alreadyResolved = false;
|
|
634
|
+
let resolvedTo;
|
|
635
|
+
let resolver = doNothing;
|
|
636
|
+
const promise = new Promise((resolve) => {
|
|
637
|
+
if (alreadyResolved) {
|
|
638
|
+
resolve(resolvedTo);
|
|
639
|
+
} else {
|
|
640
|
+
resolver = resolve;
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
const exposedResolver = (value) => {
|
|
644
|
+
alreadyResolved = true;
|
|
645
|
+
resolvedTo = value;
|
|
646
|
+
resolver(resolvedTo);
|
|
647
|
+
};
|
|
648
|
+
return [promise, exposedResolver];
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// src/master/invocation-proxy.ts
|
|
652
|
+
import DebugLogger2 from "debug";
|
|
653
|
+
import { multicast as multicast2, Observable as Observable3 } from "observable-fns";
|
|
654
|
+
|
|
655
|
+
// src/observable-promise.ts
|
|
656
|
+
import { Observable as Observable2 } from "observable-fns";
|
|
657
|
+
var doNothing2 = () => {
|
|
658
|
+
};
|
|
659
|
+
var returnInput = (input) => input;
|
|
660
|
+
var runDeferred = (fn) => Promise.resolve().then(fn);
|
|
661
|
+
function fail2(error) {
|
|
662
|
+
throw error;
|
|
663
|
+
}
|
|
664
|
+
function isThenable(thing) {
|
|
665
|
+
return thing && typeof thing.then === "function";
|
|
666
|
+
}
|
|
667
|
+
var ObservablePromise = class _ObservablePromise extends Observable2 {
|
|
668
|
+
[Symbol.toStringTag] = "[object ObservablePromise]";
|
|
669
|
+
initHasRun = false;
|
|
670
|
+
fulfillmentCallbacks = [];
|
|
671
|
+
rejectionCallbacks = [];
|
|
672
|
+
firstValue;
|
|
673
|
+
firstValueSet = false;
|
|
674
|
+
rejection;
|
|
675
|
+
state = "pending";
|
|
676
|
+
constructor(init) {
|
|
677
|
+
super((originalObserver) => {
|
|
678
|
+
const self2 = this;
|
|
679
|
+
const observer = {
|
|
680
|
+
...originalObserver,
|
|
681
|
+
complete() {
|
|
682
|
+
originalObserver.complete();
|
|
683
|
+
self2.onCompletion();
|
|
684
|
+
},
|
|
685
|
+
error(error) {
|
|
686
|
+
originalObserver.error(error);
|
|
687
|
+
self2.onError(error);
|
|
688
|
+
},
|
|
689
|
+
next(value) {
|
|
690
|
+
originalObserver.next(value);
|
|
691
|
+
self2.onNext(value);
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
try {
|
|
695
|
+
this.initHasRun = true;
|
|
696
|
+
return init(observer);
|
|
697
|
+
} catch (error) {
|
|
698
|
+
observer.error(error);
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
onNext(value) {
|
|
703
|
+
if (!this.firstValueSet) {
|
|
704
|
+
this.firstValue = value;
|
|
705
|
+
this.firstValueSet = true;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
onError(error) {
|
|
709
|
+
this.state = "rejected";
|
|
710
|
+
this.rejection = error;
|
|
711
|
+
for (const onRejected of this.rejectionCallbacks) {
|
|
712
|
+
runDeferred(() => onRejected(error));
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
onCompletion() {
|
|
716
|
+
this.state = "fulfilled";
|
|
717
|
+
for (const onFulfilled of this.fulfillmentCallbacks) {
|
|
718
|
+
runDeferred(() => onFulfilled(this.firstValue));
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
then(onFulfilledRaw, onRejectedRaw) {
|
|
722
|
+
const onFulfilled = onFulfilledRaw || returnInput;
|
|
723
|
+
const onRejected = onRejectedRaw || fail2;
|
|
724
|
+
let onRejectedCalled = false;
|
|
725
|
+
return new Promise((resolve, reject) => {
|
|
726
|
+
const rejectionCallback = (error) => {
|
|
727
|
+
if (onRejectedCalled) return;
|
|
728
|
+
onRejectedCalled = true;
|
|
729
|
+
try {
|
|
730
|
+
resolve(onRejected(error));
|
|
731
|
+
} catch (anotherError) {
|
|
732
|
+
reject(anotherError);
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
const fulfillmentCallback = (value) => {
|
|
736
|
+
try {
|
|
737
|
+
resolve(onFulfilled(value));
|
|
738
|
+
} catch (ex) {
|
|
739
|
+
const error = ex;
|
|
740
|
+
rejectionCallback(error);
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
if (!this.initHasRun) {
|
|
744
|
+
this.subscribe({ error: rejectionCallback });
|
|
745
|
+
}
|
|
746
|
+
if (this.state === "fulfilled") {
|
|
747
|
+
return resolve(onFulfilled(this.firstValue));
|
|
748
|
+
}
|
|
749
|
+
if (this.state === "rejected") {
|
|
750
|
+
onRejectedCalled = true;
|
|
751
|
+
return resolve(onRejected(this.rejection));
|
|
752
|
+
}
|
|
753
|
+
this.fulfillmentCallbacks.push(fulfillmentCallback);
|
|
754
|
+
this.rejectionCallbacks.push(rejectionCallback);
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
catch(onRejected) {
|
|
758
|
+
return this.then(void 0, onRejected);
|
|
759
|
+
}
|
|
760
|
+
finally(onCompleted) {
|
|
761
|
+
const handler = onCompleted || doNothing2;
|
|
762
|
+
return this.then(
|
|
763
|
+
(value) => {
|
|
764
|
+
handler();
|
|
765
|
+
return value;
|
|
766
|
+
},
|
|
767
|
+
() => handler()
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
static from(thing) {
|
|
771
|
+
return isThenable(thing) ? new _ObservablePromise((observer) => {
|
|
772
|
+
const onFulfilled = (value) => {
|
|
773
|
+
observer.next(value);
|
|
774
|
+
observer.complete();
|
|
775
|
+
};
|
|
776
|
+
const onRejected = (error) => {
|
|
777
|
+
observer.error(error);
|
|
778
|
+
};
|
|
779
|
+
thing.then(onFulfilled, onRejected);
|
|
780
|
+
}) : super.from(thing);
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
// src/transferable.ts
|
|
785
|
+
function isTransferable(thing) {
|
|
786
|
+
if (!thing || typeof thing !== "object") return false;
|
|
787
|
+
return true;
|
|
788
|
+
}
|
|
789
|
+
function isTransferDescriptor(thing) {
|
|
790
|
+
return thing && typeof thing === "object" && thing[$transferable];
|
|
791
|
+
}
|
|
792
|
+
function Transfer(payload, transferables) {
|
|
793
|
+
console.log("Transfer");
|
|
794
|
+
if (!transferables) {
|
|
795
|
+
if (!isTransferable(payload)) throw new Error("Not transferable");
|
|
796
|
+
transferables = [payload];
|
|
797
|
+
}
|
|
798
|
+
return {
|
|
799
|
+
[$transferable]: true,
|
|
800
|
+
send: payload,
|
|
801
|
+
transferables
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// src/master/invocation-proxy.ts
|
|
806
|
+
var debugMessages = DebugLogger2("threads:master:messages");
|
|
807
|
+
var nextJobUID = 1;
|
|
808
|
+
var dedupe = (array) => [...new Set(array)];
|
|
809
|
+
var isJobErrorMessage = (data) => data && data.type === "error" /* error */;
|
|
810
|
+
var isJobResultMessage = (data) => data && data.type === "result" /* result */;
|
|
811
|
+
var isJobStartMessage = (data) => data && data.type === "running" /* running */;
|
|
812
|
+
function createObservableForJob(worker, jobUID) {
|
|
813
|
+
return new Observable3((observer) => {
|
|
814
|
+
let asyncType;
|
|
815
|
+
const messageHandler = (event) => {
|
|
816
|
+
debugMessages("Message from worker:", event.data);
|
|
817
|
+
if (!event.data || event.data.uid !== jobUID) return;
|
|
818
|
+
if (isJobStartMessage(event.data)) {
|
|
819
|
+
asyncType = event.data.resultType;
|
|
820
|
+
} else if (isJobResultMessage(event.data)) {
|
|
821
|
+
if (asyncType === "promise") {
|
|
822
|
+
if (event.data.payload !== void 0) {
|
|
823
|
+
observer.next(deserialize(event.data.payload));
|
|
824
|
+
}
|
|
825
|
+
observer.complete();
|
|
826
|
+
worker.removeEventListener("message", messageHandler);
|
|
827
|
+
} else {
|
|
828
|
+
if (event.data.payload) {
|
|
829
|
+
observer.next(deserialize(event.data.payload));
|
|
830
|
+
}
|
|
831
|
+
if (event.data.complete) {
|
|
832
|
+
observer.complete();
|
|
833
|
+
worker.removeEventListener("message", messageHandler);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
} else if (isJobErrorMessage(event.data)) {
|
|
837
|
+
const error = deserialize(event.data.error);
|
|
838
|
+
if (asyncType === "promise" || !asyncType) {
|
|
839
|
+
observer.error(error);
|
|
840
|
+
} else {
|
|
841
|
+
observer.error(error);
|
|
842
|
+
}
|
|
843
|
+
worker.removeEventListener("message", messageHandler);
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
worker.addEventListener("message", messageHandler);
|
|
847
|
+
return () => {
|
|
848
|
+
if (asyncType === "observable" || !asyncType) {
|
|
849
|
+
const cancelMessage = {
|
|
850
|
+
type: "cancel" /* cancel */,
|
|
851
|
+
uid: jobUID
|
|
852
|
+
};
|
|
853
|
+
worker.postMessage(cancelMessage);
|
|
854
|
+
}
|
|
855
|
+
worker.removeEventListener("message", messageHandler);
|
|
856
|
+
};
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
function prepareArguments(rawArgs) {
|
|
860
|
+
if (rawArgs.length === 0) {
|
|
861
|
+
return {
|
|
862
|
+
args: [],
|
|
863
|
+
transferables: []
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
const args = [];
|
|
867
|
+
const transferables = [];
|
|
868
|
+
for (const arg of rawArgs) {
|
|
869
|
+
if (isTransferDescriptor(arg)) {
|
|
870
|
+
args.push(serialize(arg.send));
|
|
871
|
+
transferables.push(...arg.transferables);
|
|
872
|
+
} else {
|
|
873
|
+
args.push(serialize(arg));
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
return {
|
|
877
|
+
args,
|
|
878
|
+
transferables: transferables.length === 0 ? transferables : dedupe(transferables)
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
function createProxyFunction(worker, method) {
|
|
882
|
+
return (...rawArgs) => {
|
|
883
|
+
const uid = nextJobUID++;
|
|
884
|
+
const { args, transferables } = prepareArguments(rawArgs);
|
|
885
|
+
const runMessage = {
|
|
886
|
+
args,
|
|
887
|
+
method,
|
|
888
|
+
type: "run" /* run */,
|
|
889
|
+
uid
|
|
890
|
+
};
|
|
891
|
+
debugMessages("Sending command to run function to worker:", runMessage);
|
|
892
|
+
try {
|
|
893
|
+
worker.postMessage(runMessage, transferables);
|
|
894
|
+
} catch (error) {
|
|
895
|
+
return ObservablePromise.from(Promise.reject(error));
|
|
896
|
+
}
|
|
897
|
+
return ObservablePromise.from(multicast2(createObservableForJob(worker, uid)));
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
function createProxyModule(worker, methodNames) {
|
|
901
|
+
const proxy = {};
|
|
902
|
+
for (const methodName of methodNames) {
|
|
903
|
+
proxy[methodName] = createProxyFunction(worker, methodName);
|
|
904
|
+
}
|
|
905
|
+
return proxy;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// src/master/spawn.ts
|
|
909
|
+
var debugMessages2 = DebugLogger3("threads:master:messages");
|
|
910
|
+
var debugSpawn = DebugLogger3("threads:master:spawn");
|
|
911
|
+
var debugThreadUtils = DebugLogger3("threads:master:thread-utils");
|
|
912
|
+
var isInitMessage = (data) => data && data.type === "init";
|
|
913
|
+
var isUncaughtErrorMessage = (data) => data && data.type === "uncaughtError";
|
|
914
|
+
var initMessageTimeout = typeof process !== "undefined" && process.env !== void 0 && process.env.THREADS_WORKER_INIT_TIMEOUT ? Number.parseInt(process.env.THREADS_WORKER_INIT_TIMEOUT, 10) : 1e4;
|
|
915
|
+
async function withTimeout(promise, timeoutInMs, errorMessage) {
|
|
916
|
+
let timeoutHandle;
|
|
917
|
+
const timeout = new Promise((resolve, reject) => {
|
|
918
|
+
timeoutHandle = setTimeout(() => reject(new Error(errorMessage)), timeoutInMs);
|
|
919
|
+
});
|
|
920
|
+
const result = await Promise.race([promise, timeout]);
|
|
921
|
+
clearTimeout(timeoutHandle);
|
|
922
|
+
return result;
|
|
923
|
+
}
|
|
924
|
+
function receiveInitMessage(worker) {
|
|
925
|
+
return new Promise((resolve, reject) => {
|
|
926
|
+
const messageHandler = (event) => {
|
|
927
|
+
debugMessages2("Message from worker before finishing initialization:", event.data);
|
|
928
|
+
if (isInitMessage(event.data)) {
|
|
929
|
+
worker.removeEventListener("message", messageHandler);
|
|
930
|
+
resolve(event.data);
|
|
931
|
+
} else if (isUncaughtErrorMessage(event.data)) {
|
|
932
|
+
worker.removeEventListener("message", messageHandler);
|
|
933
|
+
reject(deserialize(event.data.error));
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
worker.addEventListener("message", messageHandler);
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
function createEventObservable(worker, workerTermination) {
|
|
940
|
+
return new Observable4((observer) => {
|
|
941
|
+
const messageHandler = (messageEvent) => {
|
|
942
|
+
const workerEvent = {
|
|
943
|
+
data: messageEvent.data,
|
|
944
|
+
type: "message" /* message */
|
|
945
|
+
};
|
|
946
|
+
observer.next(workerEvent);
|
|
947
|
+
};
|
|
948
|
+
const rejectionHandler = (errorEvent) => {
|
|
949
|
+
debugThreadUtils("Unhandled promise rejection event in thread:", errorEvent);
|
|
950
|
+
const workerEvent = {
|
|
951
|
+
error: new Error(errorEvent.reason),
|
|
952
|
+
type: "internalError" /* internalError */
|
|
953
|
+
};
|
|
954
|
+
observer.next(workerEvent);
|
|
955
|
+
};
|
|
956
|
+
worker.addEventListener("message", messageHandler);
|
|
957
|
+
worker.addEventListener("unhandledrejection", rejectionHandler);
|
|
958
|
+
workerTermination.then(() => {
|
|
959
|
+
const terminationEvent = { type: "termination" /* termination */ };
|
|
960
|
+
worker.removeEventListener("message", messageHandler);
|
|
961
|
+
worker.removeEventListener("unhandledrejection", rejectionHandler);
|
|
962
|
+
observer.next(terminationEvent);
|
|
963
|
+
observer.complete();
|
|
964
|
+
});
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
function createTerminator(worker) {
|
|
968
|
+
const [termination, resolver] = createPromiseWithResolver();
|
|
969
|
+
const terminate = async () => {
|
|
970
|
+
debugThreadUtils("Terminating worker");
|
|
971
|
+
await worker.terminate();
|
|
972
|
+
resolver();
|
|
973
|
+
};
|
|
974
|
+
return { terminate, termination };
|
|
975
|
+
}
|
|
976
|
+
function setPrivateThreadProps(raw, worker, workerEvents, terminate) {
|
|
977
|
+
const workerErrors = workerEvents.filter((event) => event.type === "internalError" /* internalError */).map((errorEvent) => errorEvent.error);
|
|
978
|
+
return Object.assign(raw, {
|
|
979
|
+
[$errors]: workerErrors,
|
|
980
|
+
[$events]: workerEvents,
|
|
981
|
+
[$terminate]: terminate,
|
|
982
|
+
[$worker]: worker
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
async function spawn(worker, options) {
|
|
986
|
+
debugSpawn("Initializing new thread");
|
|
987
|
+
const timeout = options && options.timeout ? options.timeout : initMessageTimeout;
|
|
988
|
+
const initMessage = await withTimeout(
|
|
989
|
+
receiveInitMessage(worker),
|
|
990
|
+
timeout,
|
|
991
|
+
`Timeout: Did not receive an init message from worker after ${timeout}ms. Make sure the worker calls expose().`
|
|
992
|
+
);
|
|
993
|
+
const exposed = initMessage.exposed;
|
|
994
|
+
const { termination, terminate } = createTerminator(worker);
|
|
995
|
+
const events = createEventObservable(worker, termination);
|
|
996
|
+
if (exposed.type === "function") {
|
|
997
|
+
const proxy = createProxyFunction(worker);
|
|
998
|
+
return setPrivateThreadProps(proxy, worker, events, terminate);
|
|
999
|
+
} else if (exposed.type === "module") {
|
|
1000
|
+
const proxy = createProxyModule(worker, exposed.methods);
|
|
1001
|
+
return setPrivateThreadProps(proxy, worker, events, terminate);
|
|
1002
|
+
} else {
|
|
1003
|
+
const type = exposed.type;
|
|
1004
|
+
throw new Error(`Worker init message states unexpected type of expose(): ${type}`);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// src/master/index-browser.ts
|
|
1009
|
+
var BlobWorker = getWorkerImplementation().blob;
|
|
1010
|
+
var Worker2 = getWorkerImplementation().default;
|
|
1011
|
+
export {
|
|
1012
|
+
BlobWorker,
|
|
1013
|
+
DefaultSerializer,
|
|
1014
|
+
Pool,
|
|
1015
|
+
Thread,
|
|
1016
|
+
Transfer,
|
|
1017
|
+
Worker2 as Worker,
|
|
1018
|
+
isWorkerRuntime3 as isWorkerRuntime,
|
|
1019
|
+
registerSerializer,
|
|
1020
|
+
spawn
|
|
1021
|
+
};
|
|
1022
|
+
//# sourceMappingURL=index-browser.mjs.map
|