@parcel/workers 2.0.0-nightly.92 → 2.0.0-nightly.934
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 +200 -92
- package/lib/backend.js +6 -0
- package/lib/bus.js +10 -2
- package/lib/child.js +116 -60
- 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 +13 -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 +155 -38
- package/src/backend.js +6 -0
- package/src/bus.js +1 -1
- package/src/child.js +75 -19
- 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/resolve-shared-reference.js +5 -0
- package/test/integration/workerfarm/reverse-handle.js +2 -2
- package/test/workerfarm.js +43 -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,7 +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
|
+
getSharedReference(ref: SharedReference): mixed,
|
57
|
+
resolveSharedReference(value: mixed): ?SharedReference,
|
56
58
|
callChild?: (childId: number, request: HandleCallRequest) => Promise<mixed>,
|
57
59
|
|};
|
58
60
|
|
@@ -66,12 +68,14 @@ export default class WorkerFarm extends EventEmitter {
|
|
66
68
|
callQueue: Array<WorkerCall> = [];
|
67
69
|
ending: boolean = false;
|
68
70
|
localWorker: WorkerModule;
|
71
|
+
localWorkerInit: ?Promise<void>;
|
69
72
|
options: FarmOptions;
|
70
73
|
run: HandleFunction;
|
71
74
|
warmWorkers: number = 0;
|
72
75
|
workers: Map<number, Worker> = new Map();
|
73
76
|
handles: Map<number, Handle> = new Map();
|
74
|
-
sharedReferences: Map<
|
77
|
+
sharedReferences: Map<SharedReference, mixed> = new Map();
|
78
|
+
sharedReferencesByValue: Map<mixed, SharedReference> = new Map();
|
75
79
|
profiler: ?Profiler;
|
76
80
|
|
77
81
|
constructor(farmOptions: $Shape<FarmOptions> = {}) {
|
@@ -92,12 +96,24 @@ export default class WorkerFarm extends EventEmitter {
|
|
92
96
|
|
93
97
|
// $FlowFixMe this must be dynamic
|
94
98
|
this.localWorker = require(this.options.workerPath);
|
99
|
+
this.localWorkerInit =
|
100
|
+
this.localWorker.childInit != null ? this.localWorker.childInit() : null;
|
95
101
|
this.run = this.createHandle('run');
|
96
102
|
|
97
103
|
this.startMaxWorkers();
|
98
104
|
}
|
99
105
|
|
100
|
-
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
|
+
|} = {
|
101
117
|
callMaster: async (
|
102
118
|
request: CallRequest,
|
103
119
|
awaitResponse: ?boolean = true,
|
@@ -120,7 +136,15 @@ export default class WorkerFarm extends EventEmitter {
|
|
120
136
|
retries: 0,
|
121
137
|
});
|
122
138
|
}),
|
123
|
-
|
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),
|
146
|
+
resolveSharedReference: (value: mixed) =>
|
147
|
+
this.sharedReferencesByValue.get(value),
|
124
148
|
};
|
125
149
|
|
126
150
|
warmupWorker(method: string, args: Array<any>): void {
|
@@ -152,7 +176,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
152
176
|
}
|
153
177
|
|
154
178
|
createHandle(method: string): HandleFunction {
|
155
|
-
return (...args) => {
|
179
|
+
return async (...args) => {
|
156
180
|
// Child process workers are slow to start (~600ms).
|
157
181
|
// While we're waiting, just run on the main thread.
|
158
182
|
// This significantly speeds up startup time.
|
@@ -166,15 +190,22 @@ export default class WorkerFarm extends EventEmitter {
|
|
166
190
|
let processedArgs = restoreDeserializedObject(
|
167
191
|
prepareForSerialization([...args, false]),
|
168
192
|
);
|
193
|
+
|
194
|
+
if (this.localWorkerInit != null) {
|
195
|
+
await this.localWorkerInit;
|
196
|
+
this.localWorkerInit = null;
|
197
|
+
}
|
169
198
|
return this.localWorker[method](this.workerApi, ...processedArgs);
|
170
199
|
}
|
171
200
|
};
|
172
201
|
}
|
173
202
|
|
174
|
-
onError(error: ErrorWithCode, worker: Worker) {
|
203
|
+
onError(error: ErrorWithCode, worker: Worker): void | Promise<void> {
|
175
204
|
// Handle ipc errors
|
176
205
|
if (error.code === 'ERR_IPC_CHANNEL_CLOSED') {
|
177
206
|
return this.stopWorker(worker);
|
207
|
+
} else {
|
208
|
+
logger.error(error, '@parcel/workers');
|
178
209
|
}
|
179
210
|
}
|
180
211
|
|
@@ -182,7 +213,8 @@ export default class WorkerFarm extends EventEmitter {
|
|
182
213
|
let worker = new Worker({
|
183
214
|
forcedKillTime: this.options.forcedKillTime,
|
184
215
|
backend: this.options.backend,
|
185
|
-
|
216
|
+
shouldPatchConsole: this.options.shouldPatchConsole,
|
217
|
+
sharedReferences: this.sharedReferences,
|
186
218
|
});
|
187
219
|
|
188
220
|
worker.fork(nullthrows(this.options.workerPath));
|
@@ -227,7 +259,11 @@ export default class WorkerFarm extends EventEmitter {
|
|
227
259
|
this.startChild();
|
228
260
|
}
|
229
261
|
|
230
|
-
|
262
|
+
let workers = [...this.workers.values()].sort(
|
263
|
+
(a, b) => a.calls.size - b.calls.size,
|
264
|
+
);
|
265
|
+
|
266
|
+
for (let worker of workers) {
|
231
267
|
if (!this.callQueue.length) {
|
232
268
|
break;
|
233
269
|
}
|
@@ -251,7 +287,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
251
287
|
let {method, args, location, awaitResponse, idx, handle: handleId} = data;
|
252
288
|
let mod;
|
253
289
|
if (handleId != null) {
|
254
|
-
mod = nullthrows(this.handles.get(handleId))
|
290
|
+
mod = nullthrows(this.handles.get(handleId)?.fn);
|
255
291
|
} else if (location) {
|
256
292
|
// $FlowFixMe this must be dynamic
|
257
293
|
mod = require(location);
|
@@ -282,7 +318,6 @@ export default class WorkerFarm extends EventEmitter {
|
|
282
318
|
}
|
283
319
|
} else {
|
284
320
|
// ESModule default interop
|
285
|
-
// $FlowFixMe
|
286
321
|
if (mod.__esModule && !mod[method] && mod.default) {
|
287
322
|
mod = mod.default;
|
288
323
|
}
|
@@ -327,15 +362,17 @@ export default class WorkerFarm extends EventEmitter {
|
|
327
362
|
async end(): Promise<void> {
|
328
363
|
this.ending = true;
|
329
364
|
|
365
|
+
await Promise.all(
|
366
|
+
Array.from(this.workers.values()).map(worker => this.stopWorker(worker)),
|
367
|
+
);
|
368
|
+
|
330
369
|
for (let handle of this.handles.values()) {
|
331
370
|
handle.dispose();
|
332
371
|
}
|
333
372
|
this.handles = new Map();
|
334
373
|
this.sharedReferences = new Map();
|
374
|
+
this.sharedReferencesByValue = new Map();
|
335
375
|
|
336
|
-
await Promise.all(
|
337
|
-
Array.from(this.workers.values()).map(worker => this.stopWorker(worker)),
|
338
|
-
);
|
339
376
|
this.ending = false;
|
340
377
|
}
|
341
378
|
|
@@ -357,28 +394,28 @@ export default class WorkerFarm extends EventEmitter {
|
|
357
394
|
);
|
358
395
|
}
|
359
396
|
|
360
|
-
createReverseHandle(fn: HandleFunction) {
|
361
|
-
let handle = new Handle({fn
|
397
|
+
createReverseHandle(fn: HandleFunction): Handle {
|
398
|
+
let handle = new Handle({fn});
|
362
399
|
this.handles.set(handle.id, handle);
|
363
400
|
return handle;
|
364
401
|
}
|
365
402
|
|
366
|
-
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>|}> {
|
367
409
|
let ref = referenceId++;
|
368
410
|
this.sharedReferences.set(ref, value);
|
411
|
+
this.sharedReferencesByValue.set(value, ref);
|
412
|
+
|
413
|
+
let toSend = buffer ? buffer.buffer : value;
|
369
414
|
let promises = [];
|
370
415
|
for (let worker of this.workers.values()) {
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
method: 'createSharedReference',
|
375
|
-
args: [ref, value],
|
376
|
-
resolve,
|
377
|
-
reject,
|
378
|
-
retries: 0,
|
379
|
-
});
|
380
|
-
}),
|
381
|
-
);
|
416
|
+
if (worker.ready) {
|
417
|
+
promises.push(worker.sendSharedReference(ref, toSend));
|
418
|
+
}
|
382
419
|
}
|
383
420
|
|
384
421
|
await Promise.all(promises);
|
@@ -387,6 +424,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
387
424
|
ref,
|
388
425
|
dispose: () => {
|
389
426
|
this.sharedReferences.delete(ref);
|
427
|
+
this.sharedReferencesByValue.delete(value);
|
390
428
|
let promises = [];
|
391
429
|
for (let worker of this.workers.values()) {
|
392
430
|
promises.push(
|
@@ -396,6 +434,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
396
434
|
args: [ref],
|
397
435
|
resolve,
|
398
436
|
reject,
|
437
|
+
skipReadyCheck: true,
|
399
438
|
retries: 0,
|
400
439
|
});
|
401
440
|
}),
|
@@ -417,6 +456,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
417
456
|
resolve,
|
418
457
|
reject,
|
419
458
|
retries: 0,
|
459
|
+
skipReadyCheck: true,
|
420
460
|
});
|
421
461
|
}),
|
422
462
|
);
|
@@ -446,6 +486,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
446
486
|
resolve,
|
447
487
|
reject,
|
448
488
|
retries: 0,
|
489
|
+
skipReadyCheck: true,
|
449
490
|
});
|
450
491
|
}),
|
451
492
|
);
|
@@ -453,7 +494,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
453
494
|
|
454
495
|
var profiles = await Promise.all(promises);
|
455
496
|
let trace = new Trace();
|
456
|
-
let filename = `profile-${
|
497
|
+
let filename = `profile-${getTimeId()}.trace`;
|
457
498
|
let stream = trace.pipe(fs.createWriteStream(filename));
|
458
499
|
|
459
500
|
for (let profile of profiles) {
|
@@ -467,21 +508,84 @@ export default class WorkerFarm extends EventEmitter {
|
|
467
508
|
|
468
509
|
logger.info({
|
469
510
|
origin: '@parcel/workers',
|
470
|
-
message: `Wrote profile to ${filename}`,
|
511
|
+
message: md`Wrote profile to ${filename}`,
|
471
512
|
});
|
472
513
|
}
|
473
514
|
|
474
|
-
|
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 {
|
475
570
|
return process.env.PARCEL_WORKERS
|
476
571
|
? parseInt(process.env.PARCEL_WORKERS, 10)
|
477
|
-
: cpuCount();
|
572
|
+
: Math.ceil(cpuCount() / 2);
|
478
573
|
}
|
479
574
|
|
480
|
-
static isWorker() {
|
575
|
+
static isWorker(): boolean {
|
481
576
|
return !!child;
|
482
577
|
}
|
483
578
|
|
484
|
-
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
|
+
|} {
|
485
589
|
invariant(
|
486
590
|
child != null,
|
487
591
|
'WorkerFarm.getWorkerApi can only be called within workers',
|
@@ -489,7 +593,20 @@ export default class WorkerFarm extends EventEmitter {
|
|
489
593
|
return child.workerApi;
|
490
594
|
}
|
491
595
|
|
492
|
-
static getConcurrentCallsPerWorker() {
|
493
|
-
return parseInt(process.env.PARCEL_MAX_CONCURRENT_CALLS, 10) ||
|
596
|
+
static getConcurrentCallsPerWorker(): number {
|
597
|
+
return parseInt(process.env.PARCEL_MAX_CONCURRENT_CALLS, 10) || 30;
|
494
598
|
}
|
495
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,19 +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<
|
44
|
+
sharedReferences: Map<SharedReference, mixed> = new Map();
|
45
|
+
sharedReferencesByValue: Map<mixed, SharedReference> = new Map();
|
41
46
|
|
42
47
|
constructor(ChildBackend: Class<ChildImpl>) {
|
43
48
|
this.child = new ChildBackend(
|
44
|
-
|
45
|
-
|
49
|
+
m => {
|
50
|
+
this.messageListener(m);
|
51
|
+
},
|
52
|
+
() => this.handleEnd(),
|
46
53
|
);
|
47
54
|
|
48
55
|
// Monitior all logging events inside this child process and forward to
|
@@ -52,17 +59,31 @@ export class Child {
|
|
52
59
|
});
|
53
60
|
}
|
54
61
|
|
55
|
-
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
|
+
|} = {
|
56
72
|
callMaster: (
|
57
73
|
request: CallRequest,
|
58
74
|
awaitResponse: ?boolean = true,
|
59
75
|
): Promise<mixed> => this.addCall(request, awaitResponse),
|
60
76
|
createReverseHandle: (fn: (...args: Array<any>) => mixed): Handle =>
|
61
77
|
this.createReverseHandle(fn),
|
62
|
-
|
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),
|
82
|
+
resolveSharedReference: (value: mixed) =>
|
83
|
+
this.sharedReferencesByValue.get(value),
|
63
84
|
};
|
64
85
|
|
65
|
-
messageListener(message: WorkerMessage):
|
86
|
+
messageListener(message: WorkerMessage): Async<void> {
|
66
87
|
if (message.type === 'response') {
|
67
88
|
return this.handleResponse(message);
|
68
89
|
} else if (message.type === 'request') {
|
@@ -74,10 +95,14 @@ export class Child {
|
|
74
95
|
this.child.send(data);
|
75
96
|
}
|
76
97
|
|
77
|
-
childInit(module: string, childId: number): void {
|
98
|
+
async childInit(module: string, childId: number): Promise<void> {
|
78
99
|
// $FlowFixMe this must be dynamic
|
79
100
|
this.module = require(module);
|
80
101
|
this.childId = childId;
|
102
|
+
|
103
|
+
if (this.module.childInit != null) {
|
104
|
+
await this.module.childInit();
|
105
|
+
}
|
81
106
|
}
|
82
107
|
|
83
108
|
async handleRequest(data: WorkerRequest): Promise<void> {
|
@@ -103,7 +128,7 @@ export class Child {
|
|
103
128
|
let result;
|
104
129
|
if (handleId != null) {
|
105
130
|
try {
|
106
|
-
let fn = nullthrows(this.handles.get(handleId))
|
131
|
+
let fn = nullthrows(this.handles.get(handleId)?.fn);
|
107
132
|
result = responseFromContent(fn(...args));
|
108
133
|
} catch (e) {
|
109
134
|
result = errorResponseFromError(e);
|
@@ -111,13 +136,13 @@ export class Child {
|
|
111
136
|
} else if (method === 'childInit') {
|
112
137
|
try {
|
113
138
|
let [moduleName, childOptions] = args;
|
114
|
-
if (childOptions.
|
139
|
+
if (childOptions.shouldPatchConsole) {
|
115
140
|
patchConsole();
|
116
141
|
} else {
|
117
142
|
unpatchConsole();
|
118
143
|
}
|
119
144
|
|
120
|
-
result = responseFromContent(this.childInit(moduleName, child));
|
145
|
+
result = responseFromContent(await this.childInit(moduleName, child));
|
121
146
|
} catch (e) {
|
122
147
|
result = errorResponseFromError(e);
|
123
148
|
}
|
@@ -135,11 +160,38 @@ export class Child {
|
|
135
160
|
} catch (e) {
|
136
161
|
result = errorResponseFromError(e);
|
137
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
|
+
}
|
138
179
|
} else if (method === 'createSharedReference') {
|
139
|
-
|
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;
|
187
|
+
this.sharedReferences.set(ref, value);
|
188
|
+
this.sharedReferencesByValue.set(value, ref);
|
140
189
|
result = responseFromContent(null);
|
141
190
|
} else if (method === 'deleteSharedReference') {
|
142
|
-
|
191
|
+
let ref = args[0];
|
192
|
+
let value = this.sharedReferences.get(ref);
|
193
|
+
this.sharedReferencesByValue.delete(value);
|
194
|
+
this.sharedReferences.delete(ref);
|
143
195
|
result = responseFromContent(null);
|
144
196
|
} else {
|
145
197
|
try {
|
@@ -152,7 +204,11 @@ export class Child {
|
|
152
204
|
}
|
153
205
|
}
|
154
206
|
|
155
|
-
|
207
|
+
try {
|
208
|
+
this.send(result);
|
209
|
+
} catch (e) {
|
210
|
+
result = this.send(errorResponseFromError(e));
|
211
|
+
}
|
156
212
|
}
|
157
213
|
|
158
214
|
handleResponse(data: WorkerResponse): void {
|
@@ -184,6 +240,7 @@ export class Child {
|
|
184
240
|
...request,
|
185
241
|
type: 'request',
|
186
242
|
child: this.childId,
|
243
|
+
// $FlowFixMe Added in Flow 0.121.0 upgrade in #4381
|
187
244
|
awaitResponse,
|
188
245
|
resolve: () => {},
|
189
246
|
reject: () => {},
|
@@ -236,10 +293,9 @@ export class Child {
|
|
236
293
|
this.loggerDisposable.dispose();
|
237
294
|
}
|
238
295
|
|
239
|
-
createReverseHandle(fn: (...args: Array<any>) => mixed) {
|
296
|
+
createReverseHandle(fn: (...args: Array<any>) => mixed): Handle {
|
240
297
|
let handle = new Handle({
|
241
298
|
fn,
|
242
|
-
workerApi: this.workerApi,
|
243
299
|
childId: this.childId,
|
244
300
|
});
|
245
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;
|