@parcel/workers 2.0.0-nightly.97 → 2.1.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/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;
|