Source file
src/time/tick_test.go
Documentation: time
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
365
366
367
368 drain1 := func() {
369 for range drainTries {
370 select {
371 case <-C:
372 return
373 default:
374 }
375 Sleep(sched)
376 }
377 }
378
379
380
381
382 drainAsync := func() {
383 if synctimerchan {
384
385
386 return
387 }
388
389
390 drain1()
391 if isTicker {
392
393
394
395
396
397
398
399
400
401
402
403 drain1()
404 }
405 }
406 noTick := func() {
407 t.Helper()
408 select {
409 default:
410 case <-C:
411 t.Errorf("extra tick")
412 }
413 }
414 assertTick := func() {
415 t.Helper()
416 select {
417 default:
418 case <-C:
419 return
420 }
421 for range tries {
422 Sleep(sched)
423 select {
424 default:
425 case <-C:
426 return
427 }
428 }
429 t.Errorf("missing tick")
430 }
431 assertLen := func() {
432 t.Helper()
433 if synctimerchan {
434 if n := len(C); n != 0 {
435 t.Errorf("synctimer has len(C) = %d, want 0 (always)", n)
436 }
437 return
438 }
439 var n int
440 if n = len(C); n == 1 {
441 return
442 }
443 for range tries {
444 Sleep(sched)
445 if n = len(C); n == 1 {
446 return
447 }
448 }
449 t.Errorf("len(C) = %d, want 1", n)
450 }
451
452
453 tim.Stop()
454 noTick()
455
456
457 tim.Reset(10000 * Second)
458 noTick()
459
460 if synctimerchan {
461
462 tim.Reset(1)
463 Sleep(sched)
464 if l, c := len(C), cap(C); l != 0 || c != 0 {
465
466 }
467 assertTick()
468 } else {
469
470 tim.Reset(1)
471 assertTick()
472 Sleep(sched)
473 tim.Reset(10000 * Second)
474 drainAsync()
475 noTick()
476
477
478
479 tim.Reset(1)
480 assertLen()
481 assertTick()
482
483
484
485 tim.Stop()
486 drainAsync()
487 tim.Reset(1)
488 assertLen()
489 assertTick()
490 }
491
492
493
494 Sleep(sched)
495 tim.Reset(10000 * Second)
496 drainAsync()
497 noTick()
498
499 notDone := func(done chan bool) {
500 t.Helper()
501 select {
502 default:
503 case <-done:
504 t.Fatalf("early done")
505 }
506 }
507
508 waitDone := func(done chan bool) {
509 t.Helper()
510 for range tries {
511 Sleep(sched)
512 select {
513 case <-done:
514 return
515 default:
516 }
517 }
518 t.Fatalf("never got done")
519 }
520
521
522 tim.Reset(10000 * Second)
523 drainAsync()
524
525
526 done := make(chan bool)
527 notDone(done)
528 go func() {
529 <-C
530 close(done)
531 }()
532 Sleep(sched)
533 notDone(done)
534
535
536 tim.Reset(20000 * Second)
537 Sleep(sched)
538 notDone(done)
539
540
541 tim.Reset(1)
542 waitDone(done)
543
544
545
546 if isTicker {
547 assertTick()
548 } else {
549 noTick()
550 }
551
552 tim.Stop()
553 drainAsync()
554 noTick()
555
556
557 tim.Reset(10000 * Second)
558 drainAsync()
559 done = make(chan bool, 2)
560 done1 := make(chan bool)
561 done2 := make(chan bool)
562 stop := make(chan bool)
563 go func() {
564 select {
565 case <-C:
566 done <- true
567 case <-stop:
568 }
569 close(done1)
570 }()
571 go func() {
572 select {
573 case <-C:
574 done <- true
575 case <-stop:
576 }
577 close(done2)
578 }()
579 Sleep(sched)
580 notDone(done)
581 tim.Reset(sched / 2)
582 Sleep(sched)
583 waitDone(done)
584 tim.Stop()
585 close(stop)
586 waitDone(done1)
587 waitDone(done2)
588 if isTicker {
589
590
591 select {
592 default:
593 case <-done:
594 }
595
596 select {
597 default:
598 case <-C:
599 }
600 }
601 notDone(done)
602
603
604 stop = make(chan bool)
605 done = make(chan bool, 2)
606 for range 2 {
607 go func() {
608 select {
609 case <-C:
610 panic("unexpected data")
611 case <-stop:
612 }
613 done <- true
614 }()
615 }
616 Sleep(sched)
617 close(stop)
618 waitDone(done)
619 waitDone(done)
620
621
622
623 if synctimerchan {
624 tim.Reset(1)
625 Sleep(10 * Millisecond)
626 if pending := tim.Stop(); pending != true {
627 t.Errorf("tim.Stop() = %v, want true", pending)
628 }
629 noTick()
630
631 tim.Reset(Hour)
632 noTick()
633 if pending := tim.Reset(1); pending != true {
634 t.Errorf("tim.Stop() = %v, want true", pending)
635 }
636 assertTick()
637 Sleep(10 * Millisecond)
638 if isTicker {
639 assertTick()
640 Sleep(10 * Millisecond)
641 } else {
642 noTick()
643 }
644 if pending, want := tim.Reset(Hour), isTicker; pending != want {
645 t.Errorf("tim.Stop() = %v, want %v", pending, want)
646 }
647 noTick()
648 }
649 }
650
651 func TestManualTicker(t *testing.T) {
652
653
654
655 c := make(chan Time)
656 tick := &Ticker{C: c}
657 tick.Stop()
658 }
659
660 func TestAfterTimes(t *testing.T) {
661 t.Parallel()
662
663
664
665
666
667 for range 10 {
668 start := Now()
669 c := After(10 * Millisecond)
670 Sleep(500 * Millisecond)
671 dt := (<-c).Sub(start)
672 if dt < 400*Millisecond {
673 return
674 }
675 t.Logf("After(10ms) time is +%v, want <400ms", dt)
676 }
677 t.Errorf("not working")
678 }
679
680 func TestTickTimes(t *testing.T) {
681 t.Parallel()
682
683 for range 10 {
684 start := Now()
685 c := Tick(10 * Millisecond)
686 Sleep(500 * Millisecond)
687 dt := (<-c).Sub(start)
688 if dt < 400*Millisecond {
689 return
690 }
691 t.Logf("Tick(10ms) time is +%v, want <400ms", dt)
692 }
693 t.Errorf("not working")
694 }
695
View as plain text