@atlaspack/workers 2.14.31 → 2.14.32
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/CHANGELOG.md +10 -0
- package/dist/Handle.js +33 -0
- package/dist/Worker.js +180 -0
- package/dist/WorkerFarm.js +541 -0
- package/dist/backend.js +34 -0
- package/dist/bus.js +24 -0
- package/dist/child.js +290 -0
- package/dist/childState.js +11 -0
- package/dist/cpuCount.js +72 -0
- package/dist/index.js +44 -0
- package/dist/process/ProcessChild.js +48 -0
- package/dist/process/ProcessWorker.js +68 -0
- package/dist/threads/ThreadsChild.js +31 -0
- package/dist/threads/ThreadsWorker.js +47 -0
- package/dist/types.js +2 -0
- package/dist/web/WebChild.js +34 -0
- package/dist/web/WebWorker.js +70 -0
- package/package.json +5 -6
- package/tsconfig.json +25 -2
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.Handle = void 0;
|
|
40
|
+
const bus = __importStar(require("./bus"));
|
|
41
|
+
const assert_1 = __importDefault(require("assert"));
|
|
42
|
+
const nullthrows_1 = __importDefault(require("nullthrows"));
|
|
43
|
+
const events_1 = __importDefault(require("events"));
|
|
44
|
+
const build_cache_1 = require("@atlaspack/build-cache");
|
|
45
|
+
const diagnostic_1 = __importStar(require("@atlaspack/diagnostic"));
|
|
46
|
+
const Worker_1 = __importDefault(require("./Worker"));
|
|
47
|
+
const cpuCount_1 = __importDefault(require("./cpuCount"));
|
|
48
|
+
const Handle_1 = __importDefault(require("./Handle"));
|
|
49
|
+
exports.Handle = Handle_1.default;
|
|
50
|
+
const childState_1 = require("./childState");
|
|
51
|
+
const backend_1 = require("./backend");
|
|
52
|
+
const profiler_1 = require("@atlaspack/profiler");
|
|
53
|
+
const fs_1 = __importDefault(require("fs"));
|
|
54
|
+
const logger_1 = __importDefault(require("@atlaspack/logger"));
|
|
55
|
+
let referenceId = 1;
|
|
56
|
+
const DEFAULT_MAX_CONCURRENT_CALLS = 30;
|
|
57
|
+
/**
|
|
58
|
+
* workerPath should always be defined inside farmOptions
|
|
59
|
+
*/
|
|
60
|
+
class WorkerFarm extends events_1.default {
|
|
61
|
+
constructor(farmOptions = {}) {
|
|
62
|
+
super();
|
|
63
|
+
this.callQueue = [];
|
|
64
|
+
this.ending = false;
|
|
65
|
+
this.warmWorkers = 0;
|
|
66
|
+
this.readyWorkers = 0;
|
|
67
|
+
this.workers = new Map();
|
|
68
|
+
this.handles = new Map();
|
|
69
|
+
this.sharedReferences = new Map();
|
|
70
|
+
this.sharedReferencesByValue = new Map();
|
|
71
|
+
this.serializedSharedReferences = new Map();
|
|
72
|
+
this.workerApi = {
|
|
73
|
+
callMaster: async (request, awaitResponse = true) => {
|
|
74
|
+
let result = await this.processRequest({
|
|
75
|
+
...request,
|
|
76
|
+
// @ts-expect-error TS2322
|
|
77
|
+
awaitResponse,
|
|
78
|
+
});
|
|
79
|
+
return (0, build_cache_1.deserialize)((0, build_cache_1.serialize)(result));
|
|
80
|
+
},
|
|
81
|
+
createReverseHandle: (fn) => this.createReverseHandle(fn),
|
|
82
|
+
callChild: (childId, request) => new Promise((resolve, reject) => {
|
|
83
|
+
(0, nullthrows_1.default)(this.workers.get(childId)).call({
|
|
84
|
+
...request,
|
|
85
|
+
resolve,
|
|
86
|
+
reject,
|
|
87
|
+
retries: 0,
|
|
88
|
+
});
|
|
89
|
+
}),
|
|
90
|
+
runHandle: (handle, args) => this.workerApi.callChild((0, nullthrows_1.default)(handle.childId), {
|
|
91
|
+
handle: handle.id,
|
|
92
|
+
args,
|
|
93
|
+
}),
|
|
94
|
+
getSharedReference: (ref) => this.sharedReferences.get(ref),
|
|
95
|
+
resolveSharedReference: (value) => this.sharedReferencesByValue.get(value),
|
|
96
|
+
};
|
|
97
|
+
this.options = {
|
|
98
|
+
maxConcurrentWorkers: WorkerFarm.getNumWorkers(),
|
|
99
|
+
maxConcurrentCallsPerWorker: WorkerFarm.getConcurrentCallsPerWorker(farmOptions.shouldTrace ? 1 : DEFAULT_MAX_CONCURRENT_CALLS),
|
|
100
|
+
forcedKillTime: 500,
|
|
101
|
+
warmWorkers: false,
|
|
102
|
+
useLocalWorker: true, // TODO: setting this to false makes some tests fail, figure out why
|
|
103
|
+
backend: (0, backend_1.detectBackend)(),
|
|
104
|
+
...farmOptions,
|
|
105
|
+
};
|
|
106
|
+
if (!this.options.workerPath) {
|
|
107
|
+
throw new Error('Please provide a worker path!');
|
|
108
|
+
}
|
|
109
|
+
this.localWorker = require(this.options.workerPath);
|
|
110
|
+
// @ts-expect-error TS2322
|
|
111
|
+
this.localWorkerInit =
|
|
112
|
+
this.localWorker.childInit != null ? this.localWorker.childInit() : null;
|
|
113
|
+
this.run = this.createHandle('run');
|
|
114
|
+
// Worker thread stdout is by default piped into the process stdout, if there are enough worker
|
|
115
|
+
// threads to exceed the default listener limit, then anything else piping into stdout will trigger
|
|
116
|
+
// the `MaxListenersExceededWarning`, so we should ensure the max listeners is at least equal to the
|
|
117
|
+
// number of workers + 1 for the main thread.
|
|
118
|
+
//
|
|
119
|
+
// Note this can't be fixed easily where other things pipe into stdout - even after starting > 10 worker
|
|
120
|
+
// threads `process.stdout.getMaxListeners()` will still return 10, however adding another pipe into `stdout`
|
|
121
|
+
// will give the warning with `<worker count + 1>` as the number of listeners.
|
|
122
|
+
process.stdout?.setMaxListeners(Math.max(process.stdout.getMaxListeners(), WorkerFarm.getNumWorkers() + 1));
|
|
123
|
+
this.startMaxWorkers();
|
|
124
|
+
}
|
|
125
|
+
warmupWorker(method, args) {
|
|
126
|
+
// Workers are already stopping
|
|
127
|
+
if (this.ending) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Workers are not warmed up yet.
|
|
131
|
+
// Send the job to a remote worker in the background,
|
|
132
|
+
// but use the result from the local worker - it will be faster.
|
|
133
|
+
let promise = this.addCall(method, [...args, true]);
|
|
134
|
+
if (promise) {
|
|
135
|
+
promise
|
|
136
|
+
.then(() => {
|
|
137
|
+
this.warmWorkers++;
|
|
138
|
+
if (this.warmWorkers >= this.workers.size) {
|
|
139
|
+
this.emit('warmedup');
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
.catch(() => { });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
shouldStartRemoteWorkers() {
|
|
146
|
+
return (this.options.maxConcurrentWorkers > 0 || !this.options.useLocalWorker);
|
|
147
|
+
}
|
|
148
|
+
createHandle(method, useMainThread = false) {
|
|
149
|
+
if (!this.options.useLocalWorker) {
|
|
150
|
+
useMainThread = false;
|
|
151
|
+
}
|
|
152
|
+
return async (...args) => {
|
|
153
|
+
// Child process workers are slow to start (~600ms).
|
|
154
|
+
// While we're waiting, just run on the main thread.
|
|
155
|
+
// This significantly speeds up startup time.
|
|
156
|
+
if (this.shouldUseRemoteWorkers() && !useMainThread) {
|
|
157
|
+
return this.addCall(method, [...args, false]);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
if (this.options.warmWorkers && this.shouldStartRemoteWorkers()) {
|
|
161
|
+
this.warmupWorker(method, args);
|
|
162
|
+
}
|
|
163
|
+
let processedArgs;
|
|
164
|
+
if (!useMainThread) {
|
|
165
|
+
processedArgs = (0, build_cache_1.restoreDeserializedObject)((0, build_cache_1.prepareForSerialization)([...args, false]));
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
processedArgs = args;
|
|
169
|
+
}
|
|
170
|
+
if (this.localWorkerInit != null) {
|
|
171
|
+
await this.localWorkerInit;
|
|
172
|
+
this.localWorkerInit = null;
|
|
173
|
+
}
|
|
174
|
+
return this.localWorker[method](this.workerApi, ...processedArgs);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
onError(error, worker) {
|
|
179
|
+
// Handle ipc errors
|
|
180
|
+
if (error.code === 'ERR_IPC_CHANNEL_CLOSED') {
|
|
181
|
+
// @ts-expect-error TS2322
|
|
182
|
+
return this.stopWorker(worker);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
logger_1.default.error(error, '@atlaspack/workers');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
startChild() {
|
|
189
|
+
let worker = new Worker_1.default({
|
|
190
|
+
forcedKillTime: this.options.forcedKillTime,
|
|
191
|
+
backend: this.options.backend,
|
|
192
|
+
shouldPatchConsole: this.options.shouldPatchConsole,
|
|
193
|
+
shouldTrace: this.options.shouldTrace,
|
|
194
|
+
sharedReferences: this.sharedReferences,
|
|
195
|
+
});
|
|
196
|
+
worker.fork((0, nullthrows_1.default)(this.options.workerPath));
|
|
197
|
+
worker.on('request', (data) => this.processRequest(data, worker));
|
|
198
|
+
worker.on('ready', () => {
|
|
199
|
+
this.readyWorkers++;
|
|
200
|
+
if (this.readyWorkers === this.options.maxConcurrentWorkers) {
|
|
201
|
+
this.emit('ready');
|
|
202
|
+
}
|
|
203
|
+
this.processQueue();
|
|
204
|
+
});
|
|
205
|
+
worker.on('response', () => this.processQueue());
|
|
206
|
+
worker.on('error', (err) => this.onError(err, worker));
|
|
207
|
+
worker.once('exit', () => this.stopWorker(worker));
|
|
208
|
+
this.workers.set(worker.id, worker);
|
|
209
|
+
}
|
|
210
|
+
async stopWorker(worker) {
|
|
211
|
+
if (!worker.stopped) {
|
|
212
|
+
this.workers.delete(worker.id);
|
|
213
|
+
worker.isStopping = true;
|
|
214
|
+
if (worker.calls.size) {
|
|
215
|
+
for (let call of worker.calls.values()) {
|
|
216
|
+
call.retries++;
|
|
217
|
+
this.callQueue.unshift(call);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
worker.calls.clear();
|
|
221
|
+
await worker.stop();
|
|
222
|
+
// Process any requests that failed and start a new worker
|
|
223
|
+
this.processQueue();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
processQueue() {
|
|
227
|
+
if (this.ending || !this.callQueue.length)
|
|
228
|
+
return;
|
|
229
|
+
if (this.workers.size < this.options.maxConcurrentWorkers) {
|
|
230
|
+
this.startChild();
|
|
231
|
+
}
|
|
232
|
+
let workers = [...this.workers.values()].sort((a, b) => a.calls.size - b.calls.size);
|
|
233
|
+
for (let worker of workers) {
|
|
234
|
+
if (!this.callQueue.length) {
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
if (!worker.ready || worker.stopped || worker.isStopping) {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (worker.calls.size < this.options.maxConcurrentCallsPerWorker) {
|
|
241
|
+
// @ts-expect-error TS2345
|
|
242
|
+
this.callWorker(worker, this.callQueue.shift());
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async callWorker(worker, call) {
|
|
247
|
+
for (let ref of this.sharedReferences.keys()) {
|
|
248
|
+
if (!worker.sentSharedReferences.has(ref)) {
|
|
249
|
+
await worker.sendSharedReference(ref, this.getSerializedSharedReference(ref));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
worker.call(call);
|
|
253
|
+
}
|
|
254
|
+
async processRequest(data, worker) {
|
|
255
|
+
let { method, args, location, awaitResponse, idx, handle: handleId } = data;
|
|
256
|
+
let mod;
|
|
257
|
+
if (handleId != null) {
|
|
258
|
+
mod = (0, nullthrows_1.default)(this.handles.get(handleId)?.fn);
|
|
259
|
+
}
|
|
260
|
+
else if (location) {
|
|
261
|
+
// @ts-expect-error TS2339
|
|
262
|
+
if (process.browser) {
|
|
263
|
+
if (location === '@atlaspack/workers/bus') {
|
|
264
|
+
mod = bus;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
throw new Error('No dynamic require possible: ' + location);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
mod = require(location);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
throw new Error('Unknown request');
|
|
276
|
+
}
|
|
277
|
+
const responseFromContent = (content) => ({
|
|
278
|
+
idx,
|
|
279
|
+
type: 'response',
|
|
280
|
+
contentType: 'data',
|
|
281
|
+
content,
|
|
282
|
+
});
|
|
283
|
+
const errorResponseFromError = (e) => ({
|
|
284
|
+
idx,
|
|
285
|
+
type: 'response',
|
|
286
|
+
contentType: 'error',
|
|
287
|
+
content: (0, diagnostic_1.anyToDiagnostic)(e),
|
|
288
|
+
});
|
|
289
|
+
let result;
|
|
290
|
+
if (method == null) {
|
|
291
|
+
try {
|
|
292
|
+
// @ts-expect-error TS2488
|
|
293
|
+
result = responseFromContent(await mod(...args));
|
|
294
|
+
}
|
|
295
|
+
catch (e) {
|
|
296
|
+
result = errorResponseFromError(e);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
// ESModule default interop
|
|
301
|
+
if (mod.__esModule && !mod[method] && mod.default) {
|
|
302
|
+
mod = mod.default;
|
|
303
|
+
}
|
|
304
|
+
try {
|
|
305
|
+
result = responseFromContent(await mod[method](...args));
|
|
306
|
+
}
|
|
307
|
+
catch (e) {
|
|
308
|
+
result = errorResponseFromError(e);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (awaitResponse) {
|
|
312
|
+
if (worker) {
|
|
313
|
+
worker.send(result);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
if (result.contentType === 'error') {
|
|
317
|
+
throw new diagnostic_1.default({ diagnostic: result.content });
|
|
318
|
+
}
|
|
319
|
+
return result.content;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
addCall(method, args) {
|
|
324
|
+
if (this.ending) {
|
|
325
|
+
throw new Error('Cannot add a worker call if workerfarm is ending.');
|
|
326
|
+
}
|
|
327
|
+
return new Promise((resolve, reject) => {
|
|
328
|
+
this.callQueue.push({
|
|
329
|
+
method,
|
|
330
|
+
args: args,
|
|
331
|
+
retries: 0,
|
|
332
|
+
resolve,
|
|
333
|
+
reject,
|
|
334
|
+
});
|
|
335
|
+
this.processQueue();
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
async end() {
|
|
339
|
+
this.ending = true;
|
|
340
|
+
await Promise.all(Array.from(this.workers.values()).map((worker) => this.stopWorker(worker)));
|
|
341
|
+
for (let handle of this.handles.values()) {
|
|
342
|
+
handle.dispose();
|
|
343
|
+
}
|
|
344
|
+
this.handles = new Map();
|
|
345
|
+
this.sharedReferences = new Map();
|
|
346
|
+
this.sharedReferencesByValue = new Map();
|
|
347
|
+
this.ending = false;
|
|
348
|
+
}
|
|
349
|
+
startMaxWorkers() {
|
|
350
|
+
// Starts workers until the maximum is reached
|
|
351
|
+
if (this.workers.size < this.options.maxConcurrentWorkers) {
|
|
352
|
+
let toStart = this.options.maxConcurrentWorkers - this.workers.size;
|
|
353
|
+
while (toStart--) {
|
|
354
|
+
this.startChild();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
shouldUseRemoteWorkers() {
|
|
359
|
+
return (!this.options.useLocalWorker ||
|
|
360
|
+
((this.warmWorkers >= this.workers.size || !this.options.warmWorkers) &&
|
|
361
|
+
this.options.maxConcurrentWorkers > 0));
|
|
362
|
+
}
|
|
363
|
+
createReverseHandle(fn) {
|
|
364
|
+
let handle = new Handle_1.default({ fn });
|
|
365
|
+
this.handles.set(handle.id, handle);
|
|
366
|
+
return handle;
|
|
367
|
+
}
|
|
368
|
+
createSharedReference(value, isCacheable = true) {
|
|
369
|
+
let ref = referenceId++;
|
|
370
|
+
this.sharedReferences.set(ref, value);
|
|
371
|
+
this.sharedReferencesByValue.set(value, ref);
|
|
372
|
+
if (!isCacheable) {
|
|
373
|
+
this.serializedSharedReferences.set(ref, null);
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
ref,
|
|
377
|
+
dispose: () => {
|
|
378
|
+
this.sharedReferences.delete(ref);
|
|
379
|
+
this.sharedReferencesByValue.delete(value);
|
|
380
|
+
this.serializedSharedReferences.delete(ref);
|
|
381
|
+
let promises = [];
|
|
382
|
+
for (let worker of this.workers.values()) {
|
|
383
|
+
if (!worker.sentSharedReferences.has(ref)) {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
worker.sentSharedReferences.delete(ref);
|
|
387
|
+
promises.push(new Promise((resolve, reject) => {
|
|
388
|
+
worker.call({
|
|
389
|
+
method: 'deleteSharedReference',
|
|
390
|
+
args: [ref],
|
|
391
|
+
resolve,
|
|
392
|
+
reject,
|
|
393
|
+
skipReadyCheck: true,
|
|
394
|
+
retries: 0,
|
|
395
|
+
});
|
|
396
|
+
}));
|
|
397
|
+
}
|
|
398
|
+
return Promise.all(promises);
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
getSerializedSharedReference(ref) {
|
|
403
|
+
let cached = this.serializedSharedReferences.get(ref);
|
|
404
|
+
if (cached) {
|
|
405
|
+
return cached;
|
|
406
|
+
}
|
|
407
|
+
let value = this.sharedReferences.get(ref);
|
|
408
|
+
let buf = (0, build_cache_1.serialize)(value).buffer;
|
|
409
|
+
// If the reference was created with the isCacheable option set to false,
|
|
410
|
+
// serializedSharedReferences will contain `null` as the value.
|
|
411
|
+
if (cached !== null) {
|
|
412
|
+
// @ts-expect-error TS2345
|
|
413
|
+
this.serializedSharedReferences.set(ref, buf);
|
|
414
|
+
}
|
|
415
|
+
// @ts-expect-error TS2322
|
|
416
|
+
return buf;
|
|
417
|
+
}
|
|
418
|
+
async startProfile() {
|
|
419
|
+
let promises = [];
|
|
420
|
+
for (let worker of this.workers.values()) {
|
|
421
|
+
promises.push(new Promise((resolve, reject) => {
|
|
422
|
+
worker.call({
|
|
423
|
+
method: 'startProfile',
|
|
424
|
+
args: [],
|
|
425
|
+
resolve,
|
|
426
|
+
reject,
|
|
427
|
+
retries: 0,
|
|
428
|
+
skipReadyCheck: true,
|
|
429
|
+
});
|
|
430
|
+
}));
|
|
431
|
+
}
|
|
432
|
+
this.profiler = new profiler_1.SamplingProfiler();
|
|
433
|
+
promises.push(this.profiler.startProfiling());
|
|
434
|
+
await Promise.all(promises);
|
|
435
|
+
}
|
|
436
|
+
async endProfile() {
|
|
437
|
+
if (!this.profiler) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
let promises = [this.profiler.stopProfiling()];
|
|
441
|
+
let names = ['Master'];
|
|
442
|
+
for (let worker of this.workers.values()) {
|
|
443
|
+
names.push('Worker ' + worker.id);
|
|
444
|
+
promises.push(new Promise((resolve, reject) => {
|
|
445
|
+
worker.call({
|
|
446
|
+
method: 'endProfile',
|
|
447
|
+
args: [],
|
|
448
|
+
resolve,
|
|
449
|
+
reject,
|
|
450
|
+
retries: 0,
|
|
451
|
+
skipReadyCheck: true,
|
|
452
|
+
});
|
|
453
|
+
}));
|
|
454
|
+
}
|
|
455
|
+
var profiles = await Promise.all(promises);
|
|
456
|
+
let trace = new profiler_1.Trace();
|
|
457
|
+
let filename = `profile-${getTimeId()}.trace`;
|
|
458
|
+
let stream = trace.pipe(fs_1.default.createWriteStream(filename));
|
|
459
|
+
for (let profile of profiles) {
|
|
460
|
+
// @ts-expect-error TS2345
|
|
461
|
+
trace.addCPUProfile(names.shift(), profile);
|
|
462
|
+
}
|
|
463
|
+
trace.flush();
|
|
464
|
+
await new Promise((resolve) => {
|
|
465
|
+
stream.once('finish', resolve);
|
|
466
|
+
});
|
|
467
|
+
logger_1.default.info({
|
|
468
|
+
origin: '@atlaspack/workers',
|
|
469
|
+
message: (0, diagnostic_1.md) `Wrote profile to ${filename}`,
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
async callAllWorkers(method, args) {
|
|
473
|
+
let promises = [];
|
|
474
|
+
for (let worker of this.workers.values()) {
|
|
475
|
+
promises.push(new Promise((resolve, reject) => {
|
|
476
|
+
worker.call({
|
|
477
|
+
method,
|
|
478
|
+
args,
|
|
479
|
+
resolve,
|
|
480
|
+
reject,
|
|
481
|
+
retries: 0,
|
|
482
|
+
});
|
|
483
|
+
}));
|
|
484
|
+
}
|
|
485
|
+
promises.push(this.localWorker[method](this.workerApi, ...args));
|
|
486
|
+
await Promise.all(promises);
|
|
487
|
+
}
|
|
488
|
+
async takeHeapSnapshot() {
|
|
489
|
+
let snapshotId = getTimeId();
|
|
490
|
+
try {
|
|
491
|
+
let snapshotPaths = await Promise.all([...this.workers.values()].map((worker) => new Promise((resolve, reject) => {
|
|
492
|
+
worker.call({
|
|
493
|
+
method: 'takeHeapSnapshot',
|
|
494
|
+
args: [snapshotId],
|
|
495
|
+
resolve,
|
|
496
|
+
reject,
|
|
497
|
+
retries: 0,
|
|
498
|
+
skipReadyCheck: true,
|
|
499
|
+
});
|
|
500
|
+
})));
|
|
501
|
+
logger_1.default.info({
|
|
502
|
+
origin: '@atlaspack/workers',
|
|
503
|
+
message: (0, diagnostic_1.md) `Wrote heap snapshots to the following paths:\n${snapshotPaths.join('\n')}`,
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
catch {
|
|
507
|
+
logger_1.default.error({
|
|
508
|
+
origin: '@atlaspack/workers',
|
|
509
|
+
message: 'Unable to take heap snapshots. Note: requires Node 11.13.0+',
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
static getNumWorkers() {
|
|
514
|
+
return process.env.ATLASPACK_WORKERS
|
|
515
|
+
? parseInt(process.env.ATLASPACK_WORKERS, 10)
|
|
516
|
+
: Math.min(4, Math.ceil((0, cpuCount_1.default)() / 2));
|
|
517
|
+
}
|
|
518
|
+
static isWorker() {
|
|
519
|
+
return !!childState_1.child;
|
|
520
|
+
}
|
|
521
|
+
static getWorkerApi() {
|
|
522
|
+
(0, assert_1.default)(childState_1.child != null, 'WorkerFarm.getWorkerApi can only be called within workers');
|
|
523
|
+
return childState_1.child.workerApi;
|
|
524
|
+
}
|
|
525
|
+
static getConcurrentCallsPerWorker(defaultValue = DEFAULT_MAX_CONCURRENT_CALLS) {
|
|
526
|
+
return (
|
|
527
|
+
// @ts-expect-error TS2345
|
|
528
|
+
parseInt(process.env.ATLASPACK_MAX_CONCURRENT_CALLS, 10) || defaultValue);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
exports.default = WorkerFarm;
|
|
532
|
+
function getTimeId() {
|
|
533
|
+
let now = new Date();
|
|
534
|
+
return (String(now.getFullYear()) +
|
|
535
|
+
String(now.getMonth() + 1).padStart(2, '0') +
|
|
536
|
+
String(now.getDate()).padStart(2, '0') +
|
|
537
|
+
'-' +
|
|
538
|
+
String(now.getHours()).padStart(2, '0') +
|
|
539
|
+
String(now.getMinutes()).padStart(2, '0') +
|
|
540
|
+
String(now.getSeconds()).padStart(2, '0'));
|
|
541
|
+
}
|
package/dist/backend.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectBackend = detectBackend;
|
|
4
|
+
exports.getWorkerBackend = getWorkerBackend;
|
|
5
|
+
// /flow-to-ts helpers
|
|
6
|
+
function detectBackend() {
|
|
7
|
+
// @ts-expect-error TS2339
|
|
8
|
+
if (process.browser)
|
|
9
|
+
return 'web';
|
|
10
|
+
switch (process.env.ATLASPACK_WORKER_BACKEND) {
|
|
11
|
+
case 'threads':
|
|
12
|
+
case 'process':
|
|
13
|
+
return process.env.ATLASPACK_WORKER_BACKEND;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
require('worker_threads');
|
|
17
|
+
return 'threads';
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
return 'process';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function getWorkerBackend(backend) {
|
|
24
|
+
switch (backend) {
|
|
25
|
+
case 'threads':
|
|
26
|
+
return require('./threads/ThreadsWorker').default;
|
|
27
|
+
case 'process':
|
|
28
|
+
return require('./process/ProcessWorker').default;
|
|
29
|
+
case 'web':
|
|
30
|
+
return require('./web/WebWorker').default;
|
|
31
|
+
default:
|
|
32
|
+
throw new Error(`Invalid backend: ${backend}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
package/dist/bus.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const events_1 = __importDefault(require("events"));
|
|
7
|
+
const childState_1 = require("./childState");
|
|
8
|
+
class Bus extends events_1.default {
|
|
9
|
+
emit(event, ...args) {
|
|
10
|
+
if (childState_1.child) {
|
|
11
|
+
childState_1.child.workerApi.callMaster({
|
|
12
|
+
// @ts-expect-error TS2339
|
|
13
|
+
location: process.browser ? '@atlaspack/workers/bus' : __filename,
|
|
14
|
+
method: 'emit',
|
|
15
|
+
args: [event, ...args],
|
|
16
|
+
}, false);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
return super.emit(event, ...args);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.default = new Bus();
|