• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const { mustCall, mustCallAtLeast } = require('../common');
4
5const assert = require('assert');
6const {
7  Worker,
8  MessageChannel,
9  MessagePort,
10  parentPort,
11} = require('worker_threads');
12const { eventLoopUtilization, now } = require('perf_hooks').performance;
13
14// Use argv to detect whether we're running as a Worker called by this test vs.
15// this test also being called as a Worker.
16if (process.argv[2] === 'iamalive') {
17  const iaElu = idleActive(eventLoopUtilization());
18  // Checks that the worker bootstrap is running after the event loop started.
19  assert.ok(iaElu > 0, `${iaElu} <= 0`);
20  parentPort.once('message', mustCall((msg) => {
21    assert.ok(msg.metricsCh instanceof MessagePort);
22    msg.metricsCh.on('message', mustCallAtLeast(workerOnMetricsMsg, 1));
23  }));
24  return;
25}
26
27function workerOnMetricsMsg(msg) {
28  if (msg.cmd === 'close') {
29    return this.close();
30  }
31
32  if (msg.cmd === 'elu') {
33    return this.postMessage(eventLoopUtilization());
34  }
35
36  if (msg.cmd === 'spin') {
37    const elu = eventLoopUtilization();
38    const t = now();
39    while (now() - t < msg.dur);
40    return this.postMessage(eventLoopUtilization(elu));
41  }
42}
43
44let worker;
45let metricsCh;
46let mainElu;
47let workerELU;
48
49(function r() {
50  // Force some idle time to accumulate before proceeding with test.
51  if (eventLoopUtilization().idle <= 0)
52    return setTimeout(mustCall(r), 5);
53
54  mainElu = eventLoopUtilization();
55
56  worker = new Worker(__filename, { argv: [ 'iamalive' ] });
57  metricsCh = new MessageChannel();
58  worker.postMessage({ metricsCh: metricsCh.port1 }, [ metricsCh.port1 ]);
59
60  workerELU = worker.performance.eventLoopUtilization;
61  metricsCh.port2.once('message', mustCall(checkWorkerIdle));
62  metricsCh.port2.postMessage({ cmd: 'elu' });
63  // Make sure it's still safe to call eventLoopUtilization() after the worker
64  // hass been closed.
65  worker.on('exit', mustCall(() => {
66    assert.deepStrictEqual(worker.performance.eventLoopUtilization(),
67                           { idle: 0, active: 0, utilization: 0 });
68  }));
69})();
70
71function checkWorkerIdle(wElu) {
72  const perfWorkerElu = workerELU();
73  const tmpMainElu = eventLoopUtilization(mainElu);
74
75  assert.ok(idleActive(wElu) > 0, `${idleActive(wElu)} <= 0`);
76  assert.ok(idleActive(workerELU(wElu)) > 0,
77            `${idleActive(workerELU(wElu))} <= 0`);
78  assert.ok(idleActive(perfWorkerElu) > idleActive(wElu),
79            `${idleActive(perfWorkerElu)} <= ${idleActive(wElu)}`);
80  assert.ok(idleActive(tmpMainElu) > idleActive(perfWorkerElu),
81            `${idleActive(tmpMainElu)} <= ${idleActive(perfWorkerElu)}`);
82
83  wElu = workerELU();
84  setTimeout(mustCall(() => {
85    wElu = workerELU(wElu);
86    // Some clocks fire early. Removing a few milliseconds to cover that.
87    assert.ok(idleActive(wElu) >= 45, `${idleActive(wElu)} < 45`);
88    // Cutting the idle time in half since it's possible that the call took a
89    // lot of resources to process?
90    assert.ok(wElu.idle >= 25, `${wElu.idle} < 25`);
91
92    checkWorkerActive();
93  }), 50);
94}
95
96function checkWorkerActive() {
97  const w = workerELU();
98
99  metricsCh.port2.postMessage({ cmd: 'spin', dur: 50 });
100  metricsCh.port2.once('message', (wElu) => {
101    const w2 = workerELU(w);
102
103    assert.ok(w2.active >= 50, `${w2.active} < 50`);
104    assert.ok(wElu.active >= 50, `${wElu.active} < 50`);
105    assert.ok(idleActive(wElu) < idleActive(w2),
106              `${idleActive(wElu)} >= ${idleActive(w2)}`);
107
108    metricsCh.port2.postMessage({ cmd: 'close' });
109  });
110}
111
112function idleActive(elu) {
113  return elu.idle + elu.active;
114}
115