1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package time_test
6
7import (
8	"fmt"
9	"runtime"
10	"sync"
11	"testing"
12	. "time"
13)
14
15func TestTicker(t *testing.T) {
16	t.Parallel()
17
18	// We want to test that a ticker takes as much time as expected.
19	// Since we don't want the test to run for too long, we don't
20	// want to use lengthy times. This makes the test inherently flaky.
21	// Start with a short time, but try again with a long one if the
22	// first test fails.
23
24	baseCount := 10
25	baseDelta := 20 * Millisecond
26
27	// On Darwin ARM64 the tick frequency seems limited. Issue 35692.
28	if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
29		// The following test will run ticker count/2 times then reset
30		// the ticker to double the duration for the rest of count/2.
31		// Since tick frequency is limited on Darwin ARM64, use even
32		// number to give the ticks more time to let the test pass.
33		// See CL 220638.
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				// System may be overloaded; sleep a bit
74				// in the hopes it will recover.
75				Sleep(Second / 2)
76			}
77			continue
78		}
79		// Now test that the ticker stopped.
80		Sleep(2 * delta)
81		select {
82		case <-ticker.C:
83			errs = append(errs, "Ticker did not shut down")
84			continue
85		default:
86			// ok
87		}
88
89		// Test passed, so all done.
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// Issue 21874
103func TestTickerStopWithDirectInitialization(t *testing.T) {
104	c := make(chan Time)
105	tk := &Ticker{C: c}
106	tk.Stop()
107}
108
109// Test that a bug tearing down a ticker has been fixed. This routine should not deadlock.
110func 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// Test the Tick convenience wrapper.
125func TestTick(t *testing.T) {
126	// Test that giving a negative duration returns nil.
127	if got := Tick(-1); got != nil {
128		t.Errorf("Tick(-1) = %v; want nil", got)
129	}
130}
131
132// Test that NewTicker panics when given a duration less than zero.
133func 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// Test that Ticker.Reset panics when given a duration less than zero.
143func 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
153func 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	// Build up the timer heap.
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	// Give ourselves 60 seconds to complete.
175	// This used to reliably fail on a Mac M3 laptop,
176	// which needed 77 seconds.
177	// Trybots are slower, so it will fail even more reliably there.
178	// With the fix, the code runs in under a second.
179	done := make(chan bool)
180	AfterFunc(60*Second, func() { close(done) })
181
182	// Set up a queuing goroutine to ping pong through the scheduler.
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}
233func 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
243func 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
253func 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
264func 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			// Allow some slack, but inuse >= N means at least 1 allocation per iteration.
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
309func 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
325type timer interface {
326	Stop() bool
327	Reset(Duration) bool
328}
329
330// tickerTimer is a Timer with Reset and Stop methods that return bools,
331// to have the same signatures as Timer.
332type tickerTimer struct {
333	*Ticker
334	stopped bool
335}
336
337func (t *tickerTimer) Stop() bool {
338	pending := !t.stopped
339	t.stopped = true
340	t.Ticker.Stop()
341	return pending
342}
343
344func (t *tickerTimer) Reset(d Duration) bool {
345	pending := !t.stopped
346	t.stopped = false
347	t.Ticker.Reset(d)
348	return pending
349}
350
351func testTimerChan(t *testing.T, tim timer, C <-chan Time, synctimerchan bool) {
352	_, isTimer := tim.(*Timer)
353	isTicker := !isTimer
354
355	// Retry parameters. Enough to deflake even on slow machines.
356	// Windows in particular has very coarse timers so we have to
357	// wait 10ms just to make a timer go off.
358	const (
359		sched      = 10 * Millisecond
360		tries      = 100
361		drainTries = 5
362	)
363
364	// drain1 removes one potential stale time value
365	// from the timer/ticker channel after Reset.
366	// When using Go 1.23 sync timers/tickers, draining is never needed
367	// (that's the whole point of the sync timer/ticker change).
368	drain1 := func() {
369		for range drainTries {
370			select {
371			case <-C:
372				return
373			default:
374			}
375			Sleep(sched)
376		}
377	}
378
379	// drainAsync removes potential stale time values after Stop/Reset.
380	// When using Go 1 async timers, draining one or two values
381	// may be needed after Reset or Stop (see comments in body for details).
382	drainAsync := func() {
383		if synctimerchan {
384			// sync timers must have the right semantics without draining:
385			// there are no stale values.
386			return
387		}
388
389		// async timers can send one stale value (then the timer is disabled).
390		drain1()
391		if isTicker {
392			// async tickers can send two stale values: there may be one
393			// sitting in the channel buffer, and there may also be one
394			// send racing with the Reset/Stop+drain that arrives after
395			// the first drain1 has pulled the value out.
396			// This is rare, but it does happen on overloaded builder machines.
397			// It can also be reproduced on an M3 MacBook Pro using:
398			//
399			//	go test -c strings
400			//	stress ./strings.test &   # chew up CPU
401			//	go test -c -race time
402			//	stress -p 48 ./time.test -test.count=10 -test.run=TestChan/asynctimerchan=1/Ticker
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	// Test simple stop; timer never in heap.
453	tim.Stop()
454	noTick()
455
456	// Test modify of timer not in heap.
457	tim.Reset(10000 * Second)
458	noTick()
459
460	if synctimerchan {
461		// Test modify of timer in heap.
462		tim.Reset(1)
463		Sleep(sched)
464		if l, c := len(C), cap(C); l != 0 || c != 0 {
465			//t.Fatalf("len(C), cap(C) = %d, %d, want 0, 0", l, c)
466		}
467		assertTick()
468	} else {
469		// Test modify of timer in heap.
470		tim.Reset(1)
471		assertTick()
472		Sleep(sched)
473		tim.Reset(10000 * Second)
474		drainAsync()
475		noTick()
476
477		// Test that len sees an immediate tick arrive
478		// for Reset of timer in heap.
479		tim.Reset(1)
480		assertLen()
481		assertTick()
482
483		// Test that len sees an immediate tick arrive
484		// for Reset of timer NOT in heap.
485		tim.Stop()
486		drainAsync()
487		tim.Reset(1)
488		assertLen()
489		assertTick()
490	}
491
492	// Sleep long enough that a second tick must happen if this is a ticker.
493	// Test that Reset does not lose the tick that should have happened.
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	// Reset timer in heap (already reset above, but just in case).
522	tim.Reset(10000 * Second)
523	drainAsync()
524
525	// Test stop while timer in heap (because goroutine is blocked on <-C).
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	// Test reset far away while timer in heap.
536	tim.Reset(20000 * Second)
537	Sleep(sched)
538	notDone(done)
539
540	// Test imminent reset while in heap.
541	tim.Reset(1)
542	waitDone(done)
543
544	// If this is a ticker, another tick should have come in already
545	// (they are 1ns apart). If a timer, it should have stopped.
546	if isTicker {
547		assertTick()
548	} else {
549		noTick()
550	}
551
552	tim.Stop()
553	drainAsync()
554	noTick()
555
556	// Again using select and with two goroutines waiting.
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		// extra send might have sent done again
590		// (handled by buffering done above).
591		select {
592		default:
593		case <-done:
594		}
595		// extra send after that might have filled C.
596		select {
597		default:
598		case <-C:
599		}
600	}
601	notDone(done)
602
603	// Test enqueueTimerChan when timer is stopped.
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	// Test that Stop and Reset block old values from being received.
622	// (Proposal go.dev/issue/37196.)
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
651func TestManualTicker(t *testing.T) {
652	// Code should not do this, but some old code dating to Go 1.9 does.
653	// Make sure this doesn't crash.
654	// See go.dev/issue/21874.
655	c := make(chan Time)
656	tick := &Ticker{C: c}
657	tick.Stop()
658}
659
660func TestAfterTimes(t *testing.T) {
661	t.Parallel()
662	// Using After(10ms) but waiting for 500ms to read the channel
663	// should produce a time from start+10ms, not start+500ms.
664	// Make sure it does.
665	// To avoid flakes due to very long scheduling delays,
666	// require 10 failures in a row before deciding something is wrong.
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
680func TestTickTimes(t *testing.T) {
681	t.Parallel()
682	// See comment in TestAfterTimes
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