@@ -52,11 +52,14 @@ const trigger_async_id_symbol = Symbol('triggerAsyncId');
52
52
53
53
// *Must* match Environment::ImmediateInfo::Fields in src/env.h.
54
54
const kCount = 0 ;
55
- const kHasOutstanding = 1 ;
55
+ const kRefCount = 1 ;
56
+ const kHasOutstanding = 2 ;
56
57
57
- const [ activateImmediateCheck , immediateInfo ] =
58
+ const [ immediateInfo , toggleImmediateRef ] =
58
59
setImmediateCallback ( processImmediate ) ;
59
60
61
+ const kRefed = Symbol ( 'refed' ) ;
62
+
60
63
// Timeout values > TIMEOUT_MAX are set to 1.
61
64
const TIMEOUT_MAX = 2 ** 31 - 1 ;
62
65
@@ -690,42 +693,41 @@ function processImmediate() {
690
693
const queue = outstandingQueue . head !== null ?
691
694
outstandingQueue : immediateQueue ;
692
695
var immediate = queue . head ;
693
- var tail = queue . tail ;
696
+ const tail = queue . tail ;
694
697
695
698
// Clear the linked list early in case new `setImmediate()` calls occur while
696
699
// immediate callbacks are executed
697
700
queue . head = queue . tail = null ;
698
701
699
- while ( immediate !== null ) {
700
- if ( ! immediate . _onImmediate ) {
701
- immediate = immediate . _idleNext ;
702
- continue ;
703
- }
702
+ let count = 0 ;
703
+ let refCount = 0 ;
704
704
705
- // Save next in case `clearImmediate (immediate)` is called from callback
706
- const next = immediate . _idleNext ;
705
+ while ( immediate !== null ) {
706
+ immediate . _destroyed = true ;
707
707
708
708
const asyncId = immediate [ async_id_symbol ] ;
709
709
emitBefore ( asyncId , immediate [ trigger_async_id_symbol ] ) ;
710
710
711
- tryOnImmediate ( immediate , next , tail ) ;
711
+ count ++ ;
712
+ if ( immediate [ kRefed ] )
713
+ refCount ++ ;
714
+ immediate [ kRefed ] = undefined ;
715
+
716
+ tryOnImmediate ( immediate , tail , count , refCount ) ;
712
717
713
718
emitAfter ( asyncId ) ;
714
719
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 ;
721
721
}
722
722
723
+ immediateInfo [ kCount ] -= count ;
724
+ immediateInfo [ kRefCount ] -= refCount ;
723
725
immediateInfo [ kHasOutstanding ] = 0 ;
724
726
}
725
727
726
728
// An optimization so that the try/finally only de-optimizes (since at least v8
727
729
// 4.7) what is in this smaller function.
728
- function tryOnImmediate ( immediate , next , oldTail ) {
730
+ function tryOnImmediate ( immediate , oldTail , count , refCount ) {
729
731
var threw = true ;
730
732
try {
731
733
// make the actual call outside the try/finally to allow it to be optimized
@@ -734,21 +736,21 @@ function tryOnImmediate(immediate, next, oldTail) {
734
736
} finally {
735
737
immediate . _onImmediate = null ;
736
738
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 ] ) ;
744
741
}
745
742
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
+ }
752
754
}
753
755
}
754
756
}
@@ -763,31 +765,51 @@ function runCallback(timer) {
763
765
}
764
766
765
767
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
+ }
775
788
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 ) ;
783
793
}
784
794
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
+ }
788
803
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
+ } ;
791
813
792
814
function setImmediate ( callback , arg1 , arg2 , arg3 ) {
793
815
if ( typeof callback !== 'function' ) {
@@ -827,15 +849,18 @@ exports.setImmediate = setImmediate;
827
849
828
850
829
851
exports . clearImmediate = function ( immediate ) {
830
- if ( ! immediate ) return ;
852
+ if ( ! immediate || immediate . _destroyed )
853
+ return ;
831
854
832
- if ( ! immediate . _destroyed ) {
833
- immediateInfo [ kCount ] -- ;
834
- immediate . _destroyed = true ;
855
+ immediateInfo [ kCount ] -- ;
856
+ immediate . _destroyed = true ;
835
857
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 ] ) ;
839
864
}
840
865
841
866
immediate . _onImmediate = null ;
0 commit comments