package endtoend import ( "time" "github.com/prysmaticlabs/prysm/shared/roughtime" ) // EpochTicker is a special ticker for timing epoch changes. // The channel emits over the epoch interval, and ensures that // the ticks are in line with the genesis time. This means that // the duration between the ticks and the genesis time are always a // multiple of the epoch duration. // In addition, the channel returns the new epoch number. type EpochTicker struct { c chan uint64 done chan struct{} } // C returns the ticker channel. Call Cancel afterwards to ensure // that the goroutine exits cleanly. func (s *EpochTicker) C() <-chan uint64 { return s.c } // Done should be called to clean up the ticker. func (s *EpochTicker) Done() { go func() { s.done <- struct{}{} }() } // GetEpochTicker is the constructor for EpochTicker. func GetEpochTicker(genesisTime time.Time, secondsPerEpoch uint64) *EpochTicker { ticker := &EpochTicker{ c: make(chan uint64), done: make(chan struct{}), } ticker.start(genesisTime, secondsPerEpoch, roughtime.Since, roughtime.Until, time.After) return ticker } func (s *EpochTicker) start( genesisTime time.Time, secondsPerEpoch uint64, since func(time.Time) time.Duration, until func(time.Time) time.Duration, after func(time.Duration) <-chan time.Time) { d := time.Duration(secondsPerEpoch) * time.Second go func() { sinceGenesis := since(genesisTime) var nextTickTime time.Time var epoch uint64 if sinceGenesis < 0 { // Handle when the current time is before the genesis time. nextTickTime = genesisTime epoch = 0 } else { nextTick := sinceGenesis.Truncate(d) + d nextTickTime = genesisTime.Add(nextTick) epoch = uint64(nextTick / d) } for { waitTime := until(nextTickTime) select { case <-after(waitTime): s.c <- epoch epoch++ nextTickTime = nextTickTime.Add(d) case <-s.done: return } } }() }