@@ -17,6 +17,7 @@ const {
17
17
const EventEmitter = require ( 'events' ) ;
18
18
const assert = require ( 'internal/assert' ) ;
19
19
const path = require ( 'path' ) ;
20
+ const { timeOrigin } = internalBinding ( 'performance' ) ;
20
21
21
22
const errorCodes = require ( 'internal/errors' ) . codes ;
22
23
const {
@@ -67,6 +68,8 @@ const kOnMessage = Symbol('kOnMessage');
67
68
const kOnCouldNotSerializeErr = Symbol ( 'kOnCouldNotSerializeErr' ) ;
68
69
const kOnErrorMessage = Symbol ( 'kOnErrorMessage' ) ;
69
70
const kParentSideStdio = Symbol ( 'kParentSideStdio' ) ;
71
+ const kLoopStartTime = Symbol ( 'kLoopStartTime' ) ;
72
+ const kIsOnline = Symbol ( 'kIsOnline' ) ;
70
73
71
74
const SHARE_ENV = SymbolFor ( 'nodejs.worker_threads.SHARE_ENV' ) ;
72
75
let debug = require ( 'internal/util/debuglog' ) . debuglog ( 'worker' , ( fn ) => {
@@ -214,6 +217,12 @@ class Worker extends EventEmitter {
214
217
null ,
215
218
hasStdin : ! ! options . stdin
216
219
} , transferList ) ;
220
+ // Use this to cache the Worker's loopStart value once available.
221
+ this [ kLoopStartTime ] = - 1 ;
222
+ this [ kIsOnline ] = false ;
223
+ this . performance = {
224
+ eventLoopUtilization : eventLoopUtilization . bind ( this ) ,
225
+ } ;
217
226
// Actually start the new thread now that everything is in place.
218
227
this [ kHandle ] . startThread ( ) ;
219
228
}
@@ -245,6 +254,7 @@ class Worker extends EventEmitter {
245
254
[ kOnMessage ] ( message ) {
246
255
switch ( message . type ) {
247
256
case messageTypes . UP_AND_RUNNING :
257
+ this [ kIsOnline ] = true ;
248
258
return this . emit ( 'online' ) ;
249
259
case messageTypes . COULD_NOT_SERIALIZE_ERROR :
250
260
return this [ kOnCouldNotSerializeErr ] ( ) ;
@@ -406,6 +416,52 @@ function makeResourceLimits(float64arr) {
406
416
} ;
407
417
}
408
418
419
+ function eventLoopUtilization ( util1 , util2 ) {
420
+ // TODO(trevnorris): Works to solve the thread-safe read/write issue of
421
+ // loopTime, but has the drawback that it can't be set until the event loop
422
+ // has had a chance to turn. So it will be impossible to read the ELU of
423
+ // a worker thread immediately after it's been created.
424
+ if ( ! this [ kIsOnline ] || ! this [ kHandle ] ) {
425
+ return { idle : 0 , active : 0 , utilization : 0 } ;
426
+ }
427
+
428
+ // Cache loopStart, since it's only written to once.
429
+ if ( this [ kLoopStartTime ] === - 1 ) {
430
+ this [ kLoopStartTime ] = this [ kHandle ] . loopStartTime ( ) ;
431
+ if ( this [ kLoopStartTime ] === - 1 )
432
+ return { idle : 0 , active : 0 , utilization : 0 } ;
433
+ }
434
+
435
+ if ( util2 ) {
436
+ const idle = util1 . idle - util2 . idle ;
437
+ const active = util1 . active - util2 . active ;
438
+ return { idle, active, utilization : active / ( idle + active ) } ;
439
+ }
440
+
441
+ const idle = this [ kHandle ] . loopIdleTime ( ) ;
442
+
443
+ // Using performance.now() here is fine since it's always the time from
444
+ // the beginning of the process, and is why it needs to be offset by the
445
+ // loopStart time (which is also calculated from the beginning of the
446
+ // process).
447
+ const active = now ( ) - this [ kLoopStartTime ] - idle ;
448
+
449
+ if ( ! util1 ) {
450
+ return { idle, active, utilization : active / ( idle + active ) } ;
451
+ }
452
+
453
+ const idle_delta = idle - util1 . idle ;
454
+ const active_delta = active - util1 . active ;
455
+ const utilization = active_delta / ( idle_delta + active_delta ) ;
456
+ return { idle : idle_delta , active : active_delta , utilization } ;
457
+ }
458
+
459
+ // Duplicate code from performance.now() so don't need to require perf_hooks.
460
+ function now ( ) {
461
+ const hr = process . hrtime ( ) ;
462
+ return ( hr [ 0 ] * 1000 + hr [ 1 ] / 1e6 ) - timeOrigin ;
463
+ }
464
+
409
465
module . exports = {
410
466
ownsProcessState,
411
467
isMainThread,
0 commit comments