Skip to content

Commit bbde16d

Browse files
BrinenasccoVeillehezhipeng.09dlclark
authored
fix Incorrect timeout error when using FindStringMatch() (#86)
* fix Timeout Error issue: when the fastclock stop running, then next matches will get a wrong fast.current and daeline, thus trigger an incorrect timeout error. Co-authored-by: ccoVeille <[email protected]> Co-authored-by: hezhipeng.09 <[email protected]> Co-authored-by: Doug Clark <[email protected]>
1 parent 5f3687a commit bbde16d

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

fastclock.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,20 @@ func makeDeadline(d time.Duration) fasttime {
5050

5151
// Start or extend clock if necessary.
5252
if end > fast.clockEnd.read() {
53+
// If time.Since(last use) > timeout, there's a chance that
54+
// fast.current will no longer be updated, which can lead to
55+
// incorrect 'end' calculations that can trigger a false timeout
56+
fast.mu.Lock()
57+
if !fast.running && !fast.start.IsZero() {
58+
// update fast.current
59+
fast.current.write(durationToTicks(time.Since(fast.start)))
60+
// recalculate our end value
61+
end = fast.current.read() + durationToTicks(d+clockPeriod)
62+
}
63+
fast.mu.Unlock()
5364
extendClock(end)
5465
}
66+
5567
return end
5668
}
5769

fastclock_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package regexp2
22

33
import (
44
"fmt"
5+
"log"
56
"testing"
67
"time"
78
)
@@ -56,3 +57,58 @@ func TestStopTimeoutClock(t *testing.T) {
5657
t.Errorf("Expected isRunning to be %v, got %v", want, got)
5758
}
5859
}
60+
func TestIncorrectDeadline(t *testing.T) {
61+
if fast.start.IsZero() {
62+
fast.start = time.Now()
63+
}
64+
// make fast stopped
65+
for fast.running {
66+
time.Sleep(clockPeriod)
67+
}
68+
t.Logf("current fast: %+v", fast)
69+
timeout := 5 * clockPeriod
70+
// make the error time much bigger
71+
time.Sleep(10 * clockPeriod)
72+
nowTick := durationToTicks(time.Since(fast.start))
73+
// before fix, fast.current will be the time fast stopped, and end is incorrect too
74+
// after fix, fast.current will be current time.
75+
d := makeDeadline(timeout)
76+
gotTick := fast.current.read()
77+
t.Logf("nowTick: %+v, gotTick: %+v", nowTick, gotTick)
78+
if nowTick > gotTick {
79+
t.Errorf("Expectd current should greater than %v, got %v", gotTick, nowTick)
80+
}
81+
expectedDeadTick := nowTick + durationToTicks(timeout)
82+
if d < expectedDeadTick {
83+
t.Errorf("Expectd deadTick %v, got %v", expectedDeadTick, d)
84+
}
85+
}
86+
87+
func TestIncorrectTimeoutError(t *testing.T) {
88+
log.SetFlags(log.Lmicroseconds)
89+
re := MustCompile(`\[(\d+)\]\s+\[([\s\S]+)\]\s+([\s\S]+).*`, RE2)
90+
// there's a lot of slop in the timeout process (on purpose to keep resources low)
91+
// need a timeout of at least 5x the clockPeriod to have consistent test behavior
92+
re.MatchTimeout = 5 * clockPeriod
93+
94+
_, err := re.FindStringMatch("[10000] [Dec 15, 2012 1:42:43 AM] com.dev.log.LoggingExample main")
95+
if err != nil {
96+
t.Fatalf("expected no error, got %v", err)
97+
}
98+
99+
// now wait - this makes sure the background timer goroutine is stopped
100+
time.Sleep(time.Second + re.MatchTimeout*2)
101+
102+
if val := fast.clockEnd.read() - fast.current.read(); val > 0 {
103+
t.Fatalf("unexpected bg timer running: %v", val)
104+
}
105+
106+
// each of these should be plenty fast to not timeout
107+
for i := 0; i < 1000; i++ {
108+
_, err := re.FindStringMatch("[10000] [Dec 15, 2012 1:42:43 AM] com.dev.log.LoggingExample main")
109+
if err != nil {
110+
log.Printf("timeout")
111+
t.Fatalf("Expecting no error, got: '%v' on iteration %v", err, i)
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)