@parcel/workers 2.0.0-nightly.122 → 2.0.0-nightly.1221

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/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
- patchConsole?: boolean,
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: number): mixed,
56
- resolveSharedReference(value: mixed): ?number,
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,15 @@ 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<number, mixed> = new Map();
76
- sharedReferencesByValue: Map<mixed, number> = new Map();
77
+ sharedReferences: Map<SharedReference, mixed> = new Map();
78
+ sharedReferencesByValue: Map<mixed, SharedReference> = new Map();
79
+ serializedSharedReferences: Map<SharedReference, ?ArrayBuffer> = new Map();
77
80
  profiler: ?Profiler;
78
81
 
79
82
  constructor(farmOptions: $Shape<FarmOptions> = {}) {
@@ -94,12 +97,39 @@ export default class WorkerFarm extends EventEmitter {
94
97
 
95
98
  // $FlowFixMe this must be dynamic
96
99
  this.localWorker = require(this.options.workerPath);
100
+ this.localWorkerInit =
101
+ this.localWorker.childInit != null ? this.localWorker.childInit() : null;
97
102
  this.run = this.createHandle('run');
98
103
 
104
+ // Worker thread stdout is by default piped into the process stdout, if there are enough worker
105
+ // threads to exceed the default listener limit, then anything else piping into stdout will trigger
106
+ // the `MaxListenersExceededWarning`, so we should ensure the max listeners is at least equal to the
107
+ // number of workers + 1 for the main thread.
108
+ //
109
+ // Note this can't be fixed easily where other things pipe into stdout - even after starting > 10 worker
110
+ // threads `process.stdout.getMaxListeners()` will still return 10, however adding another pipe into `stdout`
111
+ // will give the warning with `<worker count + 1>` as the number of listeners.
112
+ process.stdout.setMaxListeners(
113
+ Math.max(
114
+ process.stdout.getMaxListeners(),
115
+ WorkerFarm.getNumWorkers() + 1,
116
+ ),
117
+ );
118
+
99
119
  this.startMaxWorkers();
100
120
  }
101
121
 
102
- workerApi = {
122
+ workerApi: {|
123
+ callChild: (childId: number, request: HandleCallRequest) => Promise<mixed>,
124
+ callMaster: (
125
+ request: CallRequest,
126
+ awaitResponse?: ?boolean,
127
+ ) => Promise<mixed>,
128
+ createReverseHandle: (fn: HandleFunction) => Handle,
129
+ getSharedReference: (ref: SharedReference) => mixed,
130
+ resolveSharedReference: (value: mixed) => void | SharedReference,
131
+ runHandle: (handle: Handle, args: Array<any>) => Promise<mixed>,
132
+ |} = {
103
133
  callMaster: async (
104
134
  request: CallRequest,
105
135
  awaitResponse: ?boolean = true,
@@ -122,7 +152,13 @@ export default class WorkerFarm extends EventEmitter {
122
152
  retries: 0,
123
153
  });
124
154
  }),
125
- getSharedReference: (ref: number) => this.sharedReferences.get(ref),
155
+ runHandle: (handle: Handle, args: Array<any>): Promise<mixed> =>
156
+ this.workerApi.callChild(nullthrows(handle.childId), {
157
+ handle: handle.id,
158
+ args,
159
+ }),
160
+ getSharedReference: (ref: SharedReference) =>
161
+ this.sharedReferences.get(ref),
126
162
  resolveSharedReference: (value: mixed) =>
127
163
  this.sharedReferencesByValue.get(value),
128
164
  };
@@ -155,30 +191,42 @@ export default class WorkerFarm extends EventEmitter {
155
191
  );
156
192
  }
157
193
 
