github.com/anacrolix/torrent@v1.61.0/internal/mytimer/my-timer.go (about) 1 package mytimer 2 3 import ( 4 "math" 5 "time" 6 7 "github.com/anacrolix/missinggo/v2/panicif" 8 "github.com/anacrolix/sync" 9 ) 10 11 // Very common pattern I have in my code, a timer that coordinates resets from the callback 12 // function, externally, and tracks when it's due. Only one instance of the timer callback can be 13 // running at a time, and that callback is responsible for also returning the next delay. 14 type Timer struct { 15 mu sync.RWMutex 16 when time.Time 17 f Func 18 t *time.Timer 19 inited bool 20 } 21 22 func (me *Timer) Init(first TimeValue, f Func) { 23 panicif.True(me.inited) 24 me.inited = true 25 me.f = f 26 me.when = first 27 d := time.Until(first) 28 if first.IsZero() { 29 d = math.MaxInt64 30 } 31 // Should we Stop the timer if there's no initial delay set? We can't update the timer 32 // externally unless we know if the timer is scheduled. 33 me.t = time.AfterFunc(d, me.innerCallback) 34 } 35 36 func (me *Timer) update(when time.Time) { 37 me.mu.Lock() 38 defer me.mu.Unlock() 39 // Avoid hammering the scheduler with changes. I think this will work, and is probably cheaper 40 // than avoiding our object's lock. 41 if when.Equal(me.when) { 42 return 43 } 44 if !me.t.Stop() { 45 // Timer callback might be active. 46 if !me.when.IsZero() { 47 // Timer callback is running. 48 return 49 } 50 } 51 me.reset(when) 52 } 53 54 func (me *Timer) reset(when time.Time) { 55 me.when = when 56 if !me.when.IsZero() { 57 panicif.True(me.t.Reset(time.Until(me.when))) 58 } 59 } 60 func (me *Timer) When() time.Time { 61 return me.when 62 } 63 64 func (me *Timer) innerCallback() { 65 panicif.True(me.when.IsZero()) 66 panicif.True(time.Now().Before(me.when)) 67 // Nobody else can set it while we're running (when is non-zero and the timer has fired 68 // already). 69 when := me.f() 70 me.mu.Lock() 71 me.reset(when) 72 me.mu.Unlock() 73 } 74 75 // Resets if the timer func hasn't fired. 76 func (me *Timer) Update(when time.Time) { 77 me.update(when) 78 }