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