github.com/ngicks/gokugen@v0.0.5/scheduler/dispatch_loop.go (about) 1 package scheduler 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/ngicks/gokugen/common" 8 ) 9 10 // DispatchLoop waits for a TaskTimer to emit the timer signal, 11 // and then sends scheduled tasks to worker channel. 12 // 13 // Multiple calls of Start is allowed. But performance benefits are questionable. 14 type DispatchLoop struct { 15 taskTimer *TaskTimer 16 getNow common.GetNower 17 } 18 19 // NewDispatchLoop creates DispatchLoop. 20 // 21 // panic: when one or more of arguments is nil. 22 func NewDispatchLoop(taskTimer *TaskTimer, getNow common.GetNower) *DispatchLoop { 23 if taskTimer == nil || getNow == nil { 24 panic(fmt.Errorf( 25 "%w: one or more of aruguments is nil. taskTimer is nil=[%t], getNow is nil=[%t]", 26 ErrInvalidArg, 27 taskTimer == nil, 28 getNow == nil, 29 )) 30 } 31 return &DispatchLoop{ 32 taskTimer: taskTimer, 33 getNow: getNow, 34 } 35 } 36 37 func (l *DispatchLoop) PushTask(task *Task) error { 38 return l.taskTimer.Push(task) 39 } 40 41 func (l *DispatchLoop) TaskLen() int { 42 return l.taskTimer.Len() 43 } 44 45 // Start starts a dispatch loop. 46 // Start does not have reponsibility of starting the TaskTimer. 47 // A caller must ensure that the taskTimer is started. 48 // Calling multiple Start in different goroutines is allowed, but performance benefits are questionable. 49 // Cancelling ctx will end this Start loop, with returning nil. 50 // 51 // If one or more of arguments are nil, Start immediately returns ErrInvalidArg. 52 // 53 // panic: Closing taskCh *before* cancelling ctx may cause panic. 54 func (l *DispatchLoop) Start(ctx context.Context, taskCh chan<- *Task) error { 55 if taskCh == nil || ctx == nil { 56 return ErrInvalidArg 57 } 58 59 loop: 60 for { 61 select { 62 case <-ctx.Done(): 63 break loop 64 case <-l.taskTimer.GetTimer(): 65 next := l.taskTimer.GetScheduledTask(l.getNow.GetNow()) 66 if next == nil { 67 continue 68 } 69 for _, w := range next { 70 if ctx.Err() != nil { 71 // race condition causes deadlock here. 72 // must not send in that case. 73 l.taskTimer.Push(w, false) 74 } else { 75 taskCh <- w 76 } 77 } 78 } 79 } 80 return nil 81 }