ff5f50c52c
Update to current version of Go library. Update testsuite for removed types. * go-lang.c (go_langhook_init): Omit float_type_size when calling go_create_gogo. * go-c.h: Update declaration of go_create_gogo. From-SVN: r169098
172 lines
4.8 KiB
Go
172 lines
4.8 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package time
|
|
|
|
import (
|
|
"os"
|
|
"sync"
|
|
)
|
|
|
|
// A Ticker holds a synchronous channel that delivers `ticks' of a clock
|
|
// at intervals.
|
|
type Ticker struct {
|
|
C <-chan int64 // The channel on which the ticks are delivered.
|
|
c chan<- int64 // The same channel, but the end we use.
|
|
ns int64
|
|
shutdown chan bool // Buffered channel used to signal shutdown.
|
|
nextTick int64
|
|
next *Ticker
|
|
}
|
|
|
|
// Stop turns off a ticker. After Stop, no more ticks will be sent.
|
|
func (t *Ticker) Stop() {
|
|
// Make it non-blocking so multiple Stops don't block.
|
|
_ = t.shutdown <- true
|
|
}
|
|
|
|
// Tick is a convenience wrapper for NewTicker providing access to the ticking
|
|
// channel only. Useful for clients that have no need to shut down the ticker.
|
|
func Tick(ns int64) <-chan int64 {
|
|
if ns <= 0 {
|
|
return nil
|
|
}
|
|
return NewTicker(ns).C
|
|
}
|
|
|
|
type alarmer struct {
|
|
wakeUp chan bool // wakeup signals sent/received here
|
|
wakeMeAt chan int64
|
|
wakeTime int64
|
|
}
|
|
|
|
// Set alarm to go off at time ns, if not already set earlier.
|
|
func (a *alarmer) set(ns int64) {
|
|
switch {
|
|
case a.wakeTime > ns:
|
|
// Next tick we expect is too late; shut down the late runner
|
|
// and (after fallthrough) start a new wakeLoop.
|
|
close(a.wakeMeAt)
|
|
fallthrough
|
|
case a.wakeMeAt == nil:
|
|
// There's no wakeLoop, start one.
|
|
a.wakeMeAt = make(chan int64)
|
|
a.wakeUp = make(chan bool, 1)
|
|
go wakeLoop(a.wakeMeAt, a.wakeUp)
|
|
fallthrough
|
|
case a.wakeTime == 0:
|
|
// Nobody else is waiting; it's just us.
|
|
a.wakeTime = ns
|
|
a.wakeMeAt <- ns
|
|
default:
|
|
// There's already someone scheduled.
|
|
}
|
|
}
|
|
|
|
// Channel to notify tickerLoop of new Tickers being created.
|
|
var newTicker chan *Ticker
|
|
|
|
func startTickerLoop() {
|
|
newTicker = make(chan *Ticker)
|
|
go tickerLoop()
|
|
}
|
|
|
|
// wakeLoop delivers ticks at scheduled times, sleeping until the right moment.
|
|
// If another, earlier Ticker is created while it sleeps, tickerLoop() will start a new
|
|
// wakeLoop and signal that this one is done by closing the wakeMeAt channel.
|
|
func wakeLoop(wakeMeAt chan int64, wakeUp chan bool) {
|
|
for wakeAt := range wakeMeAt {
|
|
Sleep(wakeAt - Nanoseconds())
|
|
wakeUp <- true
|
|
}
|
|
}
|
|
|
|
// A single tickerLoop serves all ticks to Tickers. It waits for two events:
|
|
// either the creation of a new Ticker or a tick from the alarm,
|
|
// signalling a time to wake up one or more Tickers.
|
|
func tickerLoop() {
|
|
// Represents the next alarm to be delivered.
|
|
var alarm alarmer
|
|
var now, wakeTime int64
|
|
var tickers *Ticker
|
|
for {
|
|
select {
|
|
case t := <-newTicker:
|
|
// Add Ticker to list
|
|
t.next = tickers
|
|
tickers = t
|
|
// Arrange for a new alarm if this one precedes the existing one.
|
|
alarm.set(t.nextTick)
|
|
case <-alarm.wakeUp:
|
|
now = Nanoseconds()
|
|
wakeTime = now + 1e15 // very long in the future
|
|
var prev *Ticker = nil
|
|
// Scan list of tickers, delivering updates to those
|
|
// that need it and determining the next wake time.
|
|
// TODO(r): list should be sorted in time order.
|
|
for t := tickers; t != nil; t = t.next {
|
|
if _, ok := <-t.shutdown; ok {
|
|
// Ticker is done; remove it from list.
|
|
if prev == nil {
|
|
tickers = t.next
|
|
} else {
|
|
prev.next = t.next
|
|
}
|
|
continue
|
|
}
|
|
if t.nextTick <= now {
|
|
if len(t.c) == 0 {
|
|
// Only send if there's room. We must not block.
|
|
// The channel is allocated with a one-element
|
|
// buffer, which is sufficient: if he hasn't picked
|
|
// up the last tick, no point in sending more.
|
|
t.c <- now
|
|
}
|
|
t.nextTick += t.ns
|
|
if t.nextTick <= now {
|
|
// Still behind; advance in one big step.
|
|
t.nextTick += (now - t.nextTick + t.ns) / t.ns * t.ns
|
|
}
|
|
}
|
|
if t.nextTick < wakeTime {
|
|
wakeTime = t.nextTick
|
|
}
|
|
prev = t
|
|
}
|
|
if tickers != nil {
|
|
// Please send wakeup at earliest required time.
|
|
// If there are no tickers, don't bother.
|
|
alarm.wakeTime = wakeTime
|
|
alarm.wakeMeAt <- wakeTime
|
|
} else {
|
|
alarm.wakeTime = 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var onceStartTickerLoop sync.Once
|
|
|
|
// NewTicker returns a new Ticker containing a channel that will
|
|
// send the time, in nanoseconds, every ns nanoseconds. It adjusts the
|
|
// intervals to make up for pauses in delivery of the ticks. The value of
|
|
// ns must be greater than zero; if not, NewTicker will panic.
|
|
func NewTicker(ns int64) *Ticker {
|
|
if ns <= 0 {
|
|
panic(os.ErrorString("non-positive interval for NewTicker"))
|
|
}
|
|
c := make(chan int64, 1) // See comment on send in tickerLoop
|
|
t := &Ticker{
|
|
C: c,
|
|
c: c,
|
|
ns: ns,
|
|
shutdown: make(chan bool, 1),
|
|
nextTick: Nanoseconds() + ns,
|
|
}
|
|
onceStartTickerLoop.Do(startTickerLoop)
|
|
// must be run in background so global Tickers can be created
|
|
go func() { newTicker <- t }()
|
|
return t
|
|
}
|