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