@@ -53,11 +53,14 @@ const trigger_async_id_symbol = timerInternals.trigger_async_id_symbol;
53
53
54
54
// *Must* match Environment::ImmediateInfo::Fields in src/env.h.
55
55
const kCount = 0 ;
56
- const kHasOutstanding = 1 ;
56
+ const kRefCount = 1 ;
57
+ const kHasOutstanding = 2 ;
57
58
58
- const [ activateImmediateCheck , immediateInfo ] =
59
+ const [ immediateInfo , toggleImmediateRef ] =
59
60
setImmediateCallback ( processImmediate ) ;
60
61
62
+ const kRefed = Symbol ( 'refed' ) ;
63
+
61
64
// The Timeout class
62
65
const Timeout = timerInternals . Timeout ;
63
66
@@ -656,42 +659,41 @@ function processImmediate() {
656
659
const queue = outstandingQueue . head !== null ?
657
660
outstandingQueue : immediateQueue ;
658
661
var immediate = queue . head ;
659
- var tail = queue . tail ;
662
+ const tail = queue . tail ;
660
663
661
664
// Clear the linked list early in case new `setImmediate()` calls occur while
662
665
// immediate callbacks are executed
663
666
queue . head = queue . tail = null ;
664
667
665
- while ( immediate !== null ) {
666
- if ( ! immediate . _onImmediate ) {
667
- immediate = immediate . _idleNext ;
668
- continue ;
669
- }
668
+ let count = 0 ;
669
+ let refCount = 0 ;
670
670
671
- // Save next in case `clearImmediate (immediate)` is called from callback
672
- const next = immediate . _idleNext ;
671
+ while ( immediate !== null ) {
672
+ immediate . _destroyed = true ;
673
673
674
674
const asyncId = immediate [ async_id_symbol ] ;
675
675
emitBefore ( asyncId , immediate [ trigger_async_id_symbol ] ) ;
676
676
677
- tryOnImmediate ( immediate , next , tail ) ;
677
+ count ++ ;
678
+ if ( immediate [ kRefed ] )
679
+ refCount ++ ;
680
+ immediate [ kRefed ] = undefined ;
681
+
682
+ tryOnImmediate ( immediate , tail , count , refCount ) ;
678
683
679
684
emitAfter ( asyncId ) ;
680
685
681
- // If `clearImmediate(immediate)` wasn't called from the callback, use the
682
- // `immediate`'s next item
683
- if ( immediate . _idleNext !== null )
684
- immediate = immediate . _idleNext ;
685
- else
686
- immediate = next ;
686
+ immediate = immediate . _idleNext ;
687
687
}
688
688
689
+ immediateInfo [ kCount ] -= count ;
690
+ immediateInfo [ kRefCount ] -= refCount ;
689
691
immediateInfo [ kHasOutstanding ] = 0 ;
690
692
}
691
693
692
694
// An optimization so that the try/finally only de-optimizes (since at least v8
693
695
// 4.7) what is in this smaller function.
694
- function tryOnImmediate ( immediate , next , oldTail ) {
696
+ function tryOnImmediate ( immediate , oldTail , count , refCount ) {
695
697
var threw = true ;
696
698
try {
697
699
// make the actual call outside the try/finally to allow it to be optimized
@@ -700,21 +702,21 @@ function tryOnImmediate(immediate, next, oldTail) {
700
702
} finally {
701
703
immediate . _onImmediate = null ;
702
704
703
- if ( ! immediate . _destroyed ) {
704
- immediate . _destroyed = true ;
705
- immediateInfo [ kCount ] -- ;
706
-
707
- if ( async_hook_fields [ kDestroy ] > 0 ) {
708
- emitDestroy ( immediate [ async_id_symbol ] ) ;
709
- }
705
+ if ( async_hook_fields [ kDestroy ] > 0 ) {
706
+ emitDestroy ( immediate [ async_id_symbol ] ) ;
710
707
}
711
708
712
- if ( threw && ( immediate . _idleNext !== null || next !== null ) ) {
713
- // Handle any remaining Immediates after error handling has resolved,
714
- // assuming we're still alive to do so.
715
- outstandingQueue . head = immediate . _idleNext || next ;
716
- outstandingQueue . tail = oldTail ;
717
- immediateInfo [ kHasOutstanding ] = 1 ;
709
+ if ( threw ) {
710
+ immediateInfo [ kCount ] -= count ;
711
+ immediateInfo [ kRefCount ] -= refCount ;
712
+
713
+ if ( immediate . _idleNext !== null ) {
714
+ // Handle any remaining Immediates after error handling has resolved,
715
+ // assuming we're still alive to do so.
716
+ outstandingQueue . head = immediate . _idleNext ;
717
+ outstandingQueue . tail = oldTail ;
718
+ immediateInfo [ kHasOutstanding ] = 1 ;
719
+ }
718
720
}
719
721
}
720
722
}
@@ -729,31 +731,51 @@ function runCallback(timer) {
729
731
}
730
732
731
733
732
- function Immediate ( callback , args ) {
733
- this . _idleNext = null ;
734
- this . _idlePrev = null ;
735
- // this must be set to null first to avoid function tracking
736
- // on the hidden class, revisit in V8 versions after 6.2
737
- this . _onImmediate = null ;
738
- this . _onImmediate = callback ;
739
- this . _argv = args ;
740
- this . _destroyed = false ;
734
+ const Immediate = class Immediate {
735
+ constructor ( callback , args ) {
736
+ this . _idleNext = null ;
737
+ this . _idlePrev = null ;
738
+ // this must be set to null first to avoid function tracking
739
+ // on the hidden class, revisit in V8 versions after 6.2
740
+ this . _onImmediate = null ;
741
+ this . _onImmediate = callback ;
742
+ this . _argv = args ;
743
+ this . _destroyed = false ;
744
+ this [ kRefed ] = false ;
745
+
746
+ this [ async_id_symbol ] = ++ async_id_fields [ kAsyncIdCounter ] ;
747
+ this [ trigger_async_id_symbol ] = getDefaultTriggerAsyncId ( ) ;
748
+ if ( async_hook_fields [ kInit ] > 0 ) {
749
+ emitInit ( this [ async_id_symbol ] ,
750
+ 'Immediate' ,
751
+ this [ trigger_async_id_symbol ] ,
752
+ this ) ;
753
+ }
754
+
755
+ this . ref ( ) ;
756
+ immediateInfo [ kCount ] ++ ;
741
757
742
- this [ async_id_symbol ] = ++ async_id_fields [ kAsyncIdCounter ] ;
743
- this [ trigger_async_id_symbol ] = getDefaultTriggerAsyncId ( ) ;
744
- if ( async_hook_fields [ kInit ] > 0 ) {
745
- emitInit ( this [ async_id_symbol ] ,
746
- 'Immediate' ,
747
- this [ trigger_async_id_symbol ] ,
748
- this ) ;
758
+ immediateQueue . append ( this ) ;
749
759
}
750
760
751
- if ( immediateInfo [ kCount ] === 0 )
752
- activateImmediateCheck ( ) ;
753
- immediateInfo [ kCount ] ++ ;
761
+ ref ( ) {
762
+ if ( this [ kRefed ] === false ) {
763
+ this [ kRefed ] = true ;
764
+ if ( immediateInfo [ kRefCount ] ++ === 0 )
765
+ toggleImmediateRef ( true ) ;
766
+ }
767
+ return this ;
768
+ }
754
769
755
- immediateQueue . append ( this ) ;
756
- }
770
+ unref ( ) {
771
+ if ( this [ kRefed ] === true ) {
772
+ this [ kRefed ] = false ;
773
+ if ( -- immediateInfo [ kRefCount ] === 0 )
774
+ toggleImmediateRef ( false ) ;
775
+ }
776
+ return this ;
777
+ }
778
+ } ;
757
779
758
780
function setImmediate ( callback , arg1 , arg2 , arg3 ) {
759
781
if ( typeof callback !== 'function' ) {
@@ -793,15 +815,18 @@ exports.setImmediate = setImmediate;
793
815
794
816
795
817
exports . clearImmediate = function ( immediate ) {
796
- if ( ! immediate ) return ;
818
+ if ( ! immediate || immediate . _destroyed )
819
+ return ;
797
820
798
- if ( ! immediate . _destroyed ) {
799
- immediateInfo [ kCount ] -- ;
800
- immediate . _destroyed = true ;
821
+ immediateInfo [ kCount ] -- ;
822
+ immediate . _destroyed = true ;
801
823
802
- if ( async_hook_fields [ kDestroy ] > 0 ) {
803
- emitDestroy ( immediate [ async_id_symbol ] ) ;
804
- }
824
+ if ( immediate [ kRefed ] && -- immediateInfo [ kRefCount ] === 0 )
825
+ toggleImmediateRef ( false ) ;
826
+ immediate [ kRefed ] = undefined ;
827
+
828
+ if ( async_hook_fields [ kDestroy ] > 0 ) {
829
+ emitDestroy ( immediate [ async_id_symbol ] ) ;
805
830
}
806
831
807
832
immediate . _onImmediate = null ;
0 commit comments