91 lines
1.8 KiB
Go
91 lines
1.8 KiB
Go
|
package ticker
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
// IterationHandler is a callback of a certain block advance.
|
||
|
type IterationHandler func()
|
||
|
|
||
|
// IterationsTicker represents fixed tick number block timer.
|
||
|
//
|
||
|
// It can tick the blocks and perform certain actions
|
||
|
// on block time intervals.
|
||
|
type IterationsTicker struct {
|
||
|
m sync.Mutex
|
||
|
|
||
|
curr uint64
|
||
|
period uint64
|
||
|
|
||
|
times uint64
|
||
|
|
||
|
h IterationHandler
|
||
|
}
|
||
|
|
||
|
// NewIterationsTicker creates a new IterationsTicker.
|
||
|
//
|
||
|
// It guaranties that handler would be called the
|
||
|
// specified amount of times in the specified amount
|
||
|
// of blocks. After the last meaningful Tick IterationsTicker
|
||
|
// becomes no-op timer.
|
||
|
//
|
||
|
// Returns error only if times is greater than totalBlocks.
|
||
|
func NewIterationsTicker(totalBlocks uint64, times uint64, h IterationHandler) (*IterationsTicker, error) {
|
||
|
period := totalBlocks / times
|
||
|
|
||
|
if period == 0 {
|
||
|
return nil, fmt.Errorf("impossible to tick %d times in %d blocks",
|
||
|
times, totalBlocks,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
var curr uint64
|
||
|
|
||
|
// try to make handler calls as rare as possible
|
||
|
if totalBlocks%times != 0 {
|
||
|
extraBlocks := (period+1)*times - totalBlocks
|
||
|
|
||
|
if period >= extraBlocks {
|
||
|
curr = extraBlocks + (period-extraBlocks)/2
|
||
|
period++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return &IterationsTicker{
|
||
|
curr: curr,
|
||
|
period: period,
|
||
|
times: times,
|
||
|
h: h,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// Tick ticks one block in the IterationsTicker.
|
||
|
//
|
||
|
// Returns `false` if the timer has finished its operations
|
||
|
// and there will be no more handler calls.
|
||
|
// Calling Tick after the returned `false` is safe, no-op
|
||
|
// and also returns `false`.
|
||
|
func (ft *IterationsTicker) Tick() bool {
|
||
|
ft.m.Lock()
|
||
|
defer ft.m.Unlock()
|
||
|
|
||
|
if ft.times == 0 {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
ft.curr++
|
||
|
|
||
|
if ft.curr%ft.period == 0 {
|
||
|
ft.h()
|
||
|
|
||
|
ft.times--
|
||
|
|
||
|
if ft.times == 0 {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|