@parcel/workers 2.0.0-nightly.122 → 2.0.0-nightly.1222
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/index.d.ts +22 -0
- package/lib/Handle.js +14 -45
- package/lib/Profiler.js +20 -10
- package/lib/Trace.js +16 -11
- package/lib/Worker.js +97 -26
- package/lib/WorkerFarm.js +247 -102
- package/lib/bus.js +10 -2
- package/lib/child.js +113 -63
- package/lib/childState.js +1 -1
- package/lib/cpuCount.js +28 -7
- package/lib/index.js +26 -10
- package/lib/process/ProcessChild.js +22 -11
- package/lib/process/ProcessWorker.js +30 -19
- package/lib/threads/ThreadsChild.js +32 -14
- package/lib/threads/ThreadsWorker.js +28 -16
- package/package.json +18 -7
- package/src/Handle.js +10 -39
- package/src/Profiler.js +1 -1
- package/src/Trace.js +8 -4
- package/src/Worker.js +75 -11
- package/src/WorkerFarm.js +210 -50
- package/src/bus.js +1 -1
- package/src/child.js +70 -21
- package/src/cpuCount.js +9 -4
- package/src/index.js +1 -1
- package/src/process/ProcessChild.js +2 -1
- package/src/process/ProcessWorker.js +1 -1
- package/src/threads/ThreadsWorker.js +2 -2
- package/test/cpuCount.test.js +1 -1
- package/test/integration/workerfarm/console.js +1 -1
- package/test/integration/workerfarm/logging.js +1 -1
- package/test/integration/workerfarm/reverse-handle.js +2 -2
- package/test/workerfarm.js +5 -5
package/src/WorkerFarm.js
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
restoreDeserializedObject,
|
|
21
21
|
serialize,
|
|
22
22
|
} from '@parcel/core';
|
|
23
|
-
import ThrowableDiagnostic, {anyToDiagnostic} from '@parcel/diagnostic';
|
|
23
|
+
import ThrowableDiagnostic, {anyToDiagnostic, md} from '@parcel/diagnostic';
|
|
24
24
|
import Worker, {type WorkerCall} from './Worker';
|
|
25
25
|
import cpuCount from './cpuCount';
|
|
26
26
|
import Handle from './Handle';
|
|
@@ -31,9 +31,10 @@ import Trace from './Trace';
|
|
|
31
31
|
import fs from 'fs';
|
|
32
32
|
import logger from '@parcel/logger';
|
|
33
33
|
|
|
34
|
-
let profileId = 1;
|
|
35
34
|
let referenceId = 1;
|
|
36
35
|
|
|
36
|
+
export opaque type SharedReference = number;
|
|
37
|
+
|
|
37
38
|
export type FarmOptions = {|
|
|
38
39
|
maxConcurrentWorkers: number,
|
|
39
40
|
maxConcurrentCallsPerWorker: number,
|
|
@@ -42,7 +43,7 @@ export type FarmOptions = {|
|
|
|
42
43
|
warmWorkers: boolean,
|
|
43
44
|
workerPath?: FilePath,
|
|
44
45
|
backend: BackendType,
|
|
45
|
-
|
|
46
|
+
shouldPatchConsole?: boolean,
|
|
46
47
|
|};
|
|
47
48
|
|
|
48
49
|
type WorkerModule = {|
|
|
@@ -52,8 +53,8 @@ type WorkerModule = {|
|
|
|
52
53
|
export type WorkerApi = {|
|
|
53
54
|
callMaster(CallRequest, ?boolean): Promise<mixed>,
|
|
54
55
|
createReverseHandle(fn: HandleFunction): Handle,
|
|
55
|
-
getSharedReference(ref:
|
|
56
|
-
resolveSharedReference(value: mixed): ?
|
|
56
|
+
getSharedReference(ref: SharedReference): mixed,
|
|
57
|
+
resolveSharedReference(value: mixed): ?SharedReference,
|
|
57
58
|
callChild?: (childId: number, request: HandleCallRequest) => Promise<mixed>,
|
|
58
59
|
|};
|
|
59
60
|
|
|
@@ -67,13 +68,15 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
67
68
|
callQueue: Array<WorkerCall> = [];
|
|
68
69
|
ending: boolean = false;
|
|
69
70
|
localWorker: WorkerModule;
|
|
71
|
+
localWorkerInit: ?Promise<void>;
|
|
70
72
|
options: FarmOptions;
|
|
71
73
|
run: HandleFunction;
|
|
72
74
|
warmWorkers: number = 0;
|
|
73
75
|
workers: Map<number, Worker> = new Map();
|
|
74
76
|
handles: Map<number, Handle> = new Map();
|
|
75
|
-
sharedReferences: Map<
|
|
76
|
-
sharedReferencesByValue: Map<mixed,
|
|
77
|
+
sharedReferences: Map<SharedReference, mixed> = new Map();
|
|
78
|
+
sharedReferencesByValue: Map<mixed, SharedReference> = new Map();
|
|
79
|
+
serializedSharedReferences: Map<SharedReference, ?ArrayBuffer> = new Map();
|
|
77
80
|
profiler: ?Profiler;
|
|
78
81
|
|
|
79
82
|
constructor(farmOptions: $Shape<FarmOptions> = {}) {
|
|
@@ -94,12 +97,39 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
94
97
|
|
|
95
98
|
// $FlowFixMe this must be dynamic
|
|
96
99
|
this.localWorker = require(this.options.workerPath);
|
|
100
|
+
this.localWorkerInit =
|
|
101
|
+
this.localWorker.childInit != null ? this.localWorker.childInit() : null;
|
|
97
102
|
this.run = this.createHandle('run');
|
|
98
103
|
|
|
104
|
+
// Worker thread stdout is by default piped into the process stdout, if there are enough worker
|
|
105
|
+
// threads to exceed the default listener limit, then anything else piping into stdout will trigger
|
|
106
|
+
// the `MaxListenersExceededWarning`, so we should ensure the max listeners is at least equal to the
|
|
107
|
+
// number of workers + 1 for the main thread.
|
|
108
|
+
//
|
|
109
|
+
// Note this can't be fixed easily where other things pipe into stdout - even after starting > 10 worker
|
|
110
|
+
// threads `process.stdout.getMaxListeners()` will still return 10, however adding another pipe into `stdout`
|
|
111
|
+
// will give the warning with `<worker count + 1>` as the number of listeners.
|
|
112
|
+
process.stdout.setMaxListeners(
|
|
113
|
+
Math.max(
|
|
114
|
+
process.stdout.getMaxListeners(),
|
|
115
|
+
WorkerFarm.getNumWorkers() + 1,
|
|
116
|
+
),
|
|
117
|
+
);
|
|
118
|
+
|
|
99
119
|
this.startMaxWorkers();
|
|
100
120
|
}
|
|
101
121
|
|
|
102
|
-
workerApi
|
|
122
|
+
workerApi: {|
|
|
123
|
+
callChild: (childId: number, request: HandleCallRequest) => Promise<mixed>,
|
|
124
|
+
callMaster: (
|
|
125
|
+
request: CallRequest,
|
|
126
|
+
awaitResponse?: ?boolean,
|
|
127
|
+
) => Promise<mixed>,
|
|
128
|
+
createReverseHandle: (fn: HandleFunction) => Handle,
|
|
129
|
+
getSharedReference: (ref: SharedReference) => mixed,
|
|
130
|
+
resolveSharedReference: (value: mixed) => void | SharedReference,
|
|
131
|
+
runHandle: (handle: Handle, args: Array<any>) => Promise<mixed>,
|
|
132
|
+
|} = {
|
|
103
133
|
callMaster: async (
|
|
104
134
|
request: CallRequest,
|
|
105
135
|
awaitResponse: ?boolean = true,
|
|
@@ -122,7 +152,13 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
122
152
|
retries: 0,
|
|
123
153
|
});
|
|
124
154
|
}),
|
|
125
|
-
|
|
155
|
+
runHandle: (handle: Handle, args: Array<any>): Promise<mixed> =>
|
|
156
|
+
this.workerApi.callChild(nullthrows(handle.childId), {
|
|
157
|
+
handle: handle.id,
|
|
158
|
+
args,
|
|
159
|
+
}),
|
|
160
|
+
getSharedReference: (ref: SharedReference) =>
|
|
161
|
+
this.sharedReferences.get(ref),
|
|
126
162
|
resolveSharedReference: (value: mixed) =>
|
|
127
163
|
this.sharedReferencesByValue.get(value),
|
|
128
164
|
};
|
|
@@ -155,30 +191,42 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
155
191
|
);
|
|
156
192
|
}
|
|
157
193
|
|
|
158
|
-
createHandle(method: string): HandleFunction {
|
|
159
|
-
return (...args) => {
|
|
194
|
+
createHandle(method: string, useMainThread: boolean = false): HandleFunction {
|
|
195
|
+
return async (...args) => {
|
|
160
196
|
// Child process workers are slow to start (~600ms).
|
|
161
197
|
// While we're waiting, just run on the main thread.
|
|
162
198
|
// This significantly speeds up startup time.
|
|
163
|
-
if (this.shouldUseRemoteWorkers()) {
|
|
199
|
+
if (this.shouldUseRemoteWorkers() && !useMainThread) {
|
|
164
200
|
return this.addCall(method, [...args, false]);
|
|
165
201
|
} else {
|
|
166
202
|
if (this.options.warmWorkers && this.shouldStartRemoteWorkers()) {
|
|
167
203
|
this.warmupWorker(method, args);
|
|
168
204
|
}
|
|
169
205
|
|
|
170
|
-
let processedArgs
|
|
171
|
-
|
|
172
|
-
|
|
206
|
+
let processedArgs;
|
|
207
|
+
if (!useMainThread) {
|
|
208
|
+
processedArgs = restoreDeserializedObject(
|
|
209
|
+
prepareForSerialization([...args, false]),
|
|
210
|
+
);
|
|
211
|
+
} else {
|
|
212
|
+
processedArgs = args;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (this.localWorkerInit != null) {
|
|
216
|
+
await this.localWorkerInit;
|
|
217
|
+
this.localWorkerInit = null;
|
|
218
|
+
}
|
|
173
219
|
return this.localWorker[method](this.workerApi, ...processedArgs);
|
|
174
220
|
}
|
|
175
221
|
};
|
|
176
222
|
}
|
|
177
223
|
|
|
178
|
-
onError(error: ErrorWithCode, worker: Worker) {
|
|
224
|
+
onError(error: ErrorWithCode, worker: Worker): void | Promise<void> {
|
|
179
225
|
// Handle ipc errors
|
|
180
226
|
if (error.code === 'ERR_IPC_CHANNEL_CLOSED') {
|
|
181
227
|
return this.stopWorker(worker);
|
|
228
|
+
} else {
|
|
229
|
+
logger.error(error, '@parcel/workers');
|
|
182
230
|
}
|
|
183
231
|
}
|
|
184
232
|
|
|
@@ -186,7 +234,8 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
186
234
|
let worker = new Worker({
|
|
187
235
|
forcedKillTime: this.options.forcedKillTime,
|
|
188
236
|
backend: this.options.backend,
|
|
189
|
-
|
|
237
|
+
shouldPatchConsole: this.options.shouldPatchConsole,
|
|
238
|
+
sharedReferences: this.sharedReferences,
|
|
190
239
|
});
|
|
191
240
|
|
|
192
241
|
worker.fork(nullthrows(this.options.workerPath));
|
|
@@ -231,7 +280,11 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
231
280
|
this.startChild();
|
|
232
281
|
}
|
|
233
282
|
|
|
234
|
-
|
|
283
|
+
let workers = [...this.workers.values()].sort(
|
|
284
|
+
(a, b) => a.calls.size - b.calls.size,
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
for (let worker of workers) {
|
|
235
288
|
if (!this.callQueue.length) {
|
|
236
289
|
break;
|
|
237
290
|
}
|
|
@@ -241,11 +294,24 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
241
294
|
}
|
|
242
295
|
|
|
243
296
|
if (worker.calls.size < this.options.maxConcurrentCallsPerWorker) {
|
|
244
|
-
|
|
297
|
+
this.callWorker(worker, this.callQueue.shift());
|
|
245
298
|
}
|
|
246
299
|
}
|
|
247
300
|
}
|
|
248
301
|
|
|
302
|
+
async callWorker(worker: Worker, call: WorkerCall): Promise<void> {
|
|
303
|
+
for (let ref of this.sharedReferences.keys()) {
|
|
304
|
+
if (!worker.sentSharedReferences.has(ref)) {
|
|
305
|
+
await worker.sendSharedReference(
|
|
306
|
+
ref,
|
|
307
|
+
this.getSerializedSharedReference(ref),
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
worker.call(call);
|
|
313
|
+
}
|
|
314
|
+
|
|
249
315
|
async processRequest(
|
|
250
316
|
data: {|
|
|
251
317
|
location: FilePath,
|
|
@@ -255,7 +321,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
255
321
|
let {method, args, location, awaitResponse, idx, handle: handleId} = data;
|
|
256
322
|
let mod;
|
|
257
323
|
if (handleId != null) {
|
|
258
|
-
mod = nullthrows(this.handles.get(handleId))
|
|
324
|
+
mod = nullthrows(this.handles.get(handleId)?.fn);
|
|
259
325
|
} else if (location) {
|
|
260
326
|
// $FlowFixMe this must be dynamic
|
|
261
327
|
mod = require(location);
|
|
@@ -286,7 +352,6 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
286
352
|
}
|
|
287
353
|
} else {
|
|
288
354
|
// ESModule default interop
|
|
289
|
-
// $FlowFixMe
|
|
290
355
|
if (mod.__esModule && !mod[method] && mod.default) {
|
|
291
356
|
mod = mod.default;
|
|
292
357
|
}
|
|
@@ -331,6 +396,10 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
331
396
|
async end(): Promise<void> {
|
|
332
397
|
this.ending = true;
|
|
333
398
|
|
|
399
|
+
await Promise.all(
|
|
400
|
+
Array.from(this.workers.values()).map(worker => this.stopWorker(worker)),
|
|
401
|
+
);
|
|
402
|
+
|
|
334
403
|
for (let handle of this.handles.values()) {
|
|
335
404
|
handle.dispose();
|
|
336
405
|
}
|
|
@@ -338,9 +407,6 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
338
407
|
this.sharedReferences = new Map();
|
|
339
408
|
this.sharedReferencesByValue = new Map();
|
|
340
409
|
|
|
341
|
-
await Promise.all(
|
|
342
|
-
Array.from(this.workers.values()).map(worker => this.stopWorker(worker)),
|
|
343
|
-
);
|
|
344
410
|
this.ending = false;
|
|
345
411
|
}
|
|
346
412
|
|
|
@@ -362,40 +428,37 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
362
428
|
);
|
|
363
429
|
}
|
|
364
430
|
|
|
365
|
-
createReverseHandle(fn: HandleFunction) {
|
|
366
|
-
let handle = new Handle({fn
|
|
431
|
+
createReverseHandle(fn: HandleFunction): Handle {
|
|
432
|
+
let handle = new Handle({fn});
|
|
367
433
|
this.handles.set(handle.id, handle);
|
|
368
434
|
return handle;
|
|
369
435
|
}
|
|
370
436
|
|
|
371
|
-
|
|
437
|
+
createSharedReference(
|
|
438
|
+
value: mixed,
|
|
439
|
+
isCacheable: boolean = true,
|
|
440
|
+
): {|ref: SharedReference, dispose(): Promise<mixed>|} {
|
|
372
441
|
let ref = referenceId++;
|
|
373
442
|
this.sharedReferences.set(ref, value);
|
|
374
443
|
this.sharedReferencesByValue.set(value, ref);
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
promises.push(
|
|
378
|
-
new Promise((resolve, reject) => {
|
|
379
|
-
worker.call({
|
|
380
|
-
method: 'createSharedReference',
|
|
381
|
-
args: [ref, value],
|
|
382
|
-
resolve,
|
|
383
|
-
reject,
|
|
384
|
-
retries: 0,
|
|
385
|
-
});
|
|
386
|
-
}),
|
|
387
|
-
);
|
|
444
|
+
if (!isCacheable) {
|
|
445
|
+
this.serializedSharedReferences.set(ref, null);
|
|
388
446
|
}
|
|
389
447
|
|
|
390
|
-
await Promise.all(promises);
|
|
391
|
-
|
|
392
448
|
return {
|
|
393
449
|
ref,
|
|
394
450
|
dispose: () => {
|
|
395
451
|
this.sharedReferences.delete(ref);
|
|
396
452
|
this.sharedReferencesByValue.delete(value);
|
|
453
|
+
this.serializedSharedReferences.delete(ref);
|
|
454
|
+
|
|
397
455
|
let promises = [];
|
|
398
456
|
for (let worker of this.workers.values()) {
|
|
457
|
+
if (!worker.sentSharedReferences.has(ref)) {
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
worker.sentSharedReferences.delete(ref);
|
|
399
462
|
promises.push(
|
|
400
463
|
new Promise((resolve, reject) => {
|
|
401
464
|
worker.call({
|
|
@@ -403,6 +466,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
403
466
|
args: [ref],
|
|
404
467
|
resolve,
|
|
405
468
|
reject,
|
|
469
|
+
skipReadyCheck: true,
|
|
406
470
|
retries: 0,
|
|
407
471
|
});
|
|
408
472
|
}),
|
|
@@ -413,6 +477,24 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
413
477
|
};
|
|
414
478
|
}
|
|
415
479
|
|
|
480
|
+
getSerializedSharedReference(ref: SharedReference): ArrayBuffer {
|
|
481
|
+
let cached = this.serializedSharedReferences.get(ref);
|
|
482
|
+
if (cached) {
|
|
483
|
+
return cached;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
let value = this.sharedReferences.get(ref);
|
|
487
|
+
let buf = serialize(value).buffer;
|
|
488
|
+
|
|
489
|
+
// If the reference was created with the isCacheable option set to false,
|
|
490
|
+
// serializedSharedReferences will contain `null` as the value.
|
|
491
|
+
if (cached !== null) {
|
|
492
|
+
this.serializedSharedReferences.set(ref, buf);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return buf;
|
|
496
|
+
}
|
|
497
|
+
|
|
416
498
|
async startProfile() {
|
|
417
499
|
let promises = [];
|
|
418
500
|
for (let worker of this.workers.values()) {
|
|
@@ -424,6 +506,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
424
506
|
resolve,
|
|
425
507
|
reject,
|
|
426
508
|
retries: 0,
|
|
509
|
+
skipReadyCheck: true,
|
|
427
510
|
});
|
|
428
511
|
}),
|
|
429
512
|
);
|
|
@@ -453,6 +536,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
453
536
|
resolve,
|
|
454
537
|
reject,
|
|
455
538
|
retries: 0,
|
|
539
|
+
skipReadyCheck: true,
|
|
456
540
|
});
|
|
457
541
|
}),
|
|
458
542
|
);
|
|
@@ -460,7 +544,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
460
544
|
|
|
461
545
|
var profiles = await Promise.all(promises);
|
|
462
546
|
let trace = new Trace();
|
|
463
|
-
let filename = `profile-${
|
|
547
|
+
let filename = `profile-${getTimeId()}.trace`;
|
|
464
548
|
let stream = trace.pipe(fs.createWriteStream(filename));
|
|
465
549
|
|
|
466
550
|
for (let profile of profiles) {
|
|
@@ -474,21 +558,84 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
474
558
|
|
|
475
559
|
logger.info({
|
|
476
560
|
origin: '@parcel/workers',
|
|
477
|
-
message: `Wrote profile to ${filename}`,
|
|
561
|
+
message: md`Wrote profile to ${filename}`,
|
|
478
562
|
});
|
|
479
563
|
}
|
|
480
564
|
|
|
481
|
-
|
|
565
|
+
async callAllWorkers(method: string, args: Array<any>) {
|
|
566
|
+
let promises = [];
|
|
567
|
+
for (let worker of this.workers.values()) {
|
|
568
|
+
promises.push(
|
|
569
|
+
new Promise((resolve, reject) => {
|
|
570
|
+
worker.call({
|
|
571
|
+
method,
|
|
572
|
+
args,
|
|
573
|
+
resolve,
|
|
574
|
+
reject,
|
|
575
|
+
retries: 0,
|
|
576
|
+
});
|
|
577
|
+
}),
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
promises.push(this.localWorker[method](this.workerApi, ...args));
|
|
582
|
+
await Promise.all(promises);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
async takeHeapSnapshot() {
|
|
586
|
+
let snapshotId = getTimeId();
|
|
587
|
+
|
|
588
|
+
try {
|
|
589
|
+
let snapshotPaths = await Promise.all(
|
|
590
|
+
[...this.workers.values()].map(
|
|
591
|
+
worker =>
|
|
592
|
+
new Promise((resolve, reject) => {
|
|
593
|
+
worker.call({
|
|
594
|
+
method: 'takeHeapSnapshot',
|
|
595
|
+
args: [snapshotId],
|
|
596
|
+
resolve,
|
|
597
|
+
reject,
|
|
598
|
+
retries: 0,
|
|
599
|
+
skipReadyCheck: true,
|
|
600
|
+
});
|
|
601
|
+
}),
|
|
602
|
+
),
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
logger.info({
|
|
606
|
+
origin: '@parcel/workers',
|
|
607
|
+
message: md`Wrote heap snapshots to the following paths:\n${snapshotPaths.join(
|
|
608
|
+
'\n',
|
|
609
|
+
)}`,
|
|
610
|
+
});
|
|
611
|
+
} catch {
|
|
612
|
+
logger.error({
|
|
613
|
+
origin: '@parcel/workers',
|
|
614
|
+
message: 'Unable to take heap snapshots. Note: requires Node 11.13.0+',
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
static getNumWorkers(): number {
|
|
482
620
|
return process.env.PARCEL_WORKERS
|
|
483
621
|
? parseInt(process.env.PARCEL_WORKERS, 10)
|
|
484
|
-
: cpuCount();
|
|
622
|
+
: Math.ceil(cpuCount() / 2);
|
|
485
623
|
}
|
|
486
624
|
|
|
487
|
-
static isWorker() {
|
|
625
|
+
static isWorker(): boolean {
|
|
488
626
|
return !!child;
|
|
489
627
|
}
|
|
490
628
|
|
|
491
|
-
static getWorkerApi() {
|
|
629
|
+
static getWorkerApi(): {|
|
|
630
|
+
callMaster: (
|
|
631
|
+
request: CallRequest,
|
|
632
|
+
awaitResponse?: ?boolean,
|
|
633
|
+
) => Promise<mixed>,
|
|
634
|
+
createReverseHandle: (fn: (...args: Array<any>) => mixed) => Handle,
|
|
635
|
+
getSharedReference: (ref: SharedReference) => mixed,
|
|
636
|
+
resolveSharedReference: (value: mixed) => void | SharedReference,
|
|
637
|
+
runHandle: (handle: Handle, args: Array<any>) => Promise<mixed>,
|
|
638
|
+
|} {
|
|
492
639
|
invariant(
|
|
493
640
|
child != null,
|
|
494
641
|
'WorkerFarm.getWorkerApi can only be called within workers',
|
|
@@ -496,7 +643,20 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
496
643
|
return child.workerApi;
|
|
497
644
|
}
|
|
498
645
|
|
|
499
|
-
static getConcurrentCallsPerWorker() {
|
|
500
|
-
return parseInt(process.env.PARCEL_MAX_CONCURRENT_CALLS, 10) ||
|
|
646
|
+
static getConcurrentCallsPerWorker(): number {
|
|
647
|
+
return parseInt(process.env.PARCEL_MAX_CONCURRENT_CALLS, 10) || 30;
|
|
501
648
|
}
|
|
502
649
|
}
|
|
650
|
+
|
|
651
|
+
function getTimeId() {
|
|
652
|
+
let now = new Date();
|
|
653
|
+
return (
|
|
654
|
+
String(now.getFullYear()) +
|
|
655
|
+
String(now.getMonth() + 1).padStart(2, '0') +
|
|
656
|
+
String(now.getDate()).padStart(2, '0') +
|
|
657
|
+
'-' +
|
|
658
|
+
String(now.getHours()).padStart(2, '0') +
|
|
659
|
+
String(now.getMinutes()).padStart(2, '0') +
|
|
660
|
+
String(now.getSeconds()).padStart(2, '0')
|
|
661
|
+
);
|
|
662
|
+
}
|
package/src/bus.js
CHANGED
package/src/child.js
CHANGED
|
@@ -9,16 +9,20 @@ import type {
|
|
|
9
9
|
WorkerResponse,
|
|
10
10
|
ChildImpl,
|
|
11
11
|
} from './types';
|
|
12
|
-
import type {IDisposable} from '@parcel/types';
|
|
13
|
-
import type {
|
|
12
|
+
import type {Async, IDisposable} from '@parcel/types';
|
|
13
|
+
import type {SharedReference} from './WorkerFarm';
|
|
14
14
|
|
|
15
15
|
import invariant from 'assert';
|
|
16
16
|
import nullthrows from 'nullthrows';
|
|
17
17
|
import Logger, {patchConsole, unpatchConsole} from '@parcel/logger';
|
|
18
18
|
import ThrowableDiagnostic, {anyToDiagnostic} from '@parcel/diagnostic';
|
|
19
|
+
import {deserialize} from '@parcel/core';
|
|
19
20
|
import bus from './bus';
|
|
20
21
|
import Profiler from './Profiler';
|
|
21
|
-
import
|
|
22
|
+
import _Handle from './Handle';
|
|
23
|
+
|
|
24
|
+
// The import of './Handle' should really be imported eagerly (with @babel/plugin-transform-modules-commonjs's lazy mode).
|
|
25
|
+
const Handle = _Handle;
|
|
22
26
|
|
|
23
27
|
type ChildCall = WorkerRequest & {|
|
|
24
28
|
resolve: (result: Promise<any> | any) => void,
|
|
@@ -30,20 +34,21 @@ export class Child {
|
|
|
30
34
|
childId: ?number;
|
|
31
35
|
maxConcurrentCalls: number = 10;
|
|
32
36
|
module: ?any;
|
|
33
|
-
responseId = 0;
|
|
37
|
+
responseId: number = 0;
|
|
34
38
|
responseQueue: Map<number, ChildCall> = new Map();
|
|
35
39
|
loggerDisposable: IDisposable;
|
|
36
40
|
child: ChildImpl;
|
|
37
41
|
profiler: ?Profiler;
|
|
38
|
-
workerApi: WorkerApi;
|
|
39
42
|
handles: Map<number, Handle> = new Map();
|
|
40
|
-
sharedReferences: Map<
|
|
41
|
-
sharedReferencesByValue: Map<mixed,
|
|
43
|
+
sharedReferences: Map<SharedReference, mixed> = new Map();
|
|
44
|
+
sharedReferencesByValue: Map<mixed, SharedReference> = new Map();
|
|
42
45
|
|
|
43
46
|
constructor(ChildBackend: Class<ChildImpl>) {
|
|
44
47
|
this.child = new ChildBackend(
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
m => {
|
|
49
|
+
this.messageListener(m);
|
|
50
|
+
},
|
|
51
|
+
() => this.handleEnd(),
|
|
47
52
|
);
|
|
48
53
|
|
|
49
54
|
// Monitior all logging events inside this child process and forward to
|
|
@@ -53,19 +58,31 @@ export class Child {
|
|
|
53
58
|
});
|
|
54
59
|
}
|
|
55
60
|
|
|
56
|
-
workerApi
|
|
61
|
+
workerApi: {|
|
|
62
|
+
callMaster: (
|
|
63
|
+
request: CallRequest,
|
|
64
|
+
awaitResponse?: ?boolean,
|
|
65
|
+
) => Promise<mixed>,
|
|
66
|
+
createReverseHandle: (fn: (...args: Array<any>) => mixed) => Handle,
|
|
67
|
+
getSharedReference: (ref: SharedReference) => mixed,
|
|
68
|
+
resolveSharedReference: (value: mixed) => void | SharedReference,
|
|
69
|
+
runHandle: (handle: Handle, args: Array<any>) => Promise<mixed>,
|
|
70
|
+
|} = {
|
|
57
71
|
callMaster: (
|
|
58
72
|
request: CallRequest,
|
|
59
73
|
awaitResponse: ?boolean = true,
|
|
60
74
|
): Promise<mixed> => this.addCall(request, awaitResponse),
|
|
61
75
|
createReverseHandle: (fn: (...args: Array<any>) => mixed): Handle =>
|
|
62
76
|
this.createReverseHandle(fn),
|
|
63
|
-
|
|
77
|
+
runHandle: (handle: Handle, args: Array<any>): Promise<mixed> =>
|
|
78
|
+
this.workerApi.callMaster({handle: handle.id, args}, true),
|
|
79
|
+
getSharedReference: (ref: SharedReference) =>
|
|
80
|
+
this.sharedReferences.get(ref),
|
|
64
81
|
resolveSharedReference: (value: mixed) =>
|
|
65
82
|
this.sharedReferencesByValue.get(value),
|
|
66
83
|
};
|
|
67
84
|
|
|
68
|
-
messageListener(message: WorkerMessage):
|
|
85
|
+
messageListener(message: WorkerMessage): Async<void> {
|
|
69
86
|
if (message.type === 'response') {
|
|
70
87
|
return this.handleResponse(message);
|
|
71
88
|
} else if (message.type === 'request') {
|
|
@@ -77,10 +94,14 @@ export class Child {
|
|
|
77
94
|
this.child.send(data);
|
|
78
95
|
}
|
|
79
96
|
|
|
80
|
-
childInit(module: string, childId: number): void {
|
|
97
|
+
async childInit(module: string, childId: number): Promise<void> {
|
|
81
98
|
// $FlowFixMe this must be dynamic
|
|
82
99
|
this.module = require(module);
|
|
83
100
|
this.childId = childId;
|
|
101
|
+
|
|
102
|
+
if (this.module.childInit != null) {
|
|
103
|
+
await this.module.childInit();
|
|
104
|
+
}
|
|
84
105
|
}
|
|
85
106
|
|
|
86
107
|
async handleRequest(data: WorkerRequest): Promise<void> {
|
|
@@ -106,7 +127,7 @@ export class Child {
|
|
|
106
127
|
let result;
|
|
107
128
|
if (handleId != null) {
|
|
108
129
|
try {
|
|
109
|
-
let fn = nullthrows(this.handles.get(handleId))
|
|
130
|
+
let fn = nullthrows(this.handles.get(handleId)?.fn);
|
|
110
131
|
result = responseFromContent(fn(...args));
|
|
111
132
|
} catch (e) {
|
|
112
133
|
result = errorResponseFromError(e);
|
|
@@ -114,13 +135,13 @@ export class Child {
|
|
|
114
135
|
} else if (method === 'childInit') {
|
|
115
136
|
try {
|
|
116
137
|
let [moduleName, childOptions] = args;
|
|
117
|
-
if (childOptions.
|
|
138
|
+
if (childOptions.shouldPatchConsole) {
|
|
118
139
|
patchConsole();
|
|
119
140
|
} else {
|
|
120
141
|
unpatchConsole();
|
|
121
142
|
}
|
|
122
143
|
|
|
123
|
-
result = responseFromContent(this.childInit(moduleName, child));
|
|
144
|
+
result = responseFromContent(await this.childInit(moduleName, child));
|
|
124
145
|
} catch (e) {
|
|
125
146
|
result = errorResponseFromError(e);
|
|
126
147
|
}
|
|
@@ -138,13 +159,37 @@ export class Child {
|
|
|
138
159
|
} catch (e) {
|
|
139
160
|
result = errorResponseFromError(e);
|
|
140
161
|
}
|
|
162
|
+
} else if (method === 'takeHeapSnapshot') {
|
|
163
|
+
try {
|
|
164
|
+
let v8 = require('v8');
|
|
165
|
+
result = responseFromContent(
|
|
166
|
+
v8.writeHeapSnapshot(
|
|
167
|
+
'heap-' +
|
|
168
|
+
args[0] +
|
|
169
|
+
'-' +
|
|
170
|
+
(this.childId ? 'worker' + this.childId : 'main') +
|
|
171
|
+
'.heapsnapshot',
|
|
172
|
+
),
|
|
173
|
+
);
|
|
174
|
+
} catch (e) {
|
|
175
|
+
result = errorResponseFromError(e);
|
|
176
|
+
}
|
|
141
177
|
} else if (method === 'createSharedReference') {
|
|
142
|
-
let [ref,
|
|
178
|
+
let [ref, _value] = args;
|
|
179
|
+
let value =
|
|
180
|
+
_value instanceof ArrayBuffer
|
|
181
|
+
? // In the case the value is pre-serialized as a buffer,
|
|
182
|
+
// deserialize it.
|
|
183
|
+
deserialize(Buffer.from(_value))
|
|
184
|
+
: _value;
|
|
143
185
|
this.sharedReferences.set(ref, value);
|
|
144
186
|
this.sharedReferencesByValue.set(value, ref);
|
|
145
187
|
result = responseFromContent(null);
|
|
146
188
|
} else if (method === 'deleteSharedReference') {
|
|
147
|
-
|
|
189
|
+
let ref = args[0];
|
|
190
|
+
let value = this.sharedReferences.get(ref);
|
|
191
|
+
this.sharedReferencesByValue.delete(value);
|
|
192
|
+
this.sharedReferences.delete(ref);
|
|
148
193
|
result = responseFromContent(null);
|
|
149
194
|
} else {
|
|
150
195
|
try {
|
|
@@ -157,7 +202,11 @@ export class Child {
|
|
|
157
202
|
}
|
|
158
203
|
}
|
|
159
204
|
|
|
160
|
-
|
|
205
|
+
try {
|
|
206
|
+
this.send(result);
|
|
207
|
+
} catch (e) {
|
|
208
|
+
result = this.send(errorResponseFromError(e));
|
|
209
|
+
}
|
|
161
210
|
}
|
|
162
211
|
|
|
163
212
|
handleResponse(data: WorkerResponse): void {
|
|
@@ -189,6 +238,7 @@ export class Child {
|
|
|
189
238
|
...request,
|
|
190
239
|
type: 'request',
|
|
191
240
|
child: this.childId,
|
|
241
|
+
// $FlowFixMe Added in Flow 0.121.0 upgrade in #4381
|
|
192
242
|
awaitResponse,
|
|
193
243
|
resolve: () => {},
|
|
194
244
|
reject: () => {},
|
|
@@ -241,10 +291,9 @@ export class Child {
|
|
|
241
291
|
this.loggerDisposable.dispose();
|
|
242
292
|
}
|
|
243
293
|
|
|
244
|
-
createReverseHandle(fn: (...args: Array<any>) => mixed) {
|
|
294
|
+
createReverseHandle(fn: (...args: Array<any>) => mixed): Handle {
|
|
245
295
|
let handle = new Handle({
|
|
246
296
|
fn,
|
|
247
|
-
workerApi: this.workerApi,
|
|
248
297
|
childId: this.childId,
|
|
249
298
|
});
|
|
250
299
|
this.handles.set(handle.id, handle);
|