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
6
7import "unsafe"
8
9// Note: The runtime knows the layout of struct Ticker, since newTimer allocates it.
10// Note also that Ticker and Timer have the same layout, so that newTimer can handle both.
11// The initTimer and initTicker fields are named differently so that
12// users cannot convert between the two without unsafe.
13
14// A Ticker holds a channel that delivers “ticks” of a clock
15// at intervals.
16type Ticker struct {
17	C          <-chan Time // The channel on which the ticks are delivered.
18	initTicker bool
19}
20
21// NewTicker returns a new [Ticker] containing a channel that will send
22// the current time on the channel after each tick. The period of the
23// ticks is specified by the duration argument. The ticker will adjust
24// the time interval or drop ticks to make up for slow receivers.
25// The duration d must be greater than zero; if not, NewTicker will
26// panic.
27//
28// Before Go 1.23, the garbage collector did not recover
29// tickers that had not yet expired or been stopped, so code often
30// immediately deferred t.Stop after calling NewTicker, to make
31// the ticker recoverable when it was no longer needed.
32// As of Go 1.23, the garbage collector can recover unreferenced
33// tickers, even if they haven't been stopped.
34// The Stop method is no longer necessary to help the garbage collector.
35// (Code may of course still want to call Stop to stop the ticker for other reasons.)
36func NewTicker(d Duration) *Ticker {
37	if d <= 0 {
38		panic("non-positive interval for NewTicker")
39	}
40	// Give the channel a 1-element time buffer.
41	// If the client falls behind while reading, we drop ticks
42	// on the floor until the client catches up.
43	c := make(chan Time, 1)
44	t := (*Ticker)(unsafe.Pointer(newTimer(when(d), int64(d), sendTime, c, syncTimer(c))))
45	t.C = c
46	return t
47}
48
49// Stop turns off a ticker. After Stop, no more ticks will be sent.
50// Stop does not close the channel, to prevent a concurrent goroutine
51// reading from the channel from seeing an erroneous "tick".
52func (t *Ticker) Stop() {
53	if !t.initTicker {
54		// This is misuse, and the same for time.Timer would panic,
55		// but this didn't always panic, and we keep it not panicking
56		// to avoid breaking old programs. See issue 21874.
57		return
58	}
59	stopTimer((*Timer)(unsafe.Pointer(t)))
60}
61
62// Reset stops a ticker and resets its period to the specified duration.
63// The next tick will arrive after the new period elapses. The duration d
64// must be greater than zero; if not, Reset will panic.
65func (t *Ticker) Reset(d Duration) {
66	if d <= 0 {
67		panic("non-positive interval for Ticker.Reset")
68	}
69	if !t.initTicker {
70		panic("time: Reset called on uninitialized Ticker")
71	}
72	resetTimer((*Timer)(unsafe.Pointer(t)), when(d), int64(d))
73}
74
75// Tick is a convenience wrapper for [NewTicker] providing access to the ticking
76// channel only. Unlike NewTicker, Tick will return nil if d <= 0.
77//
78// Before Go 1.23, this documentation warned that the underlying
79// [Ticker] would never be recovered by the garbage collector, and that
80// if efficiency was a concern, code should use NewTicker instead and
81// call [Ticker.Stop] when the ticker is no longer needed.
82// As of Go 1.23, the garbage collector can recover unreferenced
83// tickers, even if they haven't been stopped.
84// The Stop method is no longer necessary to help the garbage collector.
85// There is no longer any reason to prefer NewTicker when Tick will do.
86func Tick(d Duration) <-chan Time {
87	if d <= 0 {
88		return nil
89	}
90	return NewTicker(d).C
91}
92