Source file
src/time/tick_test.go
1
2
3
4
5 package time_test
6
7 import (
8 "fmt"
9 "runtime"
10 "sync"
11 "testing"
12 . "time"
13 )
14
15 func TestTicker(t *testing.T) {
16 t.Parallel()
17
18
19
20
21
22
23
24 baseCount := 10
25 baseDelta := 20 * Millisecond
26
27
28 if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
29
30
31
32
33
34 baseCount = 6
35 baseDelta = 100 * Millisecond
36 }
37
38 var errs []string
39 logErrs := func() {
40 for _, e := range errs {
41 t.Log(e)
42 }
43 }
44
45 for _, test := range []struct {
46 count int
47 delta Duration
48 }{{
49 count: baseCount,
50 delta: baseDelta,
51 }, {
52 count: 8,
53 delta: 1 * Second,
54 }} {
55 count, delta := test.count, test.delta
56 ticker := NewTicker(delta)
57 t0 := Now()
58 for range count / 2 {
59 <-ticker.C
60 }
61 ticker.Reset(delta * 2)
62 for range count - count/2 {
63 <-ticker.C
64 }
65 ticker.Stop()
66 t1 := Now()
67 dt := t1.Sub(t0)
68 target := 3 * delta * Duration(count/2)
69 slop := target * 3 / 10
70 if dt < target-slop || dt > target+slop {
71 errs = append(errs, fmt.Sprintf("%d %s ticks then %d %s ticks took %s, expected [%s,%s]", count/2, delta, count/2, delta*2, dt, target-slop, target+slop))
72 if dt > target+slop {
73
74
75 Sleep(Second / 2)
76 }
77 continue
78 }
79
80 Sleep(2 * delta)
81 select {
82 case <-ticker.C:
83 errs = append(errs, "Ticker did not shut down")
84 continue
85 default:
86
87 }
88
89
90 if len(errs) > 0 {
91 t.Logf("saw %d errors, ignoring to avoid flakiness", len(errs))
92 logErrs()
93 }
94
95 return
96 }
97
98 t.Errorf("saw %d errors", len(errs))
99 logErrs()
100 }
101
102
103 func TestTickerStopWithDirectInitialization(t *testing.T) {
104 c := make(chan Time)
105 tk := &Ticker{C: c}
106 tk.Stop()
107 }
108
109
110 func TestTeardown(t *testing.T) {
111 t.Parallel()
112
113 Delta := 100 * Millisecond
114 if testing.Short() {
115 Delta = 20 * Millisecond
116 }
117 for range 3 {
118 ticker := NewTicker(Delta)
119 <-ticker.C
120 ticker.Stop()
121 }
122 }
123
124
125 func TestTick(t *testing.T) {
126
127 if got := Tick(-1); got != nil {
128 t.Errorf("Tick(-1) = %v; want nil", got)
129 }
130 }
131
132
133 func TestNewTickerLtZeroDuration(t *testing.T) {
134 defer func() {
135 if err := recover(); err == nil {
136 t.Errorf("NewTicker(-1) should have panicked")
137 }
138 }()
139 NewTicker(-1)
140 }
141
142
143 func TestTickerResetLtZeroDuration(t *testing.T) {
144 defer func() {
145 if err := recover(); err == nil {
146 t.Errorf("Ticker.Reset(0) should have panicked")
147 }
148 }()
149 tk := NewTicker(Second)
150 tk.Reset(0)
151 }
152
153 func TestLongAdjustTimers(t *testing.T) {
154 if runtime.GOOS == "android" || runtime.GOOS == "ios" {
155 t.Skipf("skipping on %s - too slow", runtime.GOOS)
156 }
157 t.Parallel()
158 var wg sync.WaitGroup
159 defer wg.Wait()
160
161
162 const count = 5000
163 wg.Add(count)
164 for range count {
165 go func() {
166 defer wg.Done()
167 Sleep(10 * Microsecond)
168 }()
169 }
170 for range count {
171 Sleep(1 * Microsecond)
172 }
173
174
175
176
177
178
179 done := make(chan bool)
180 AfterFunc(60*Second, func() { close(done) })
181
182
183 inQ := make(chan func())
184 outQ := make(chan func())
185
186 defer close(inQ)
187
188 wg.Add(1)
189 go func() {
190 defer wg.Done()
191 defer close(outQ)
192 var q []func()
193 for {
194 var sendTo chan func()
195 var send func()
196 if len(q) > 0 {
197 sendTo = outQ
198 send = q[0]
199 }
200 select {
201 case sendTo <- send:
202 q = q[1:]
203 case f, ok := <-inQ:
204 if !ok {
205 return
206 }
207 q = append(q, f)
208 case <-done:
209 return
210 }
211 }
212 }()
213
214 for i := range 50000 {
215 const try = 20
216 for range try {
217 inQ <- func() {}
218 }
219 for range try {
220 select {
221 case _, ok := <-outQ:
222 if !ok {
223 t.Fatal("output channel is closed")
224 }
225 case <-After(5 * Second):
226 t.Fatalf("failed to read work, iteration %d", i)
227 case <-done:
228 t.Fatal("timer expired")
229 }
230 }
231 }
232 }
233 func BenchmarkTicker(b *testing.B) {
234 benchmark(b, func(pb *testing.PB) {
235 ticker := NewTicker(Nanosecond)
236 for pb.Next() {
237 <-ticker.C
238 }
239 ticker.Stop()
240 })
241 }
242
243 func BenchmarkTickerReset(b *testing.B) {
244 benchmark(b, func(pb *testing.PB) {
245 ticker := NewTicker(Nanosecond)
246 for pb.Next() {
247 ticker.Reset(Nanosecond * 2)
248 }
249 ticker.Stop()
250 })
251 }
252
253 func BenchmarkTickerResetNaive(b *testing.B) {
254 benchmark(b, func(pb *testing.PB) {
255 ticker := NewTicker(Nanosecond)
256 for pb.Next() {
257 ticker.Stop()
258 ticker = NewTicker(Nanosecond * 2)
259 }
260 ticker.Stop()
261 })
262 }
263
264 func TestTimerGC(t *testing.T) {
265 run := func(t *testing.T, what string, f func()) {
266 t.Helper()
267 t.Run(what, func(t *testing.T) {
268 t.Helper()
269 const N = 1e4
270 var stats runtime.MemStats
271 runtime.GC()
272 runtime.GC()
273 runtime.GC()
274 runtime.ReadMemStats(&stats)
275 before := int64(stats.Mallocs - stats.Frees)
276
277 for j := 0; j < N; j++ {
278 f()
279 }
280
281 runtime.GC()
282 runtime.GC()
283 runtime.GC()
284 runtime.ReadMemStats(&stats)
285 after := int64(stats.Mallocs - stats.Frees)
286
287
288 inuse := after - before
289 if inuse >= N {
290 t.Errorf("%s did not get GC'ed: %d allocations", what, inuse)
291
292 Sleep(1 * Second)
293 runtime.ReadMemStats(&stats)
294 after := int64(stats.Mallocs - stats.Frees)
295 inuse = after - before
296 t.Errorf("after a sleep: %d allocations", inuse)
297 }
298 })
299 }
300
301 run(t, "After", func() { After(Hour) })
302 run(t, "Tick", func() { Tick(Hour) })
303 run(t, "NewTimer", func() { NewTimer(Hour) })
304 run(t, "NewTicker", func() { NewTicker(Hour) })
305 run(t, "NewTimerStop", func() { NewTimer(Hour).Stop() })
306 run(t, "NewTickerStop", func() { NewTicker(Hour).Stop() })
307 }
308
309 func TestChan(t *testing.T) {
310 for _, name := range []string{"0", "1", "2"} {
311 t.Run("asynctimerchan="+name, func(t *testing.T) {
312 t.Setenv("GODEBUG", "asynctimerchan="+name)
313 t.Run("Timer", func(t *testing.T) {
314 tim := NewTimer(10000 * Second)
315 testTimerChan(t, tim, tim.C, name == "0")
316 })
317 t.Run("Ticker", func(t *testing.T) {
318 tim := &tickerTimer{Ticker: NewTicker(10000 * Second)}
319 testTimerChan(t, tim, tim.C, name == "0")
320 })
321 })
322 }
323 }
324
325 type timer interface {
326 Stop() bool
327 Reset(Duration) bool
328 }
329
330
331
332 type tickerTimer struct {
333 *Ticker
334 stopped bool
335 }
336
337 func (t *tickerTimer) Stop() bool {
338 pending := !t.stopped
339 t.stopped = true
340 t.Ticker.Stop()
341 return pending
342 }
343
344 func (t *tickerTimer) Reset(d Duration) bool {
345 pending := !t.stopped
346 t.stopped = false
347 t.Ticker.Reset(d)
348 return pending
349 }
350
351 func testTimerChan(t *testing.T, tim timer, C <-chan Time, synctimerchan bool) {
352 _, isTimer := tim.(*Timer)
353 isTicker := !isTimer
354
355
356
357
358 const (
359 sched = 10 * Millisecond
360 tries = 100
361 drainTries = 5
362 )
363
364 drain := func() {
365 for range drainTries {
366 select {
367 case <-C:
368 return
369 default:
370 }
371 Sleep(sched)
372 }
373 }
374 noTick := func() {
375 t.Helper()
376 select {
377 default:
378 case <-C:
379 t.Errorf("extra tick")
380 }
381 }
382 assertTick := func() {
383 t.Helper()
384 select {
385 default:
386 case <-C:
387 return
388 }
389 for range tries {
390 Sleep(sched)
391 select {
392 default:
393 case <-C:
394 return
395 }
396 }
397 t.Errorf("missing tick")
398 }
399 assertLen := func() {
400 t.Helper()
401 if synctimerchan {
402 if n := len(C); n != 0 {
403 t.Errorf("synctimer has len(C) = %d, want 0 (always)", n)
404 }
405 return
406 }
407 var n int
408 if n = len(C); n == 1 {
409 return
410 }
411 for range tries {
412 Sleep(sched)
413 if n = len(C); n == 1 {
414 return
415 }
416 }
417 t.Errorf("len(C) = %d, want 1", n)
418 }
419
420
421 tim.Stop()
422 noTick()
423
424
425 tim.Reset(10000 * Second)
426 noTick()
427
428 if synctimerchan {
429
430 tim.Reset(1)
431 Sleep(sched)
432 if l, c := len(C), cap(C); l != 0 || c != 0 {
433
434 }
435 assertTick()
436 } else {
437
438 tim.Reset(1)
439 assertTick()
440 Sleep(sched)
441 tim.Reset(10000 * Second)
442 if isTicker {
443 assertTick()
444 }
445 noTick()
446
447
448
449 tim.Reset(1)
450 assertLen()
451 assertTick()
452
453
454
455 tim.Stop()
456 if !synctimerchan {
457 drain()
458 }
459 tim.Reset(1)
460 assertLen()
461 assertTick()
462 }
463
464
465
466 Sleep(sched)
467 tim.Reset(10000 * Second)
468 if !synctimerchan && isTicker {
469 assertLen()
470 assertTick()
471 }
472 noTick()
473
474 notDone := func(done chan bool) {
475 t.Helper()
476 select {
477 default:
478 case <-done:
479 t.Fatalf("early done")
480 }
481 }
482
483 waitDone := func(done chan bool) {
484 t.Helper()
485 for range tries {
486 Sleep(sched)
487 select {
488 case <-done:
489 return
490 default:
491 }
492 }
493 t.Fatalf("never got done")
494 }
495
496
497 tim.Reset(10000 * Second)
498 if !synctimerchan {
499 drain()
500 }
501
502
503 done := make(chan bool)
504 notDone(done)
505 go func() {
506 <-C
507 close(done)
508 }()
509 Sleep(sched)
510 notDone(done)
511
512
513 tim.Reset(20000 * Second)
514 Sleep(sched)
515 notDone(done)
516
517
518 tim.Reset(1)
519 waitDone(done)
520
521
522
523 if isTicker {
524 assertTick()
525 } else {
526 noTick()
527 }
528
529 tim.Stop()
530 if isTicker || !synctimerchan {
531 t.Logf("drain")
532 drain()
533 }
534 noTick()
535
536
537 tim.Reset(10000 * Second)
538 if !synctimerchan {
539 drain()
540 }
541 done = make(chan bool, 2)
542 done1 := make(chan bool)
543 done2 := make(chan bool)
544 stop := make(chan bool)
545 go func() {
546 select {
547 case <-C:
548 done <- true
549 case <-stop:
550 }
551 close(done1)
552 }()
553 go func() {
554 select {
555 case <-C:
556 done <- true
557 case <-stop:
558 }
559 close(done2)
560 }()
561 Sleep(sched)
562 notDone(done)
563 tim.Reset(sched / 2)
564 Sleep(sched)
565 waitDone(done)
566 tim.Stop()
567 close(stop)
568 waitDone(done1)
569 waitDone(done2)
570 if isTicker {
571
572
573 select {
574 default:
575 case <-done:
576 }
577
578 select {
579 default:
580 case <-C:
581 }
582 }
583 notDone(done)
584
585
586 stop = make(chan bool)
587 done = make(chan bool, 2)
588 for range 2 {
589 go func() {
590 select {
591 case <-C:
592 panic("unexpected data")
593 case <-stop:
594 }
595 done <- true
596 }()
597 }
598 Sleep(sched)
599 close(stop)
600 waitDone(done)
601 waitDone(done)
602
603
604
605 if synctimerchan {
606 tim.Reset(1)
607 Sleep(10 * Millisecond)
608 if pending := tim.Stop(); pending != true {
609 t.Errorf("tim.Stop() = %v, want true", pending)
610 }
611 noTick()
612
613 tim.Reset(Hour)
614 noTick()
615 if pending := tim.Reset(1); pending != true {
616 t.Errorf("tim.Stop() = %v, want true", pending)
617 }
618 assertTick()
619 Sleep(10 * Millisecond)
620 if isTicker {
621 assertTick()
622 Sleep(10 * Millisecond)
623 } else {
624 noTick()
625 }
626 if pending, want := tim.Reset(Hour), isTicker; pending != want {
627 t.Errorf("tim.Stop() = %v, want %v", pending, want)
628 }
629 noTick()
630 }
631 }
632
633 func TestManualTicker(t *testing.T) {
634
635
636
637 c := make(chan Time)
638 tick := &Ticker{C: c}
639 tick.Stop()
640 }
641
642 func TestAfterTimes(t *testing.T) {
643 t.Parallel()
644
645
646
647
648
649 for range 10 {
650 start := Now()
651 c := After(10 * Millisecond)
652 Sleep(500 * Millisecond)
653 dt := (<-c).Sub(start)
654 if dt < 400*Millisecond {
655 return
656 }
657 t.Logf("After(10ms) time is +%v, want <400ms", dt)
658 }
659 t.Errorf("not working")
660 }
661
662 func TestTickTimes(t *testing.T) {
663 t.Parallel()
664
665 for range 10 {
666 start := Now()
667 c := Tick(10 * Millisecond)
668 Sleep(500 * Millisecond)
669 dt := (<-c).Sub(start)
670 if dt < 400*Millisecond {
671 return
672 }
673 t.Logf("Tick(10ms) time is +%v, want <400ms", dt)
674 }
675 t.Errorf("not working")
676 }
677
View as plain text