1
1
'use strict' ;
2
2
3
3
/*
4
- * This is a regression test for https://github.com/joyent/node/issues/15447
5
- * and https://github.com/joyent/node/issues/9333.
4
+ * This is a regression test for
5
+ * https://github.com/nodejs/node-v0.x-archive/issues/15447 and
6
+ * and https://github.com/nodejs/node-v0.x-archive/issues/9333.
6
7
*
7
8
* When a timer is added in another timer's callback, its underlying timer
8
9
* handle was started with a timeout that was actually incorrect.
@@ -28,17 +29,23 @@ const Timer = process.binding('timer_wrap').Timer;
28
29
29
30
const TIMEOUT = 100 ;
30
31
31
- let nbBlockingCallbackCalls = 0 ;
32
- let latestDelay = 0 ;
33
- let timeCallbackScheduled = 0 ;
32
+ let nbBlockingCallbackCalls ;
33
+ let latestDelay ;
34
+ let timeCallbackScheduled ;
35
+
36
+ // These tests are timing dependent so they may fail even when the bug is
37
+ // not present (if the host is sufficiently busy that the timers are delayed
38
+ // significantly). However, they fail 100% of the time when the bug *is*
39
+ // present, so to increase reliability, allow for a small number of retries.
40
+ let retries = 2 ;
34
41
35
42
function initTest ( ) {
36
43
nbBlockingCallbackCalls = 0 ;
37
44
latestDelay = 0 ;
38
45
timeCallbackScheduled = 0 ;
39
46
}
40
47
41
- function blockingCallback ( callback ) {
48
+ function blockingCallback ( retry , callback ) {
42
49
++ nbBlockingCallbackCalls ;
43
50
44
51
if ( nbBlockingCallbackCalls > 1 ) {
@@ -47,34 +54,61 @@ function blockingCallback(callback) {
47
54
// to fire, they shouldn't generally be more than 100% late in this case.
48
55
// But they are guaranteed to be at least 100ms late given the bug in
49
56
// https://github.com/nodejs/node-v0.x-archive/issues/15447 and
50
- // https://github.com/nodejs/node-v0.x-archive/issues/9333..
51
- assert ( latestDelay < TIMEOUT * 2 ) ;
57
+ // https://github.com/nodejs/node-v0.x-archive/issues/9333.
58
+ if ( latestDelay >= TIMEOUT * 2 ) {
59
+ if ( retries > 0 ) {
60
+ retries -- ;
61
+ return retry ( callback ) ;
62
+ }
63
+ assert . fail ( null , null ,
64
+ `timeout delayed by more than 100% (${ latestDelay } ms)` ) ;
65
+ }
52
66
if ( callback )
53
67
return callback ( ) ;
54
68
} else {
55
69
// block by busy-looping to trigger the issue
56
70
common . busyLoop ( TIMEOUT ) ;
57
71
58
72
timeCallbackScheduled = Timer . now ( ) ;
59
- setTimeout ( blockingCallback . bind ( null , callback ) , TIMEOUT ) ;
73
+ setTimeout ( blockingCallback . bind ( null , retry , callback ) , TIMEOUT ) ;
60
74
}
61
75
}
62
76
63
- const testAddingTimerToEmptyTimersList = common . mustCall ( function ( callback ) {
77
+ function testAddingTimerToEmptyTimersList ( callback ) {
64
78
initTest ( ) ;
65
79
// Call setTimeout just once to make sure the timers list is
66
80
// empty when blockingCallback is called.
67
- setTimeout ( blockingCallback . bind ( null , callback ) , TIMEOUT ) ;
68
- } ) ;
81
+ setTimeout (
82
+ blockingCallback . bind ( null , testAddingTimerToEmptyTimersList , callback ) ,
83
+ TIMEOUT
84
+ ) ;
85
+ }
86
+
87
+ function testAddingTimerToNonEmptyTimersList ( ) {
88
+ // If both timers fail and attempt a retry, only actually do anything for one
89
+ // of them.
90
+ let retryOK = true ;
91
+ const retry = ( ) => {
92
+ if ( retryOK )
93
+ testAddingTimerToNonEmptyTimersList ( ) ;
94
+ retryOK = false ;
95
+ } ;
69
96
70
- const testAddingTimerToNonEmptyTimersList = common . mustCall ( function ( ) {
71
97
initTest ( ) ;
72
98
// Call setTimeout twice with the same timeout to make
73
99
// sure the timers list is not empty when blockingCallback is called.
74
- setTimeout ( blockingCallback , TIMEOUT ) ;
75
- setTimeout ( blockingCallback , TIMEOUT ) ;
76
- } ) ;
100
+ setTimeout (
101
+ blockingCallback . bind ( null , retry ) ,
102
+ TIMEOUT
103
+ ) ;
104
+ setTimeout (
105
+ blockingCallback . bind ( null , retry ) ,
106
+ TIMEOUT
107
+ ) ;
108
+ }
77
109
78
110
// Run the test for the empty timers list case, and then for the non-empty
79
- // timers list one
80
- testAddingTimerToEmptyTimersList ( testAddingTimerToNonEmptyTimersList ) ;
111
+ // timers list one.
112
+ testAddingTimerToEmptyTimersList (
113
+ common . mustCall ( testAddingTimerToNonEmptyTimersList )
114
+ ) ;
0 commit comments