@parcel/workers 2.0.0-beta.1 → 2.0.0-nightly.1002
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 +63 -31
- package/lib/WorkerFarm.js +191 -82
- package/lib/bus.js +10 -2
- package/lib/child.js +111 -56
- package/lib/childState.js +1 -1
- package/lib/cpuCount.js +28 -6
- package/lib/index.js +26 -10
- package/lib/process/ProcessChild.js +21 -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 +39 -16
- package/src/WorkerFarm.js +145 -29
- package/src/bus.js +1 -1
- package/src/child.js +70 -20
- package/src/cpuCount.js +9 -3
- package/src/index.js +1 -1
- package/src/process/ProcessChild.js +1 -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,14 @@ 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();
|
|
77
79
|
profiler: ?Profiler;
|
|
78
80
|
|
|
79
81
|
constructor(farmOptions: $Shape<FarmOptions> = {}) {
|
|
@@ -94,12 +96,24 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
94
96
|
|
|
95
97
|
// $FlowFixMe this must be dynamic
|
|
96
98
|
this.localWorker = require(this.options.workerPath);
|
|
99
|
+
this.localWorkerInit =
|
|
100
|
+
this.localWorker.childInit != null ? this.localWorker.childInit() : null;
|
|
97
101
|
this.run = this.createHandle('run');
|
|
98
102
|
|
|
99
103
|
this.startMaxWorkers();
|
|
100
104
|
}
|
|
101
105
|
|
|
102
|
-
workerApi
|
|
106
|
+
workerApi: {|
|
|
107
|
+
callChild: (childId: number, request: HandleCallRequest) => Promise<mixed>,
|
|
108
|
+
callMaster: (
|
|
109
|
+
request: CallRequest,
|
|
110
|
+
awaitResponse?: ?boolean,
|
|
111
|
+
) => Promise<mixed>,
|
|
112
|
+
createReverseHandle: (fn: HandleFunction) => Handle,
|
|
113
|
+
getSharedReference: (ref: SharedReference) => mixed,
|
|
114
|
+
resolveSharedReference: (value: mixed) => void | SharedReference,
|
|
115
|
+
runHandle: (handle: Handle, args: Array<any>) => Promise<mixed>,
|
|
116
|
+
|} = {
|
|
103
117
|
callMaster: async (
|
|
104
118
|
request: CallRequest,
|
|
105
119
|
awaitResponse: ?boolean = true,
|
|
@@ -122,7 +136,13 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
122
136
|
retries: 0,
|
|
123
137
|
});
|
|
124
138
|
}),
|
|
125
|
-
|
|
139
|
+
runHandle: (handle: Handle, args: Array<any>): Promise<mixed> =>
|
|
140
|
+
this.workerApi.callChild(nullthrows(handle.childId), {
|
|
141
|
+
handle: handle.id,
|
|
142
|
+
args,
|
|
143
|
+
}),
|
|
144
|
+
getSharedReference: (ref: SharedReference) =>
|
|
145
|
+
this.sharedReferences.get(ref),
|
|
126
146
|
resolveSharedReference: (value: mixed) =>
|
|
127
147
|
this.sharedReferencesByValue.get(value),
|
|
128
148
|
};
|
|
@@ -156,7 +176,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
156
176
|
}
|
|
157
177
|
|
|
158
178
|
createHandle(method: string): HandleFunction {
|
|
159
|
-
return (...args) => {
|
|
179
|
+
return async (...args) => {
|
|
160
180
|
// Child process workers are slow to start (~600ms).
|
|
161
181
|
// While we're waiting, just run on the main thread.
|
|
162
182
|
// This significantly speeds up startup time.
|
|
@@ -170,12 +190,17 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
170
190
|
let processedArgs = restoreDeserializedObject(
|
|
171
191
|
prepareForSerialization([...args, false]),
|
|
172
192
|
);
|
|
193
|
+
|
|
194
|
+
if (this.localWorkerInit != null) {
|
|
195
|
+
await this.localWorkerInit;
|
|
196
|
+
this.localWorkerInit = null;
|
|
197
|
+
}
|
|
173
198
|
return this.localWorker[method](this.workerApi, ...processedArgs);
|
|
174
199
|
}
|
|
175
200
|
};
|
|
176
201
|
}
|
|
177
202
|
|
|
178
|
-
onError(error: ErrorWithCode, worker: Worker) {
|
|
203
|
+
onError(error: ErrorWithCode, worker: Worker): void | Promise<void> {
|
|
179
204
|
// Handle ipc errors
|
|
180
205
|
if (error.code === 'ERR_IPC_CHANNEL_CLOSED') {
|
|
181
206
|
return this.stopWorker(worker);
|
|
@@ -188,7 +213,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
188
213
|
let worker = new Worker({
|
|
189
214
|
forcedKillTime: this.options.forcedKillTime,
|
|
190
215
|
backend: this.options.backend,
|
|
191
|
-
|
|
216
|
+
shouldPatchConsole: this.options.shouldPatchConsole,
|
|
192
217
|
sharedReferences: this.sharedReferences,
|
|
193
218
|
});
|
|
194
219
|
|
|
@@ -234,7 +259,11 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
234
259
|
this.startChild();
|
|
235
260
|
}
|
|
236
261
|
|
|
237
|
-
|
|
262
|
+
let workers = [...this.workers.values()].sort(
|
|
263
|
+
(a, b) => a.calls.size - b.calls.size,
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
for (let worker of workers) {
|
|
238
267
|
if (!this.callQueue.length) {
|
|
239
268
|
break;
|
|
240
269
|
}
|
|
@@ -258,7 +287,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
258
287
|
let {method, args, location, awaitResponse, idx, handle: handleId} = data;
|
|
259
288
|
let mod;
|
|
260
289
|
if (handleId != null) {
|
|
261
|
-
mod = nullthrows(this.handles.get(handleId))
|
|
290
|
+
mod = nullthrows(this.handles.get(handleId)?.fn);
|
|
262
291
|
} else if (location) {
|
|
263
292
|
// $FlowFixMe this must be dynamic
|
|
264
293
|
mod = require(location);
|
|
@@ -333,6 +362,10 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
333
362
|
async end(): Promise<void> {
|
|
334
363
|
this.ending = true;
|
|
335
364
|
|
|
365
|
+
await Promise.all(
|
|
366
|
+
Array.from(this.workers.values()).map(worker => this.stopWorker(worker)),
|
|
367
|
+
);
|
|
368
|
+
|
|
336
369
|
for (let handle of this.handles.values()) {
|
|
337
370
|
handle.dispose();
|
|
338
371
|
}
|
|
@@ -340,9 +373,6 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
340
373
|
this.sharedReferences = new Map();
|
|
341
374
|
this.sharedReferencesByValue = new Map();
|
|
342
375
|
|
|
343
|
-
await Promise.all(
|
|
344
|
-
Array.from(this.workers.values()).map(worker => this.stopWorker(worker)),
|
|
345
|
-
);
|
|
346
376
|
this.ending = false;
|
|
347
377
|
}
|
|
348
378
|
|
|
@@ -364,20 +394,27 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
364
394
|
);
|
|
365
395
|
}
|
|
366
396
|
|
|
367
|
-
createReverseHandle(fn: HandleFunction) {
|
|
368
|
-
let handle = new Handle({fn
|
|
397
|
+
createReverseHandle(fn: HandleFunction): Handle {
|
|
398
|
+
let handle = new Handle({fn});
|
|
369
399
|
this.handles.set(handle.id, handle);
|
|
370
400
|
return handle;
|
|
371
401
|
}
|
|
372
402
|
|
|
373
|
-
async createSharedReference(
|
|
403
|
+
async createSharedReference(
|
|
404
|
+
value: mixed,
|
|
405
|
+
// An optional, pre-serialized representation of the value to be used
|
|
406
|
+
// in its place.
|
|
407
|
+
buffer?: Buffer,
|
|
408
|
+
): Promise<{|ref: SharedReference, dispose(): Promise<mixed>|}> {
|
|
374
409
|
let ref = referenceId++;
|
|
375
410
|
this.sharedReferences.set(ref, value);
|
|
376
411
|
this.sharedReferencesByValue.set(value, ref);
|
|
412
|
+
|
|
413
|
+
let toSend = buffer ? buffer.buffer : value;
|
|
377
414
|
let promises = [];
|
|
378
415
|
for (let worker of this.workers.values()) {
|
|
379
416
|
if (worker.ready) {
|
|
380
|
-
promises.push(worker.sendSharedReference(ref,
|
|
417
|
+
promises.push(worker.sendSharedReference(ref, toSend));
|
|
381
418
|
}
|
|
382
419
|
}
|
|
383
420
|
|
|
@@ -397,6 +434,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
397
434
|
args: [ref],
|
|
398
435
|
resolve,
|
|
399
436
|
reject,
|
|
437
|
+
skipReadyCheck: true,
|
|
400
438
|
retries: 0,
|
|
401
439
|
});
|
|
402
440
|
}),
|
|
@@ -418,6 +456,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
418
456
|
resolve,
|
|
419
457
|
reject,
|
|
420
458
|
retries: 0,
|
|
459
|
+
skipReadyCheck: true,
|
|
421
460
|
});
|
|
422
461
|
}),
|
|
423
462
|
);
|
|
@@ -447,6 +486,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
447
486
|
resolve,
|
|
448
487
|
reject,
|
|
449
488
|
retries: 0,
|
|
489
|
+
skipReadyCheck: true,
|
|
450
490
|
});
|
|
451
491
|
}),
|
|
452
492
|
);
|
|
@@ -454,7 +494,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
454
494
|
|
|
455
495
|
var profiles = await Promise.all(promises);
|
|
456
496
|
let trace = new Trace();
|
|
457
|
-
let filename = `profile-${
|
|
497
|
+
let filename = `profile-${getTimeId()}.trace`;
|
|
458
498
|
let stream = trace.pipe(fs.createWriteStream(filename));
|
|
459
499
|
|
|
460
500
|
for (let profile of profiles) {
|
|
@@ -468,21 +508,84 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
468
508
|
|
|
469
509
|
logger.info({
|
|
470
510
|
origin: '@parcel/workers',
|
|
471
|
-
message: `Wrote profile to ${filename}`,
|
|
511
|
+
message: md`Wrote profile to ${filename}`,
|
|
472
512
|
});
|
|
473
513
|
}
|
|
474
514
|
|
|
475
|
-
|
|
515
|
+
async callAllWorkers(method: string, args: Array<any>) {
|
|
516
|
+
let promises = [];
|
|
517
|
+
for (let worker of this.workers.values()) {
|
|
518
|
+
promises.push(
|
|
519
|
+
new Promise((resolve, reject) => {
|
|
520
|
+
worker.call({
|
|
521
|
+
method,
|
|
522
|
+
args,
|
|
523
|
+
resolve,
|
|
524
|
+
reject,
|
|
525
|
+
retries: 0,
|
|
526
|
+
});
|
|
527
|
+
}),
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
promises.push(this.localWorker[method](this.workerApi, ...args));
|
|
532
|
+
await Promise.all(promises);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
async takeHeapSnapshot() {
|
|
536
|
+
let snapshotId = getTimeId();
|
|
537
|
+
|
|
538
|
+
try {
|
|
539
|
+
let snapshotPaths = await Promise.all(
|
|
540
|
+
[...this.workers.values()].map(
|
|
541
|
+
worker =>
|
|
542
|
+
new Promise((resolve, reject) => {
|
|
543
|
+
worker.call({
|
|
544
|
+
method: 'takeHeapSnapshot',
|
|
545
|
+
args: [snapshotId],
|
|
546
|
+
resolve,
|
|
547
|
+
reject,
|
|
548
|
+
retries: 0,
|
|
549
|
+
skipReadyCheck: true,
|
|
550
|
+
});
|
|
551
|
+
}),
|
|
552
|
+
),
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
logger.info({
|
|
556
|
+
origin: '@parcel/workers',
|
|
557
|
+
message: md`Wrote heap snapshots to the following paths:\n${snapshotPaths.join(
|
|
558
|
+
'\n',
|
|
559
|
+
)}`,
|
|
560
|
+
});
|
|
561
|
+
} catch {
|
|
562
|
+
logger.error({
|
|
563
|
+
origin: '@parcel/workers',
|
|
564
|
+
message: 'Unable to take heap snapshots. Note: requires Node 11.13.0+',
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
static getNumWorkers(): number {
|
|
476
570
|
return process.env.PARCEL_WORKERS
|
|
477
571
|
? parseInt(process.env.PARCEL_WORKERS, 10)
|
|
478
|
-
: cpuCount();
|
|
572
|
+
: Math.ceil(cpuCount() / 2);
|
|
479
573
|
}
|
|
480
574
|
|
|
481
|
-
static isWorker() {
|
|
575
|
+
static isWorker(): boolean {
|
|
482
576
|
return !!child;
|
|
483
577
|
}
|
|
484
578
|
|
|
485
|
-
static getWorkerApi() {
|
|
579
|
+
static getWorkerApi(): {|
|
|
580
|
+
callMaster: (
|
|
581
|
+
request: CallRequest,
|
|
582
|
+
awaitResponse?: ?boolean,
|
|
583
|
+
) => Promise<mixed>,
|
|
584
|
+
createReverseHandle: (fn: (...args: Array<any>) => mixed) => Handle,
|
|
585
|
+
getSharedReference: (ref: SharedReference) => mixed,
|
|
586
|
+
resolveSharedReference: (value: mixed) => void | SharedReference,
|
|
587
|
+
runHandle: (handle: Handle, args: Array<any>) => Promise<mixed>,
|
|
588
|
+
|} {
|
|
486
589
|
invariant(
|
|
487
590
|
child != null,
|
|
488
591
|
'WorkerFarm.getWorkerApi can only be called within workers',
|
|
@@ -490,7 +593,20 @@ export default class WorkerFarm extends EventEmitter {
|
|
|
490
593
|
return child.workerApi;
|
|
491
594
|
}
|
|
492
595
|
|
|
493
|
-
static getConcurrentCallsPerWorker() {
|
|
494
|
-
return parseInt(process.env.PARCEL_MAX_CONCURRENT_CALLS, 10) ||
|
|
596
|
+
static getConcurrentCallsPerWorker(): number {
|
|
597
|
+
return parseInt(process.env.PARCEL_MAX_CONCURRENT_CALLS, 10) || 30;
|
|
495
598
|
}
|
|
496
599
|
}
|
|
600
|
+
|
|
601
|
+
function getTimeId() {
|
|
602
|
+
let now = new Date();
|
|
603
|
+
return (
|
|
604
|
+
String(now.getFullYear()) +
|
|
605
|
+
String(now.getMonth() + 1).padStart(2, '0') +
|
|
606
|
+
String(now.getDate()).padStart(2, '0') +
|
|
607
|
+
'-' +
|
|
608
|
+
String(now.getHours()).padStart(2, '0') +
|
|
609
|
+
String(now.getMinutes()).padStart(2, '0') +
|
|
610
|
+
String(now.getSeconds()).padStart(2, '0')
|
|
611
|
+
);
|
|
612
|
+
}
|
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 {WorkerApi} from './WorkerFarm';
|
|
12
|
+
import type {Async, IDisposable} from '@parcel/types';
|
|
13
|
+
import type {SharedReference, WorkerApi} 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,22 @@ 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
42
|
workerApi: WorkerApi;
|
|
39
43
|
handles: Map<number, Handle> = new Map();
|
|
40
|
-
sharedReferences: Map<
|
|
41
|
-
sharedReferencesByValue: Map<mixed,
|
|
44
|
+
sharedReferences: Map<SharedReference, mixed> = new Map();
|
|
45
|
+
sharedReferencesByValue: Map<mixed, SharedReference> = new Map();
|
|
42
46
|
|
|
43
47
|
constructor(ChildBackend: Class<ChildImpl>) {
|
|
44
48
|
this.child = new ChildBackend(
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
m => {
|
|
50
|
+
this.messageListener(m);
|
|
51
|
+
},
|
|
52
|
+
() => this.handleEnd(),
|
|
47
53
|
);
|
|
48
54
|
|
|
49
55
|
// Monitior all logging events inside this child process and forward to
|
|
@@ -53,19 +59,31 @@ export class Child {
|
|
|
53
59
|
});
|
|
54
60
|
}
|
|
55
61
|
|
|
56
|
-
workerApi
|
|
62
|
+
workerApi: {|
|
|
63
|
+
callMaster: (
|
|
64
|
+
request: CallRequest,
|
|
65
|
+
awaitResponse?: ?boolean,
|
|
66
|
+
) => Promise<mixed>,
|
|
67
|
+
createReverseHandle: (fn: (...args: Array<any>) => mixed) => Handle,
|
|
68
|
+
getSharedReference: (ref: SharedReference) => mixed,
|
|
69
|
+
resolveSharedReference: (value: mixed) => void | SharedReference,
|
|
70
|
+
runHandle: (handle: Handle, args: Array<any>) => Promise<mixed>,
|
|
71
|
+
|} = {
|
|
57
72
|
callMaster: (
|
|
58
73
|
request: CallRequest,
|
|
59
74
|
awaitResponse: ?boolean = true,
|
|
60
75
|
): Promise<mixed> => this.addCall(request, awaitResponse),
|
|
61
76
|
createReverseHandle: (fn: (...args: Array<any>) => mixed): Handle =>
|
|
62
77
|
this.createReverseHandle(fn),
|
|
63
|
-
|
|
78
|
+
runHandle: (handle: Handle, args: Array<any>): Promise<mixed> =>
|
|
79
|
+
this.workerApi.callMaster({handle: handle.id, args}, true),
|
|
80
|
+
getSharedReference: (ref: SharedReference) =>
|
|
81
|
+
this.sharedReferences.get(ref),
|
|
64
82
|
resolveSharedReference: (value: mixed) =>
|
|
65
83
|
this.sharedReferencesByValue.get(value),
|
|
66
84
|
};
|
|
67
85
|
|
|
68
|
-
messageListener(message: WorkerMessage):
|
|
86
|
+
messageListener(message: WorkerMessage): Async<void> {
|
|
69
87
|
if (message.type === 'response') {
|
|
70
88
|
return this.handleResponse(message);
|
|
71
89
|
} else if (message.type === 'request') {
|
|
@@ -77,10 +95,14 @@ export class Child {
|
|
|
77
95
|
this.child.send(data);
|
|
78
96
|
}
|
|
79
97
|
|
|
80
|
-
childInit(module: string, childId: number): void {
|
|
98
|
+
async childInit(module: string, childId: number): Promise<void> {
|
|
81
99
|
// $FlowFixMe this must be dynamic
|
|
82
100
|
this.module = require(module);
|
|
83
101
|
this.childId = childId;
|
|
102
|
+
|
|
103
|
+
if (this.module.childInit != null) {
|
|
104
|
+
await this.module.childInit();
|
|
105
|
+
}
|
|
84
106
|
}
|
|
85
107
|
|
|
86
108
|
async handleRequest(data: WorkerRequest): Promise<void> {
|
|
@@ -106,7 +128,7 @@ export class Child {
|
|
|
106
128
|
let result;
|
|
107
129
|
if (handleId != null) {
|
|
108
130
|
try {
|
|
109
|
-
let fn = nullthrows(this.handles.get(handleId))
|
|
131
|
+
let fn = nullthrows(this.handles.get(handleId)?.fn);
|
|
110
132
|
result = responseFromContent(fn(...args));
|
|
111
133
|
} catch (e) {
|
|
112
134
|
result = errorResponseFromError(e);
|
|
@@ -114,13 +136,13 @@ export class Child {
|
|
|
114
136
|
} else if (method === 'childInit') {
|
|
115
137
|
try {
|
|
116
138
|
let [moduleName, childOptions] = args;
|
|
117
|
-
if (childOptions.
|
|
139
|
+
if (childOptions.shouldPatchConsole) {
|
|
118
140
|
patchConsole();
|
|
119
141
|
} else {
|
|
120
142
|
unpatchConsole();
|
|
121
143
|
}
|
|
122
144
|
|
|
123
|
-
result = responseFromContent(this.childInit(moduleName, child));
|
|
145
|
+
result = responseFromContent(await this.childInit(moduleName, child));
|
|
124
146
|
} catch (e) {
|
|
125
147
|
result = errorResponseFromError(e);
|
|
126
148
|
}
|
|
@@ -138,13 +160,38 @@ export class Child {
|
|
|
138
160
|
} catch (e) {
|
|
139
161
|
result = errorResponseFromError(e);
|
|
140
162
|
}
|
|
163
|
+
} else if (method === 'takeHeapSnapshot') {
|
|
164
|
+
try {
|
|
165
|
+
let v8 = require('v8');
|
|
166
|
+
result = responseFromContent(
|
|
167
|
+
// $FlowFixMe
|
|
168
|
+
v8.writeHeapSnapshot(
|
|
169
|
+
'heap-' +
|
|
170
|
+
args[0] +
|
|
171
|
+
'-' +
|
|
172
|
+
(this.childId ? 'worker' + this.childId : 'main') +
|
|
173
|
+
'.heapsnapshot',
|
|
174
|
+
),
|
|
175
|
+
);
|
|
176
|
+
} catch (e) {
|
|
177
|
+
result = errorResponseFromError(e);
|
|
178
|
+
}
|
|
141
179
|
} else if (method === 'createSharedReference') {
|
|
142
|
-
let [ref,
|
|
180
|
+
let [ref, _value] = args;
|
|
181
|
+
let value =
|
|
182
|
+
_value instanceof ArrayBuffer
|
|
183
|
+
? // In the case the value is pre-serialized as a buffer,
|
|
184
|
+
// deserialize it.
|
|
185
|
+
deserialize(Buffer.from(_value))
|
|
186
|
+
: _value;
|
|
143
187
|
this.sharedReferences.set(ref, value);
|
|
144
188
|
this.sharedReferencesByValue.set(value, ref);
|
|
145
189
|
result = responseFromContent(null);
|
|
146
190
|
} else if (method === 'deleteSharedReference') {
|
|
147
|
-
|
|
191
|
+
let ref = args[0];
|
|
192
|
+
let value = this.sharedReferences.get(ref);
|
|
193
|
+
this.sharedReferencesByValue.delete(value);
|
|
194
|
+
this.sharedReferences.delete(ref);
|
|
148
195
|
result = responseFromContent(null);
|
|
149
196
|
} else {
|
|
150
197
|
try {
|
|
@@ -157,7 +204,11 @@ export class Child {
|
|
|
157
204
|
}
|
|
158
205
|
}
|
|
159
206
|
|
|
160
|
-
|
|
207
|
+
try {
|
|
208
|
+
this.send(result);
|
|
209
|
+
} catch (e) {
|
|
210
|
+
result = this.send(errorResponseFromError(e));
|
|
211
|
+
}
|
|
161
212
|
}
|
|
162
213
|
|
|
163
214
|
handleResponse(data: WorkerResponse): void {
|
|
@@ -242,10 +293,9 @@ export class Child {
|
|
|
242
293
|
this.loggerDisposable.dispose();
|
|
243
294
|
}
|
|
244
295
|
|
|
245
|
-
createReverseHandle(fn: (...args: Array<any>) => mixed) {
|
|
296
|
+
createReverseHandle(fn: (...args: Array<any>) => mixed): Handle {
|
|
246
297
|
let handle = new Handle({
|
|
247
298
|
fn,
|
|
248
|
-
workerApi: this.workerApi,
|
|
249
299
|
childId: this.childId,
|
|
250
300
|
});
|
|
251
301
|
this.handles.set(handle.id, handle);
|
package/src/cpuCount.js
CHANGED
|
@@ -26,6 +26,11 @@ export function detectRealCores(): number {
|
|
|
26
26
|
);
|
|
27
27
|
} else if (platform === 'darwin') {
|
|
28
28
|
amount = parseInt(exec('sysctl -n hw.physicalcpu_max'), 10);
|
|
29
|
+
} else if (platform === 'win32') {
|
|
30
|
+
const str = exec('wmic cpu get NumberOfCores').match(/\d+/g);
|
|
31
|
+
if (str !== null) {
|
|
32
|
+
amount = parseInt(str.filter(n => n !== '')[0], 10);
|
|
33
|
+
}
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
if (!amount || amount <= 0) {
|
|
@@ -36,7 +41,7 @@ export function detectRealCores(): number {
|
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
let cores;
|
|
39
|
-
export default function getCores(bypassCache?: boolean = false) {
|
|
44
|
+
export default function getCores(bypassCache?: boolean = false): number {
|
|
40
45
|
// Do not re-run commands if we already have the count...
|
|
41
46
|
if (cores && !bypassCache) {
|
|
42
47
|
return cores;
|
|
@@ -48,8 +53,9 @@ export default function getCores(bypassCache?: boolean = false) {
|
|
|
48
53
|
// Guess the amount of real cores
|
|
49
54
|
cores = os
|
|
50
55
|
.cpus()
|
|
51
|
-
.filter(
|
|
52
|
-
|
|
56
|
+
.filter(
|
|
57
|
+
(cpu, index) => !cpu.model.includes('Intel') || index % 2 === 1,
|
|
58
|
+
).length;
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
// Another fallback
|
package/src/index.js
CHANGED
|
@@ -32,7 +32,7 @@ export default class ThreadsWorker implements WorkerImpl {
|
|
|
32
32
|
this.onExit = onExit;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
start() {
|
|
35
|
+
start(): Promise<void> {
|
|
36
36
|
this.worker = new Worker(WORKER_PATH, {
|
|
37
37
|
execArgv: this.execArgv,
|
|
38
38
|
env: process.env,
|
|
@@ -47,7 +47,7 @@ export default class ThreadsWorker implements WorkerImpl {
|
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
stop() {
|
|
50
|
+
stop(): Promise<void> {
|
|
51
51
|
// In node 12, this returns a promise, but previously it accepted a callback
|
|
52
52
|
// TODO: Pass a callback in earlier versions of Node
|
|
53
53
|
return Promise.resolve(this.worker.terminate());
|
package/test/cpuCount.test.js
CHANGED
|
@@ -3,7 +3,7 @@ import os from 'os';
|
|
|
3
3
|
|
|
4
4
|
import getCores, {detectRealCores} from '../src/cpuCount';
|
|
5
5
|
|
|
6
|
-
describe('cpuCount', function() {
|
|
6
|
+
describe('cpuCount', function () {
|
|
7
7
|
it('Should be able to detect real cpu count', () => {
|
|
8
8
|
// Windows not supported as getting the cpu count takes a couple seconds...
|
|
9
9
|
if (os.platform() === 'win32') return;
|