package timer import ( "sync" ) // BlockMeter calculates block time interval dynamically. type BlockMeter func() (uint32, error) // BlockTickHandler is a callback of a certain block advance. type BlockTickHandler func() // BlockTimer represents block timer. // // It can tick the blocks and perform certain actions // on block time intervals. type BlockTimer struct { mtx sync.Mutex dur BlockMeter baseDur uint32 cur, tgt uint32 last uint32 h BlockTickHandler once bool } // StaticBlockMeter returns BlockMeters that always returns (d, nil). func StaticBlockMeter(d uint32) BlockMeter { return func() (uint32, error) { return d, nil } } // NewBlockTimer creates a new BlockTimer. // // Reset should be called before timer ticking. func NewBlockTimer(dur BlockMeter, h BlockTickHandler) *BlockTimer { return &BlockTimer{ dur: dur, h: h, } } // NewOneTickTimer creates a new BlockTimer that ticks only once. func NewOneTickTimer(dur BlockMeter, h BlockTickHandler) *BlockTimer { return &BlockTimer{ dur: dur, h: h, once: true, } } // Reset resets previous ticks of the BlockTimer. // // Returns BlockMeter's error upon occurrence. func (t *BlockTimer) Reset() error { d, err := t.dur() if err != nil { return err } t.mtx.Lock() t.resetWithBaseInterval(d) t.mtx.Unlock() return nil } func (t *BlockTimer) resetWithBaseInterval(d uint32) { t.baseDur = d t.reset() } func (t *BlockTimer) reset() { delta := t.baseDur if delta == 0 { delta = 1 } t.tgt = delta t.cur = 0 } // Tick ticks one block in the BlockTimer. // // Executes all callbacks which are awaiting execution at the new block. func (t *BlockTimer) Tick(h uint32) { t.mtx.Lock() t.tick(h) t.mtx.Unlock() } func (t *BlockTimer) tick(h uint32) { if h != 0 && t.last == h { return } t.last = h t.cur++ if t.cur == t.tgt { // it would be advisable to optimize such execution, for example: // 1. push handler to worker pool t.wp.Submit(h); // 2. call t.tickH(h) t.h() if !t.once { t.cur = 0 t.reset() } } }