gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/timer_test.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package tcpip_test 16 17 import ( 18 "math" 19 "sync" 20 "testing" 21 "time" 22 23 "gvisor.dev/gvisor/pkg/tcpip" 24 ) 25 26 func TestMonotonicTimeBefore(t *testing.T) { 27 var mt tcpip.MonotonicTime 28 if mt.Before(mt) { 29 t.Errorf("%#v.Before(%#v)", mt, mt) 30 } 31 32 one := mt.Add(1) 33 if one.Before(mt) { 34 t.Errorf("%#v.Before(%#v)", one, mt) 35 } 36 if !mt.Before(one) { 37 t.Errorf("!%#v.Before(%#v)", mt, one) 38 } 39 } 40 41 func TestMonotonicTimeAfter(t *testing.T) { 42 var mt tcpip.MonotonicTime 43 if mt.After(mt) { 44 t.Errorf("%#v.After(%#v)", mt, mt) 45 } 46 47 one := mt.Add(1) 48 if mt.After(one) { 49 t.Errorf("%#v.After(%#v)", mt, one) 50 } 51 if !one.After(mt) { 52 t.Errorf("!%#v.After(%#v)", one, mt) 53 } 54 } 55 56 func TestMonotonicTimeAddSub(t *testing.T) { 57 var mt tcpip.MonotonicTime 58 if one, two := mt.Add(2), mt.Add(1).Add(1); one != two { 59 t.Errorf("mt.Add(2) != mt.Add(1).Add(1) (%#v != %#v)", one, two) 60 } 61 62 min := mt.Add(math.MinInt64) 63 max := mt.Add(math.MaxInt64) 64 65 if overflow := mt.Add(1).Add(math.MaxInt64); overflow != max { 66 t.Errorf("mt.Add(math.MaxInt64) != mt.Add(1).Add(math.MaxInt64) (%#v != %#v)", max, overflow) 67 } 68 if underflow := mt.Add(-1).Add(math.MinInt64); underflow != min { 69 t.Errorf("mt.Add(math.MinInt64) != mt.Add(-1).Add(math.MinInt64) (%#v != %#v)", min, underflow) 70 } 71 72 if got, want := min.Sub(min), time.Duration(0); want != got { 73 t.Errorf("got min.Sub(min) = %d, want %d", got, want) 74 } 75 if got, want := max.Sub(max), time.Duration(0); want != got { 76 t.Errorf("got max.Sub(max) = %d, want %d", got, want) 77 } 78 79 if overflow, want := max.Sub(min), time.Duration(math.MaxInt64); overflow != want { 80 t.Errorf("mt.Add(math.MaxInt64).Sub(mt.Add(math.MinInt64) != %s (%#v)", want, overflow) 81 } 82 if underflow, want := min.Sub(max), time.Duration(math.MinInt64); underflow != want { 83 t.Errorf("mt.Add(math.MinInt64).Sub(mt.Add(math.MaxInt64) != %s (%#v)", want, underflow) 84 } 85 } 86 87 func TestMonotonicTimeSub(t *testing.T) { 88 var mt tcpip.MonotonicTime 89 90 if one, two := mt.Add(2), mt.Add(1).Add(1); one != two { 91 t.Errorf("mt.Add(2) != mt.Add(1).Add(1) (%#v != %#v)", one, two) 92 } 93 94 if max, overflow := mt.Add(math.MaxInt64), mt.Add(1).Add(math.MaxInt64); max != overflow { 95 t.Errorf("mt.Add(math.MaxInt64) != mt.Add(1).Add(math.MaxInt64) (%#v != %#v)", max, overflow) 96 } 97 if max, underflow := mt.Add(math.MinInt64), mt.Add(-1).Add(math.MinInt64); max != underflow { 98 t.Errorf("mt.Add(math.MinInt64) != mt.Add(-1).Add(math.MinInt64) (%#v != %#v)", max, underflow) 99 } 100 } 101 102 const ( 103 shortDuration = 1 * time.Nanosecond 104 middleDuration = 100 * time.Millisecond 105 ) 106 107 func TestJobReschedule(t *testing.T) { 108 clock := tcpip.NewStdClock() 109 var wg sync.WaitGroup 110 var lock sync.Mutex 111 112 for i := 0; i < 2; i++ { 113 wg.Add(1) 114 115 go func() { 116 lock.Lock() 117 // Assigning a new timer value updates the timer's locker and function. 118 // This test makes sure there is no data race when reassigning a timer 119 // that has an active timer (even if it has been stopped as a stopped 120 // timer may be blocked on a lock before it can check if it has been 121 // stopped while another goroutine holds the same lock). 122 job := tcpip.NewJob(clock, &lock, func() { 123 wg.Done() 124 }) 125 job.Schedule(shortDuration) 126 lock.Unlock() 127 }() 128 } 129 wg.Wait() 130 } 131 132 func stdClockWithAfter() (tcpip.Clock, func(time.Duration) <-chan time.Time) { 133 return tcpip.NewStdClock(), time.After 134 } 135 136 func TestJobExecution(t *testing.T) { 137 t.Parallel() 138 139 clock, after := stdClockWithAfter() 140 var lock sync.Mutex 141 ch := make(chan struct{}) 142 143 job := tcpip.NewJob(clock, &lock, func() { 144 ch <- struct{}{} 145 }) 146 job.Schedule(shortDuration) 147 148 // Wait for timer to fire. 149 select { 150 case <-ch: 151 case <-after(middleDuration): 152 t.Fatal("timed out waiting for timer to fire") 153 } 154 155 // The timer should have fired only once. 156 select { 157 case <-ch: 158 t.Fatal("no other timers should have fired") 159 case <-after(middleDuration): 160 } 161 } 162 163 func TestCancellableTimerResetFromLongDuration(t *testing.T) { 164 t.Parallel() 165 166 clock, after := stdClockWithAfter() 167 var lock sync.Mutex 168 ch := make(chan struct{}) 169 170 job := tcpip.NewJob(clock, &lock, func() { ch <- struct{}{} }) 171 job.Schedule(middleDuration) 172 173 lock.Lock() 174 job.Cancel() 175 lock.Unlock() 176 177 job.Schedule(shortDuration) 178 179 // Wait for timer to fire. 180 select { 181 case <-ch: 182 case <-after(middleDuration): 183 t.Fatal("timed out waiting for timer to fire") 184 } 185 186 // The timer should have fired only once. 187 select { 188 case <-ch: 189 t.Fatal("no other timers should have fired") 190 case <-after(middleDuration): 191 } 192 } 193 194 func TestJobRescheduleFromShortDuration(t *testing.T) { 195 t.Parallel() 196 197 clock, after := stdClockWithAfter() 198 var lock sync.Mutex 199 ch := make(chan struct{}) 200 201 lock.Lock() 202 job := tcpip.NewJob(clock, &lock, func() { ch <- struct{}{} }) 203 job.Schedule(shortDuration) 204 job.Cancel() 205 lock.Unlock() 206 207 // Wait for timer to fire if it wasn't correctly stopped. 208 select { 209 case <-ch: 210 t.Fatal("timer fired after being stopped") 211 case <-after(middleDuration): 212 } 213 214 job.Schedule(shortDuration) 215 216 // Wait for timer to fire. 217 select { 218 case <-ch: 219 case <-after(middleDuration): 220 t.Fatal("timed out waiting for timer to fire") 221 } 222 223 // The timer should have fired only once. 224 select { 225 case <-ch: 226 t.Fatal("no other timers should have fired") 227 case <-after(middleDuration): 228 } 229 } 230 231 func TestJobImmediatelyCancel(t *testing.T) { 232 t.Parallel() 233 234 clock, after := stdClockWithAfter() 235 var lock sync.Mutex 236 ch := make(chan struct{}) 237 238 for i := 0; i < 1000; i++ { 239 lock.Lock() 240 job := tcpip.NewJob(clock, &lock, func() { ch <- struct{}{} }) 241 job.Schedule(shortDuration) 242 job.Cancel() 243 lock.Unlock() 244 } 245 246 // Wait for timer to fire if it wasn't correctly stopped. 247 select { 248 case <-ch: 249 t.Fatal("timer fired after being stopped") 250 case <-after(middleDuration): 251 } 252 } 253 254 func stdClockWithAfterAndSleep() (tcpip.Clock, func(time.Duration) <-chan time.Time, func(time.Duration)) { 255 clock, after := stdClockWithAfter() 256 return clock, after, time.Sleep 257 } 258 259 func TestJobCancelledRescheduleWithoutLock(t *testing.T) { 260 t.Parallel() 261 262 clock, after, sleep := stdClockWithAfterAndSleep() 263 var lock sync.Mutex 264 ch := make(chan struct{}) 265 266 lock.Lock() 267 job := tcpip.NewJob(clock, &lock, func() { ch <- struct{}{} }) 268 job.Schedule(shortDuration) 269 job.Cancel() 270 lock.Unlock() 271 272 for i := 0; i < 10; i++ { 273 job.Schedule(middleDuration) 274 275 lock.Lock() 276 // Sleep until the timer fires and gets blocked trying to take the lock. 277 sleep(middleDuration * 2) 278 job.Cancel() 279 lock.Unlock() 280 } 281 282 // Wait for double the duration so timers that weren't correctly stopped can 283 // fire. 284 select { 285 case <-ch: 286 t.Fatal("timer fired after being stopped") 287 case <-after(middleDuration * 2): 288 } 289 } 290 291 func TestManyCancellableTimerResetAfterBlockedOnLock(t *testing.T) { 292 t.Parallel() 293 294 clock, after, sleep := stdClockWithAfterAndSleep() 295 var lock sync.Mutex 296 ch := make(chan struct{}) 297 298 lock.Lock() 299 job := tcpip.NewJob(clock, &lock, func() { ch <- struct{}{} }) 300 job.Schedule(shortDuration) 301 for i := 0; i < 10; i++ { 302 // Sleep until the timer fires and gets blocked trying to take the lock. 303 sleep(middleDuration) 304 job.Cancel() 305 job.Schedule(shortDuration) 306 } 307 lock.Unlock() 308 309 // Wait for double the duration for the last timer to fire. 310 select { 311 case <-ch: 312 case <-after(middleDuration): 313 t.Fatal("timed out waiting for timer to fire") 314 } 315 316 // The timer should have fired only once. 317 select { 318 case <-ch: 319 t.Fatal("no other timers should have fired") 320 case <-after(middleDuration): 321 } 322 } 323 324 func TestManyJobReschedulesUnderLock(t *testing.T) { 325 t.Parallel() 326 327 clock, after := stdClockWithAfter() 328 var lock sync.Mutex 329 ch := make(chan struct{}) 330 331 lock.Lock() 332 job := tcpip.NewJob(clock, &lock, func() { ch <- struct{}{} }) 333 job.Schedule(shortDuration) 334 for i := 0; i < 10; i++ { 335 job.Cancel() 336 job.Schedule(shortDuration) 337 } 338 lock.Unlock() 339 340 // Wait for double the duration for the last timer to fire. 341 select { 342 case <-ch: 343 case <-after(middleDuration): 344 t.Fatal("timed out waiting for timer to fire") 345 } 346 347 // The timer should have fired only once. 348 select { 349 case <-ch: 350 t.Fatal("no other timers should have fired") 351 case <-after(middleDuration): 352 } 353 }