@@ -20,6 +20,7 @@ const {
20
20
const EventEmitter = require ( 'events' ) ;
21
21
const assert = require ( 'internal/assert' ) ;
22
22
const path = require ( 'path' ) ;
23
+ const { timeOrigin } = internalBinding ( 'performance' ) ;
23
24
24
25
const errorCodes = require ( 'internal/errors' ) . codes ;
25
26
const {
@@ -70,6 +71,8 @@ const kOnMessage = Symbol('kOnMessage');
70
71
const kOnCouldNotSerializeErr = Symbol ( 'kOnCouldNotSerializeErr' ) ;
71
72
const kOnErrorMessage = Symbol ( 'kOnErrorMessage' ) ;
72
73
const kParentSideStdio = Symbol ( 'kParentSideStdio' ) ;
74
+ const kLoopStartTime = Symbol ( 'kLoopStartTime' ) ;
75
+ const kIsOnline = Symbol ( 'kIsOnline' ) ;
73
76
74
77
const SHARE_ENV = SymbolFor ( 'nodejs.worker_threads.SHARE_ENV' ) ;
75
78
let debug = require ( 'internal/util/debuglog' ) . debuglog ( 'worker' , ( fn ) => {
@@ -223,6 +226,12 @@ class Worker extends EventEmitter {
223
226
null ,
224
227
hasStdin : ! ! options . stdin
225
228
} , transferList ) ;
229
+ // Use this to cache the Worker's loopStart value once available.
230
+ this [ kLoopStartTime ] = - 1 ;
231
+ this [ kIsOnline ] = false ;
232
+ this . performance = {
233
+ eventLoopUtilization : eventLoopUtilization . bind ( this ) ,
234
+ } ;
226
235
// Actually start the new thread now that everything is in place.
227
236
this [ kHandle ] . startThread ( ) ;
228
237
}
@@ -254,6 +263,7 @@ class Worker extends EventEmitter {
254
263
[ kOnMessage ] ( message ) {
255
264
switch ( message . type ) {
256
265
case messageTypes . UP_AND_RUNNING :
266
+ this [ kIsOnline ] = true ;
257
267
return this . emit ( 'online' ) ;
258
268
case messageTypes . COULD_NOT_SERIALIZE_ERROR :
259
269
return this [ kOnCouldNotSerializeErr ] ( ) ;
@@ -415,6 +425,52 @@ function makeResourceLimits(float64arr) {
415
425
} ;
416
426
}
417
427
428
+ function eventLoopUtilization ( util1 , util2 ) {
429
+ // TODO(trevnorris): Works to solve the thread-safe read/write issue of
430
+ // loopTime, but has the drawback that it can't be set until the event loop
431
+ // has had a chance to turn. So it will be impossible to read the ELU of
432
+ // a worker thread immediately after it's been created.
433
+ if ( ! this [ kIsOnline ] || ! this [ kHandle ] ) {
434
+ return { idle : 0 , active : 0 , utilization : 0 } ;
435
+ }
436
+
437
+ // Cache loopStart, since it's only written to once.
438
+ if ( this [ kLoopStartTime ] === - 1 ) {
439
+ this [ kLoopStartTime ] = this [ kHandle ] . loopStartTime ( ) ;
440
+ if ( this [ kLoopStartTime ] === - 1 )
441
+ return { idle : 0 , active : 0 , utilization : 0 } ;
442
+ }
443
+
444
+ if ( util2 ) {
445
+ const idle = util1 . idle - util2 . idle ;
446
+ const active = util1 . active - util2 . active ;
447
+ return { idle, active, utilization : active / ( idle + active ) } ;
448
+ }
449
+
450
+ const idle = this [ kHandle ] . loopIdleTime ( ) ;
451
+
452
+ // Using performance.now() here is fine since it's always the time from
453
+ // the beginning of the process, and is why it needs to be offset by the
454
+ // loopStart time (which is also calculated from the beginning of the
455
+ // process).
456
+ const active = now ( ) - this [ kLoopStartTime ] - idle ;
457
+
458
+ if ( ! util1 ) {
459
+ return { idle, active, utilization : active / ( idle + active ) } ;
460
+ }
461
+
462
+ const idle_delta = idle - util1 . idle ;
463
+ const active_delta = active - util1 . active ;
464
+ const utilization = active_delta / ( idle_delta + active_delta ) ;
465
+ return { idle : idle_delta , active : active_delta , utilization } ;
466
+ }
467
+
468
+ // Duplicate code from performance.now() so don't need to require perf_hooks.
469
+ function now ( ) {
470
+ const hr = process . hrtime ( ) ;
471
+ return ( hr [ 0 ] * 1000 + hr [ 1 ] / 1e6 ) - timeOrigin ;
472
+ }
473
+
418
474
module . exports = {
419
475
ownsProcessState,
420
476
isMainThread,
0 commit comments