158
- createHandle(method: string): HandleFunction {
159
- return (...args) => {
194
+ createHandle(method: string, useMainThread: boolean = false): HandleFunction {
195
+ return async (...args) => {
160
196
  // Child process workers are slow to start (~600ms).
161
197
  // While we're waiting, just run on the main thread.
162
198
  // This significantly speeds up startup time.
163
- if (this.shouldUseRemoteWorkers()) {
199
+ if (this.shouldUseRemoteWorkers() && !useMainThread) {
164
200
  return this.addCall(method, [...args, false]);
165
201
  } else {
166
202
  if (this.options.warmWorkers && this.shouldStartRemoteWorkers()) {
167
203
  this.warmupWorker(method, args);
168
204
  }
169
205
 
170
- let processedArgs = restoreDeserializedObject(
171
- prepareForSerialization([...args, false]),
172
- );
206
+ let processedArgs;
207
+ if (!useMainThread) {
208
+ processedArgs = restoreDeserializedObject(
209
+ prepareForSerialization([...args, false]),
210
+ );
211
+ } else {
212
+ processedArgs = args;
213
+ }
214
+
215
+ if (this.localWorkerInit != null) {
216
+ await this.localWorkerInit;
217
+ this.localWorkerInit = null;
218
+ }
173
219
  return this.localWorker[method](this.workerApi, ...processedArgs);
174
220
  }
175
221
  };
176
222
  }
177
223
 
178
- onError(error: ErrorWithCode, worker: Worker) {
224
+ onError(error: ErrorWithCode, worker: Worker): void | Promise<void> {
179
225
  // Handle ipc errors
180
226
  if (error.code === 'ERR_IPC_CHANNEL_CLOSED') {
181
227
  return this.stopWorker(worker);
228
+ } else {
229
+ logger.error(error, '@parcel/workers');
182
230
  }
183
231
  }
184
232
 
@@ -186,7 +234,8 @@ export default class WorkerFarm extends EventEmitter {
186
234
  let worker = new Worker({
187
235
  forcedKillTime: this.options.forcedKillTime,
188
236
  backend: this.options.backend,
189
- patchConsole: this.options.patchConsole,
237
+ shouldPatchConsole: this.options.shouldPatchConsole,
238
+ sharedReferences: this.sharedReferences,
190
239
  });
191
240
 
192
241
  worker.fork(nullthrows(this.options.workerPath));
@@ -231,7 +280,11 @@ export default class WorkerFarm extends EventEmitter {
231
280
  this.startChild();
232
281
  }
233
282
 
234
- for (let worker of this.workers.values()) {
283
+ let workers = [...this.workers.values()].sort(
284
+ (a, b) => a.calls.size - b.calls.size,
285
+ );
286
+
287
+ for (let worker of workers) {
235
288
  if (!this.callQueue.length) {
236
289
  break;
237
290
  }
@@ -241,11 +294,24 @@ export default class WorkerFarm extends EventEmitter {
241
294
  }
242
295
 
243
296
  if (worker.calls.size < this.options.maxConcurrentCallsPerWorker) {
244
- worker.call(this.callQueue.shift());
297
+ this.callWorker(worker, this.callQueue.shift());
245
298
  }
246
299
  }
247
300
  }
248
301
 
302
+ async callWorker(worker: Worker, call: WorkerCall): Promise<void> {
303
+ for (let ref of this.sharedReferences.keys()) {
304
+ if (!worker.sentSharedReferences.has(ref)) {
305
+ await worker.sendSharedReference(
306
+ ref,
307
+ this.getSerializedSharedReference(ref),
308
+ );
309
+ }
310
+ }
311
+
312
+ worker.call(call);
313
+ }
314
+
249
315
  async processRequest(
250
316
  data: {|
251
317
  location: FilePath,
@@ -255,7 +321,7 @@ export default class WorkerFarm extends EventEmitter {
255
321
  let {method, args, location, awaitResponse, idx, handle: handleId} = data;
256
322
  let mod;
257
323
  if (handleId != null) {
258
- mod = nullthrows(this.handles.get(handleId)).fn;
324
+ mod = nullthrows(this.handles.get(handleId)?.fn);
259
325
  } else if (location) {
260
326
  // $FlowFixMe this must be dynamic
261
327
  mod = require(location);
@@ -286,7 +352,6 @@ export default class WorkerFarm extends EventEmitter {
286
352
  }
287
353
  } else {
288
354
  // ESModule default interop
289
- // $FlowFixMe
290
355
  if (mod.__esModule && !mod[method] && mod.default) {
291
356
  mod = mod.default;
292
357
  }
@@ -331,6 +396,10 @@ export default class WorkerFarm extends EventEmitter {
331
396
  async end(): Promise<void> {
332
397
  this.ending = true;
333
398
 
399
+ await Promise.all(
400
+ Array.from(this.workers.values()).map(worker => this.stopWorker(worker)),
401
+ );
402
+
334
403
  for (let handle of this.handles.values()) {
335
404
  handle.dispose();
336
405
  }
@@ -338,9 +407,6 @@ export default class WorkerFarm extends EventEmitter {
338
407
  this.sharedReferences = new Map();
339
408
  this.sharedReferencesByValue = new Map();
340
409
 
341
- await Promise.all(
342
- Array.from(this.workers.values()).map(worker => this.stopWorker(worker)),
343
- );
344
410
  this.ending = false;
345
411
  }
346
412
 
@@ -362,40 +428,37 @@ export default class WorkerFarm extends EventEmitter {
362
428
  );
363
429
  }
364
430
 
365
- createReverseHandle(fn: HandleFunction) {
366
- let handle = new Handle({fn, workerApi: this.workerApi});
431
+ createReverseHandle(fn: HandleFunction): Handle {
432
+ let handle = new Handle({fn});
367
433
  this.handles.set(handle.id, handle);
368
434
  return handle;
369
435
  }
370
436
 
371
- async createSharedReference(value: mixed) {
437
+ createSharedReference(
438
+ value: mixed,
439
+ isCacheable: boolean = true,
440
+ ): {|ref: SharedReference, dispose(): Promise<mixed>|} {
372
441
  let ref = referenceId++;
373
442
  this.sharedReferences.set(ref, value);
374
443
  this.sharedReferencesByValue.set(value, ref);
375
- let promises = [];
376
- for (let worker of this.workers.values()) {
377
- promises.push(
378
- new Promise((resolve, reject) => {
379
- worker.call({
380
- method: 'createSharedReference',
381
- args: [ref, value],
382
- resolve,
383
- reject,
384
- retries: 0,
385
- });
386
- }),
387
- );
444
+ if (!isCacheable) {
445
+ this.serializedSharedReferences.set(ref, null);
388
446
  }
389
447
 
390
- await Promise.all(promises);
391
-
392
448
  return {
393
449
  ref,
394
450
  dispose: () => {
395
451
  this.sharedReferences.delete(ref);
396
452
  this.sharedReferencesByValue.delete(value);
453
+ this.serializedSharedReferences.delete(ref);
454
+
397
455
  let promises = [];
398
456
  for (let worker of this.workers.values()) {
457
+ if (!worker.sentSharedReferences.has(ref)) {
458
+ continue;
459
+ }
460
+
461
+ worker.sentSharedReferences.delete(ref);
399
462
  promises.push(
400
463
  new Promise((resolve, reject) => {
401
464
  worker.call({
@@ -403,6 +466,7 @@ export default class WorkerFarm extends EventEmitter {
403
466
  args: [ref],
404
467
  resolve,
405
468
  reject,
469
+ skipReadyCheck: true,
406
470
  retries: 0,
407
471
  });
408
472
  }),
@@ -413,6 +477,24 @@ export default class WorkerFarm extends EventEmitter {
413
477
  };
414
478
  }
415
479
 
480
+ getSerializedSharedReference(ref: SharedReference): ArrayBuffer {
481
+ let cached = this.serializedSharedReferences.get(ref);
482
+ if (cached) {
483
+ return cached;
484
+ }
485
+
486
+ let value = this.sharedReferences.get(ref);
487
+ let buf = serialize(value).buffer;
488
+
489
+ // If the reference was created with the isCacheable option set to false,
490
+ // serializedSharedReferences will contain `null` as the value.
491
+ if (cached !== null) {
492
+ this.serializedSharedReferences.set(ref, buf);
493
+ }
494
+
495
+ return buf;
496
+ }
497
+
416
498
  async startProfile() {
417
499
  let promises = [];
418
500
  for (let worker of this.workers.values()) {
@@ -424,6 +506,7 @@ export default class WorkerFarm extends EventEmitter {
424
506
  resolve,
425
507
  reject,
426
508
  retries: 0,
509
+ skipReadyCheck: true,
427
510
  });
428
511
  }),
429
512
  );
@@ -453,6 +536,7 @@ export default class WorkerFarm extends EventEmitter {
453
536
  resolve,
454
537
  reject,
455
538
  retries: 0,
539
+ skipReadyCheck: true,
456
540
  });
457
541
  }),
458
542
  );
@@ -460,7 +544,7 @@ export default class WorkerFarm extends EventEmitter {
460
544
 
461
545
  var profiles = await Promise.all(promises);
462
546
  let trace = new Trace();
463
- let filename = `profile-${profileId++}.trace`;
547
+ let filename = `profile-${getTimeId()}.trace`;
464
548
  let stream = trace.pipe(fs.createWriteStream(filename));
465
549
 
466
550
  for (let profile of profiles) {
@@ -474,21 +558,84 @@ export default class WorkerFarm extends EventEmitter {
474
558
 
475
559
  logger.info({
476
560
  origin: '@parcel/workers',
477
- message: `Wrote profile to ${filename}`,
561
+ message: md`Wrote profile to ${filename}`,
478
562
  });
479
563
  }
480
564
 
481
- static getNumWorkers() {
565
+ async callAllWorkers(method: string, args: Array<any>) {
566
+ let promises = [];
567
+ for (let worker of this.workers.values()) {
568
+ promises.push(
569
+ new Promise((resolve, reject) => {
570
+ worker.call({
571
+ method,
572
+ args,
573
+ resolve,
574
+ reject,
575
+ retries: 0,
576
+ });
577
+ }),
578
+ );
579
+ }
580
+
581
+ promises.push(this.localWorker[method](this.workerApi, ...args));
582
+ await Promise.all(promises);
583
+ }
584
+
585
+ async takeHeapSnapshot() {
586
+ let snapshotId = getTimeId();
587
+
588
+ try {
589
+ let snapshotPaths = await Promise.all(
590
+ [...this.workers.values()].map(
591
+ worker =>
592
+ new Promise((resolve, reject) => {
593
+ worker.call({
594
+ method: 'takeHeapSnapshot',
595
+ args: [snapshotId],
596
+ resolve,
597
+ reject,
598
+ retries: 0,
599
+ skipReadyCheck: true,
600
+ });
601
+ }),
602
+ ),
603
+ );
604
+
605
+ logger.info({
606
+ origin: '@parcel/workers',
607
+ message: md`Wrote heap snapshots to the following paths:\n${snapshotPaths.join(
608
+ '\n',
609
+ )}`,
610
+ });
611
+ } catch {
612
+ logger.error({
613
+ origin: '@parcel/workers',
614
+ message: 'Unable to take heap snapshots. Note: requires Node 11.13.0+',
615
+ });
616
+ }
617
+ }
618
+
619
+ static getNumWorkers(): number {
482
620
  return process.env.PARCEL_WORKERS
483
621
  ? parseInt(process.env.PARCEL_WORKERS, 10)
484
- : cpuCount();
622
+ : Math.ceil(cpuCount() / 2);
485
623
  }
486
624
 
487
- static isWorker() {
625
+ static isWorker(): boolean {
488
626
  return !!child;
489
627
  }
490
628
 
491
- static getWorkerApi() {
629
+ static getWorkerApi(): {|
630
+ callMaster: (
631
+ request: CallRequest,
632
+ awaitResponse?: ?boolean,
633
+ ) => Promise<mixed>,
634
+ createReverseHandle: (fn: (...args: Array<any>) => mixed) => Handle,
635
+ getSharedReference: (ref: SharedReference) => mixed,
636
+ resolveSharedReference: (value: mixed) => void | SharedReference,
637
+ runHandle: (handle: Handle, args: Array<any>) => Promise<mixed>,
638
+ |} {
492
639
  invariant(
493
640
  child != null,
494
641
  'WorkerFarm.getWorkerApi can only be called within workers',
@@ -496,7 +643,20 @@ export default class WorkerFarm extends EventEmitter {
496
643
  return child.workerApi;
497
644
  }
498
645
 
499
- static getConcurrentCallsPerWorker() {
500
- return parseInt(process.env.PARCEL_MAX_CONCURRENT_CALLS, 10) || 5;
646
+ static getConcurrentCallsPerWorker(): number {
647
+ return parseInt(process.env.PARCEL_MAX_CONCURRENT_CALLS, 10) || 30;
501
648
  }
502
649
  }
650
+
651
+ function getTimeId() {
652
+ let now = new Date();
653
+ return (
654
+ String(now.getFullYear()) +
655
+ String(now.getMonth() + 1).padStart(2, '0') +
656
+ String(now.getDate()).padStart(2, '0') +
657
+ '-' +
658
+ String(now.getHours()).padStart(2, '0') +
659
+ String(now.getMinutes()).padStart(2, '0') +
660
+ String(now.getSeconds()).padStart(2, '0')
661
+ );
662
+ }
package/src/bus.js CHANGED
@@ -20,4 +20,4 @@ class Bus extends EventEmitter {
20
20
  }
21
21
  }
22
22
 
23
- export default new Bus();
23
+ export default (new Bus(): Bus);
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} 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 Handle from './Handle';
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,21 @@ 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
- workerApi: WorkerApi;
39
42
  handles: Map<number, Handle> = new Map();
40
- sharedReferences: Map<number, mixed> = new Map();
41
- sharedReferencesByValue: Map<mixed, number> = new Map();
43
+ sharedReferences: Map<SharedReference, mixed> = new Map();
44
+ sharedReferencesByValue: Map<mixed, SharedReference> = new Map();
42
45
 
43
46
  constructor(ChildBackend: Class<ChildImpl>) {
44
47
  this.child = new ChildBackend(
45
- this.messageListener.bind(this),
46
- this.handleEnd.bind(this),
48
+ m => {
49
+ this.messageListener(m);
50
+ },
51
+ () => this.handleEnd(),
47
52
  );
48
53
 
49
54
  // Monitior all logging events inside this child process and forward to
@@ -53,19 +58,31 @@ export class Child {
53
58
  });
54
59
  }
55
60
 
56
- workerApi = {
61
+ workerApi: {|
62
+ callMaster: (
63
+ request: CallRequest,
64
+ awaitResponse?: ?boolean,
65
+ ) => Promise<mixed>,
66
+ createReverseHandle: (fn: (...args: Array<any>) => mixed) => Handle,
67
+ getSharedReference: (ref: SharedReference) => mixed,
68
+ resolveSharedReference: (value: mixed) => void | SharedReference,
69
+ runHandle: (handle: Handle, args: Array<any>) => Promise<mixed>,
70
+ |} = {
57
71
  callMaster: (
58
72
  request: CallRequest,
59
73
  awaitResponse: ?boolean = true,
60
74
  ): Promise<mixed> => this.addCall(request, awaitResponse),
61
75
  createReverseHandle: (fn: (...args: Array<any>) => mixed): Handle =>
62
76
  this.createReverseHandle(fn),
63
- getSharedReference: (ref: number) => this.sharedReferences.get(ref),
77
+ runHandle: (handle: Handle, args: Array<any>): Promise<mixed> =>
78
+ this.workerApi.callMaster({handle: handle.id, args}, true),
79
+ getSharedReference: (ref: SharedReference) =>
80
+ this.sharedReferences.get(ref),
64
81
  resolveSharedReference: (value: mixed) =>
65
82
  this.sharedReferencesByValue.get(value),
66
83
  };
67
84
 
68
- messageListener(message: WorkerMessage): void | Promise<void> {
85
+ messageListener(message: WorkerMessage): Async<void> {
69
86
  if (message.type === 'response') {
70
87
  return this.handleResponse(message);
71
88
  } else if (message.type === 'request') {
@@ -77,10 +94,14 @@ export class Child {
77
94
  this.child.send(data);
78
95
  }
79
96
 
80
- childInit(module: string, childId: number): void {
97
+ async childInit(module: string, childId: number): Promise<void> {
81
98
  // $FlowFixMe this must be dynamic
82
99
  this.module = require(module);
83
100
  this.childId = childId;
101
+
102
+ if (this.module.childInit != null) {
103
+ await this.module.childInit();
104
+ }
84
105
  }
85
106
 
86
107
  async handleRequest(data: WorkerRequest): Promise<void> {
@@ -106,7 +127,7 @@ export class Child {
106
127
  let result;
107
128
  if (handleId != null) {
108
129
  try {
109
- let fn = nullthrows(this.handles.get(handleId)).fn;
130
+ let fn = nullthrows(this.handles.get(handleId)?.fn);
110
131
  result = responseFromContent(fn(...args));
111
132
  } catch (e) {
112
133
  result = errorResponseFromError(e);
@@ -114,13 +135,13 @@ export class Child {
114
135
  } else if (method === 'childInit') {
115
136
  try {
116
137
  let [moduleName, childOptions] = args;
117
- if (childOptions.patchConsole) {
138
+ if (childOptions.shouldPatchConsole) {
118
139
  patchConsole();
119
140
  } else {
120
141
  unpatchConsole();
121
142
  }
122
143
 
123
- result = responseFromContent(this.childInit(moduleName, child));
144
+ result = responseFromContent(await this.childInit(moduleName, child));
124
145
  } catch (e) {
125
146
  result = errorResponseFromError(e);
126
147
  }
@@ -138,13 +159,37 @@ export class Child {
138
159
  } catch (e) {
139
160
  result = errorResponseFromError(e);
140
161
  }
162
+ } else if (method === 'takeHeapSnapshot') {
163
+ try {
164
+ let v8 = require('v8');
165
+ result = responseFromContent(
166
+ v8.writeHeapSnapshot(
167
+ 'heap-' +
168
+ args[0] +
169
+ '-' +
170
+ (this.childId ? 'worker' + this.childId : 'main') +
171
+ '.heapsnapshot',
172
+ ),
173
+ );
174
+ } catch (e) {
175
+ result = errorResponseFromError(e);
176
+ }
141
177
  } else if (method === 'createSharedReference') {
142
- let [ref, value] = args;
178
+ let [ref, _value] = args;
179
+ let value =
180
+ _value instanceof ArrayBuffer
181
+ ? // In the case the value is pre-serialized as a buffer,
182
+ // deserialize it.
183
+ deserialize(Buffer.from(_value))
184
+ : _value;
143
185
  this.sharedReferences.set(ref, value);
144
186
  this.sharedReferencesByValue.set(value, ref);
145
187
  result = responseFromContent(null);
146
188
  } else if (method === 'deleteSharedReference') {
147
- this.sharedReferences.delete(args[0]);
189
+ let ref = args[0];
190
+ let value = this.sharedReferences.get(ref);
191
+ this.sharedReferencesByValue.delete(value);
192
+ this.sharedReferences.delete(ref);
148
193
  result = responseFromContent(null);
149
194
  } else {
150
195
  try {
@@ -157,7 +202,11 @@ export class Child {
157
202
  }
158
203
  }
159
204
 
160
- this.send(result);
205
+ try {
206
+ this.send(result);
207
+ } catch (e) {
208
+ result = this.send(errorResponseFromError(e));
209
+ }
161
210
  }
162
211
 
163
212
  handleResponse(data: WorkerResponse): void {
@@ -189,6 +238,7 @@ export class Child {
189
238
  ...request,
190
239
  type: 'request',
191
240
  child: this.childId,
241
+ // $FlowFixMe Added in Flow 0.121.0 upgrade in #4381
192
242
  awaitResponse,
193
243
  resolve: () => {},
194
244
  reject: () => {},
@@ -241,10 +291,9 @@ export class Child {
241
291
  this.loggerDisposable.dispose();
242
292
  }
243
293
 
244
- createReverseHandle(fn: (...args: Array<any>) => mixed) {
294
+ createReverseHandle(fn: (...args: Array<any>) => mixed): Handle {
245
295
  let handle = new Handle({
246
296
  fn,
247
- workerApi: this.workerApi,
248
297
  childId: this.childId,
249
298
  });
250
299
  this.handles.set(handle.id, handle);