@@ -17,6 +17,7 @@ const {
1717const EventEmitter = require ( 'events' ) ;
1818const assert = require ( 'internal/assert' ) ;
1919const path = require ( 'path' ) ;
20+ const { timeOrigin } = internalBinding ( 'performance' ) ;
2021
2122const errorCodes = require ( 'internal/errors' ) . codes ;
2223const {
@@ -67,6 +68,8 @@ const kOnMessage = Symbol('kOnMessage');
6768const kOnCouldNotSerializeErr = Symbol ( 'kOnCouldNotSerializeErr' ) ;
6869const kOnErrorMessage = Symbol ( 'kOnErrorMessage' ) ;
6970const kParentSideStdio = Symbol ( 'kParentSideStdio' ) ;
71+ const kLoopStartTime = Symbol ( 'kLoopStartTime' ) ;
72+ const kIsOnline = Symbol ( 'kIsOnline' ) ;
7073
7174const SHARE_ENV = SymbolFor ( 'nodejs.worker_threads.SHARE_ENV' ) ;
7275let debug = require ( 'internal/util/debuglog' ) . debuglog ( 'worker' , ( fn ) => {
@@ -214,6 +217,12 @@ class Worker extends EventEmitter {
214217 null ,
215218 hasStdin : ! ! options . stdin
216219 } , 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+ } ;
217226 // Actually start the new thread now that everything is in place.
218227 this [ kHandle ] . startThread ( ) ;
219228 }
@@ -245,6 +254,7 @@ class Worker extends EventEmitter {
245254 [ kOnMessage ] ( message ) {
246255 switch ( message . type ) {
247256 case messageTypes . UP_AND_RUNNING :
257+ this [ kIsOnline ] = true ;
248258 return this . emit ( 'online' ) ;
249259 case messageTypes . COULD_NOT_SERIALIZE_ERROR :
250260 return this [ kOnCouldNotSerializeErr ] ( ) ;
@@ -406,6 +416,52 @@ function makeResourceLimits(float64arr) {
406416 } ;
407417}
408418
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+
409465module . exports = {
410466 ownsProcessState,
411467 isMainThread,
0 commit comments