@parcel/workers 2.0.0-nightly.92 → 2.0.1
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 +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;
|