@parcel/workers 2.0.0-nightly.97 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +96 -26
- package/lib/WorkerFarm.js +197 -95
- package/lib/backend.js +6 -0
- package/lib/bus.js +10 -2
- package/lib/child.js +114 -63
- package/lib/cpuCount.js +27 -6
- package/lib/index.js +24 -8
- 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 +74 -11
- package/src/WorkerFarm.js +150 -40
- package/src/backend.js +6 -0
- package/src/bus.js +1 -1
- package/src/child.js +71 -20
- package/src/cpuCount.js +9 -4
- 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 +6 -6
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,15 +190,22 @@ 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);
|
207
|
+
} else {
|
208
|
+
logger.error(error, '@parcel/workers');
|
182
209
|
}
|
183
210
|
}
|
184
211
|
|
@@ -186,7 +213,8 @@ export default class WorkerFarm extends EventEmitter {
|
|
186
213
|
let worker = new Worker({
|
187
214
|
forcedKillTime: this.options.forcedKillTime,
|
188
215
|
backend: this.options.backend,
|
189
|
-
|
216
|
+
shouldPatchConsole: this.options.shouldPatchConsole,
|
217
|
+
sharedReferences: this.sharedReferences,
|
190
218
|
});
|
191
219
|
|
192
220
|
worker.fork(nullthrows(this.options.workerPath));
|
@@ -231,7 +259,11 @@ export default class WorkerFarm extends EventEmitter {
|
|
231
259
|
this.startChild();
|
232
260
|
}
|
233
261
|
|
234
|
-
|
262
|
+
let workers = [...this.workers.values()].sort(
|
263
|
+
(a, b) => a.calls.size - b.calls.size,
|
264
|
+
);
|
265
|
+
|
266
|
+
for (let worker of workers) {
|
235
267
|
if (!this.callQueue.length) {
|
236
268
|
break;
|
237
269
|
}
|
@@ -255,7 +287,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
255
287
|
let {method, args, location, awaitResponse, idx, handle: handleId} = data;
|
256
288
|
let mod;
|
257
289
|
if (handleId != null) {
|
258
|
-
mod = nullthrows(this.handles.get(handleId))
|
290
|
+
mod = nullthrows(this.handles.get(handleId)?.fn);
|
259
291
|
} else if (location) {
|
260
292
|
// $FlowFixMe this must be dynamic
|
261
293
|
mod = require(location);
|
@@ -286,7 +318,6 @@ export default class WorkerFarm extends EventEmitter {
|
|
286
318
|
}
|
287
319
|
} else {
|
288
320
|
// ESModule default interop
|
289
|
-
// $FlowFixMe
|
290
321
|
if (mod.__esModule && !mod[method] && mod.default) {
|
291
322
|
mod = mod.default;
|
292
323
|
}
|
@@ -331,6 +362,10 @@ export default class WorkerFarm extends EventEmitter {
|
|
331
362
|
async end(): Promise<void> {
|
332
363
|
this.ending = true;
|
333
364
|
|
365
|
+
await Promise.all(
|
366
|
+
Array.from(this.workers.values()).map(worker => this.stopWorker(worker)),
|
367
|
+
);
|
368
|
+
|
334
369
|
for (let handle of this.handles.values()) {
|
335
370
|
handle.dispose();
|
336
371
|
}
|
@@ -338,9 +373,6 @@ export default class WorkerFarm extends EventEmitter {
|
|
338
373
|
this.sharedReferences = new Map();
|
339
374
|
this.sharedReferencesByValue = new Map();
|
340
375
|
|
341
|
-
await Promise.all(
|
342
|
-
Array.from(this.workers.values()).map(worker => this.stopWorker(worker)),
|
343
|
-
);
|
344
376
|
this.ending = false;
|
345
377
|
}
|
346
378
|
|
@@ -362,29 +394,28 @@ export default class WorkerFarm extends EventEmitter {
|
|
362
394
|
);
|
363
395
|
}
|
364
396
|
|
365
|
-
createReverseHandle(fn: HandleFunction) {
|
366
|
-
let handle = new Handle({fn
|
397
|
+
createReverseHandle(fn: HandleFunction): Handle {
|
398
|
+
let handle = new Handle({fn});
|
367
399
|
this.handles.set(handle.id, handle);
|
368
400
|
return handle;
|
369
401
|
}
|
370
402
|
|
371
|
-
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>|}> {
|
372
409
|
let ref = referenceId++;
|
373
410
|
this.sharedReferences.set(ref, value);
|
374
411
|
this.sharedReferencesByValue.set(value, ref);
|
412
|
+
|
413
|
+
let toSend = buffer ? buffer.buffer : value;
|
375
414
|
let promises = [];
|
376
415
|
for (let worker of this.workers.values()) {
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
method: 'createSharedReference',
|
381
|
-
args: [ref, value],
|
382
|
-
resolve,
|
383
|
-
reject,
|
384
|
-
retries: 0,
|
385
|
-
});
|
386
|
-
}),
|
387
|
-
);
|
416
|
+
if (worker.ready) {
|
417
|
+
promises.push(worker.sendSharedReference(ref, toSend));
|
418
|
+
}
|
388
419
|
}
|
389
420
|
|
390
421
|
await Promise.all(promises);
|
@@ -403,6 +434,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
403
434
|
args: [ref],
|
404
435
|
resolve,
|
405
436
|
reject,
|
437
|
+
skipReadyCheck: true,
|
406
438
|
retries: 0,
|
407
439
|
});
|
408
440
|
}),
|
@@ -424,6 +456,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
424
456
|
resolve,
|
425
457
|
reject,
|
426
458
|
retries: 0,
|
459
|
+
skipReadyCheck: true,
|
427
460
|
});
|
428
461
|
}),
|
429
462
|
);
|
@@ -453,6 +486,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
453
486
|
resolve,
|
454
487
|
reject,
|
455
488
|
retries: 0,
|
489
|
+
skipReadyCheck: true,
|
456
490
|
});
|
457
491
|
}),
|
458
492
|
);
|
@@ -460,7 +494,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
460
494
|
|
461
495
|
var profiles = await Promise.all(promises);
|
462
496
|
let trace = new Trace();
|
463
|
-
let filename = `profile-${
|
497
|
+
let filename = `profile-${getTimeId()}.trace`;
|
464
498
|
let stream = trace.pipe(fs.createWriteStream(filename));
|
465
499
|
|
466
500
|
for (let profile of profiles) {
|
@@ -474,21 +508,84 @@ export default class WorkerFarm extends EventEmitter {
|
|
474
508
|
|
475
509
|
logger.info({
|
476
510
|
origin: '@parcel/workers',
|
477
|
-
message: `Wrote profile to ${filename}`,
|
511
|
+
message: md`Wrote profile to ${filename}`,
|
478
512
|
});
|
479
513
|
}
|
480
514
|
|
481
|
-
|
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 {
|
482
570
|
return process.env.PARCEL_WORKERS
|
483
571
|
? parseInt(process.env.PARCEL_WORKERS, 10)
|
484
|
-
: cpuCount();
|
572
|
+
: Math.ceil(cpuCount() / 2);
|
485
573
|
}
|
486
574
|
|
487
|
-
static isWorker() {
|
575
|
+
static isWorker(): boolean {
|
488
576
|
return !!child;
|
489
577
|
}
|
490
578
|
|
491
|
-
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
|
+
|} {
|
492
589
|
invariant(
|
493
590
|
child != null,
|
494
591
|
'WorkerFarm.getWorkerApi can only be called within workers',
|
@@ -496,7 +593,20 @@ export default class WorkerFarm extends EventEmitter {
|
|
496
593
|
return child.workerApi;
|
497
594
|
}
|
498
595
|
|
499
|
-
static getConcurrentCallsPerWorker() {
|
500
|
-
return parseInt(process.env.PARCEL_MAX_CONCURRENT_CALLS, 10) ||
|
596
|
+
static getConcurrentCallsPerWorker(): number {
|
597
|
+
return parseInt(process.env.PARCEL_MAX_CONCURRENT_CALLS, 10) || 30;
|
501
598
|
}
|
502
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/backend.js
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
import type {BackendType, WorkerImpl} from './types';
|
3
3
|
|
4
4
|
export function detectBackend(): BackendType {
|
5
|
+
switch (process.env.PARCEL_WORKER_BACKEND) {
|
6
|
+
case 'threads':
|
7
|
+
case 'process':
|
8
|
+
return process.env.PARCEL_WORKER_BACKEND;
|
9
|
+
}
|
10
|
+
|
5
11
|
try {
|
6
12
|
require('worker_threads');
|
7
13
|
return 'threads';
|
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 {
|
@@ -189,6 +240,7 @@ export class Child {
|
|
189
240
|
...request,
|
190
241
|
type: 'request',
|
191
242
|
child: this.childId,
|
243
|
+
// $FlowFixMe Added in Flow 0.121.0 upgrade in #4381
|
192
244
|
awaitResponse,
|
193
245
|
resolve: () => {},
|
194
246
|
reject: () => {},
|
@@ -241,10 +293,9 @@ export class Child {
|
|
241
293
|
this.loggerDisposable.dispose();
|
242
294
|
}
|
243
295
|
|
244
|
-
createReverseHandle(fn: (...args: Array<any>) => mixed) {
|
296
|
+
createReverseHandle(fn: (...args: Array<any>) => mixed): Handle {
|
245
297
|
let handle = new Handle({
|
246
298
|
fn,
|
247
|
-
workerApi: this.workerApi,
|
248
299
|
childId: this.childId,
|
249
300
|
});
|
250
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,9 +41,8 @@ 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
|
-
// $FlowFixMe
|
42
46
|
if (cores && !bypassCache) {
|
43
47
|
return cores;
|
44
48
|
}
|
@@ -49,8 +53,9 @@ export default function getCores(bypassCache?: boolean = false) {
|
|
49
53
|
// Guess the amount of real cores
|
50
54
|
cores = os
|
51
55
|
.cpus()
|
52
|
-
.filter(
|
53
|
-
|
56
|
+
.filter(
|
57
|
+
(cpu, index) => !cpu.model.includes('Intel') || index % 2 === 1,
|
58
|
+
).length;
|
54
59
|
}
|
55
60
|
|
56
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;
|