2022-03-02 16:26:57 +00:00
|
|
|
package ticker
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
// IterationHandler is a callback of a certain block advance.
|
|
|
|
type IterationHandler func()
|
|
|
|
|
2022-04-21 11:28:05 +00:00
|
|
|
// IterationsTicker represents a fixed tick number block timer.
|
2022-03-02 16:26:57 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2022-04-21 11:28:05 +00:00
|
|
|
// It guaranties that a handler would be called the
|
2022-03-02 16:26:57 +00:00
|
|
|
// specified amount of times in the specified amount
|
2022-04-21 11:28:05 +00:00
|
|
|
// of blocks. After the last meaningful Tick, IterationsTicker
|
2022-03-02 16:26:57 +00:00
|
|
|
// becomes no-op timer.
|
|
|
|
//
|
2022-04-21 11:28:05 +00:00
|
|
|
// Returns an error only if times is greater than totalBlocks.
|
2022-03-02 16:26:57 +00:00
|
|
|
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
|
|
|
|
}
|