@@ -52,11 +52,14 @@ const trigger_async_id_symbol = Symbol('triggerAsyncId');
5252
5353// *Must* match Environment::ImmediateInfo::Fields in src/env.h.
5454const kCount = 0 ;
55- const kHasOutstanding = 1 ;
55+ const kRefCount = 1 ;
56+ const kHasOutstanding = 2 ;
5657
57- const [ activateImmediateCheck , immediateInfo ] =
58+ const [ immediateInfo , toggleImmediateRef ] =
5859 setImmediateCallback ( processImmediate ) ;
5960
61+ const kRefed = Symbol ( 'refed' ) ;
62+
6063// Timeout values > TIMEOUT_MAX are set to 1.
6164const TIMEOUT_MAX = 2 ** 31 - 1 ;
6265
@@ -690,42 +693,41 @@ function processImmediate() {
690693 const queue = outstandingQueue . head !== null ?
691694 outstandingQueue : immediateQueue ;
692695 var immediate = queue . head ;
693- var tail = queue . tail ;
696+ const tail = queue . tail ;
694697
695698 // Clear the linked list early in case new `setImmediate()` calls occur while
696699 // immediate callbacks are executed
697700 queue . head = queue . tail = null ;
698701
699- while ( immediate !== null ) {
700- if ( ! immediate . _onImmediate ) {
701- immediate = immediate . _idleNext ;
702- continue ;
703- }
702+ let count = 0 ;
703+ let refCount = 0 ;
704704
705- // Save next in case `clearImmediate (immediate)` is called from callback
706- const next = immediate . _idleNext ;
705+ while ( immediate !== null ) {
706+ immediate . _destroyed = true ;
707707
708708 const asyncId = immediate [ async_id_symbol ] ;
709709 emitBefore ( asyncId , immediate [ trigger_async_id_symbol ] ) ;
710710
711- tryOnImmediate ( immediate , next , tail ) ;
711+ count ++ ;
712+ if ( immediate [ kRefed ] )
713+ refCount ++ ;
714+ immediate [ kRefed ] = undefined ;
715+
716+ tryOnImmediate ( immediate , tail , count , refCount ) ;
712717
713718 emitAfter ( asyncId ) ;
714719
715- // If `clearImmediate(immediate)` wasn't called from the callback, use the
716- // `immediate`'s next item
717- if ( immediate . _idleNext !== null )
718- immediate = immediate . _idleNext ;
719- else
720- immediate = next ;
720+ immediate = immediate . _idleNext ;
721721 }
722722
723+ immediateInfo [ kCount ] -= count ;
724+ immediateInfo [ kRefCount ] -= refCount ;
723725 immediateInfo [ kHasOutstanding ] = 0 ;
724726}
725727
726728// An optimization so that the try/finally only de-optimizes (since at least v8
727729// 4.7) what is in this smaller function.
728- function tryOnImmediate ( immediate , next , oldTail ) {
730+ function tryOnImmediate ( immediate , oldTail , count , refCount ) {
729731 var threw = true ;
730732 try {
731733 // make the actual call outside the try/finally to allow it to be optimized
@@ -734,21 +736,21 @@ function tryOnImmediate(immediate, next, oldTail) {
734736 } finally {
735737 immediate . _onImmediate = null ;
736738
737- if ( ! immediate . _destroyed ) {
738- immediate . _destroyed = true ;
739- immediateInfo [ kCount ] -- ;
740-
741- if ( async_hook_fields [ kDestroy ] > 0 ) {
742- emitDestroy ( immediate [ async_id_symbol ] ) ;
743- }
739+ if ( async_hook_fields [ kDestroy ] > 0 ) {
740+ emitDestroy ( immediate [ async_id_symbol ] ) ;
744741 }
745742
746- if ( threw && ( immediate . _idleNext !== null || next !== null ) ) {
747- // Handle any remaining Immediates after error handling has resolved,
748- // assuming we're still alive to do so.
749- outstandingQueue . head = immediate . _idleNext || next ;
750- outstandingQueue . tail = oldTail ;
751- immediateInfo [ kHasOutstanding ] = 1 ;
743+ if ( threw ) {
744+ immediateInfo [ kCount ] -= count ;
745+ immediateInfo [ kRefCount ] -= refCount ;
746+
747+ if ( immediate . _idleNext !== null ) {
748+ // Handle any remaining Immediates after error handling has resolved,
749+ // assuming we're still alive to do so.
750+ outstandingQueue . head = immediate . _idleNext ;
751+ outstandingQueue . tail = oldTail ;
752+ immediateInfo [ kHasOutstanding ] = 1 ;
753+ }
752754 }
753755 }
754756}
@@ -763,31 +765,51 @@ function runCallback(timer) {
763765}
764766
765767
766- function Immediate ( callback , args ) {
767- this . _idleNext = null ;
768- this . _idlePrev = null ;
769- // this must be set to null first to avoid function tracking
770- // on the hidden class, revisit in V8 versions after 6.2
771- this . _onImmediate = null ;
772- this . _onImmediate = callback ;
773- this . _argv = args ;
774- this . _destroyed = false ;
768+ const Immediate = class Immediate {
769+ constructor ( callback , args ) {
770+ this . _idleNext = null ;
771+ this . _idlePrev = null ;
772+ // this must be set to null first to avoid function tracking
773+ // on the hidden class, revisit in V8 versions after 6.2
774+ this . _onImmediate = null ;
775+ this . _onImmediate = callback ;
776+ this . _argv = args ;
777+ this . _destroyed = false ;
778+ this [ kRefed ] = false ;
779+
780+ this [ async_id_symbol ] = ++ async_id_fields [ kAsyncIdCounter ] ;
781+ this [ trigger_async_id_symbol ] = getDefaultTriggerAsyncId ( ) ;
782+ if ( async_hook_fields [ kInit ] > 0 ) {
783+ emitInit ( this [ async_id_symbol ] ,
784+ 'Immediate' ,
785+ this [ trigger_async_id_symbol ] ,
786+ this ) ;
787+ }
775788
776- this [ async_id_symbol ] = ++ async_id_fields [ kAsyncIdCounter ] ;
777- this [ trigger_async_id_symbol ] = getDefaultTriggerAsyncId ( ) ;
778- if ( async_hook_fields [ kInit ] > 0 ) {
779- emitInit ( this [ async_id_symbol ] ,
780- 'Immediate' ,
781- this [ trigger_async_id_symbol ] ,
782- this ) ;
789+ this . ref ( ) ;
790+ immediateInfo [ kCount ] ++ ;
791+
792+ immediateQueue . append ( this ) ;
783793 }
784794
785- if ( immediateInfo [ kCount ] === 0 )
786- activateImmediateCheck ( ) ;
787- immediateInfo [ kCount ] ++ ;
795+ ref ( ) {
796+ if ( this [ kRefed ] === false ) {
797+ this [ kRefed ] = true ;
798+ if ( immediateInfo [ kRefCount ] ++ === 0 )
799+ toggleImmediateRef ( true ) ;
800+ }
801+ return this ;
802+ }
788803
789- immediateQueue . append ( this ) ;
790- }
804+ unref ( ) {
805+ if ( this [ kRefed ] === true ) {
806+ this [ kRefed ] = false ;
807+ if ( -- immediateInfo [ kRefCount ] === 0 )
808+ toggleImmediateRef ( false ) ;
809+ }
810+ return this ;
811+ }
812+ } ;
791813
792814function setImmediate ( callback , arg1 , arg2 , arg3 ) {
793815 if ( typeof callback !== 'function' ) {
@@ -827,15 +849,18 @@ exports.setImmediate = setImmediate;
827849
828850
829851exports . clearImmediate = function ( immediate ) {
830- if ( ! immediate ) return ;
852+ if ( ! immediate || immediate . _destroyed )
853+ return ;
831854
832- if ( ! immediate . _destroyed ) {
833- immediateInfo [ kCount ] -- ;
834- immediate . _destroyed = true ;
855+ immediateInfo [ kCount ] -- ;
856+ immediate . _destroyed = true ;
835857
836- if ( async_hook_fields [ kDestroy ] > 0 ) {
837- emitDestroy ( immediate [ async_id_symbol ] ) ;
838- }
858+ if ( immediate [ kRefed ] && -- immediateInfo [ kRefCount ] === 0 )
859+ toggleImmediateRef ( false ) ;
860+ immediate [ kRefed ] = undefined ;
861+
862+ if ( async_hook_fields [ kDestroy ] > 0 ) {
863+ emitDestroy ( immediate [ async_id_symbol ] ) ;
839864 }
840865
841866 immediate . _onImmediate = null ;
0 commit comments