github.com/ngicks/gokugen@v0.0.5/scheduler/canceller_loop.go (about) 1 package scheduler 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/ngicks/gokugen/common" 9 ) 10 11 // CancellerLoop requests TaskTimer to remove cancelled element at a given interval. 12 // This is intended to be only one running instance per a TaskTimer. 13 type CancellerLoop struct { 14 workingState 15 taskTimer *TaskTimer 16 getNow common.GetNower 17 interval time.Duration 18 } 19 20 // NewCancellerLoop creates a CancellerLoop. 21 // 22 // panic: when one or more of arguments is nil or zero-vakue. 23 func NewCancellerLoop(taskTimer *TaskTimer, getNow common.GetNower, interval time.Duration) *CancellerLoop { 24 if taskTimer == nil || getNow == nil || interval <= 0 { 25 panic(fmt.Errorf( 26 "%w: one or more of aruguments is nil or zero-value. taskTimer is nil=[%t], getNow is nil=[%t], interval is zero=[%t]", 27 ErrInvalidArg, 28 taskTimer == nil, 29 getNow == nil, 30 interval == 0, 31 )) 32 } 33 return &CancellerLoop{ 34 taskTimer: taskTimer, 35 getNow: getNow, 36 interval: interval, 37 } 38 } 39 40 // Start starts a loop that requests TaskTimer to remove cancelled tasks at at given interval. 41 // Cancelling of Start is controlled by ctx. 42 // 43 // If ctx is nil, Start immediately returns ErrInvalidArg. 44 // If loop is already running in some goroutine, Start immediately returns ErrAlreadyStarted. 45 func (l *CancellerLoop) Start(ctx context.Context) error { 46 if ctx == nil { 47 return ErrInvalidArg 48 } 49 50 if !l.setWorking() { 51 return ErrAlreadyStarted 52 } 53 defer l.setWorking(false) 54 55 ticker := time.NewTicker(l.interval) 56 defer ticker.Stop() 57 58 loop: 59 for { 60 select { 61 case <-ctx.Done(): 62 break loop 63 case <-ticker.C: 64 removeCancelled(l.taskTimer, l.getNow) 65 } 66 } 67 return nil 68 } 69 70 func removeCancelled(taskTimer *TaskTimer, getNow common.GetNower) (removed bool) { 71 p := taskTimer.Peek() 72 if p != nil && p.scheduledTime.Sub(getNow.GetNow()) > time.Second { 73 // Racy Push may add min element in between previous Peek and this RemoveCancelled. 74 // But it is ok because each taskTimer method is thread safe. 75 // Benchmark shows RemoveCancelled takes only a few micro secs. 76 taskTimer.RemoveCancelled(0, 10_000) 77 return true 78 } 79 return false 80 }