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