github.com/geph-official/geph2@v0.22.6-0.20210211030601-f527cb59b0df/libs/kcp-go/timedsched.go (about) 1 package kcp 2 3 import ( 4 "container/heap" 5 "runtime" 6 "sync" 7 "time" 8 ) 9 10 // SystemTimedSched is the library level timed-scheduler 11 var SystemTimedSched = NewTimedSched(runtime.NumCPU()) 12 13 type timedFunc struct { 14 execute func() 15 ts time.Time 16 } 17 18 // a heap for sorted timed function 19 type timedFuncHeap []timedFunc 20 21 func (h timedFuncHeap) Len() int { return len(h) } 22 func (h timedFuncHeap) Less(i, j int) bool { return h[i].ts.Before(h[j].ts) } 23 func (h timedFuncHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 24 func (h *timedFuncHeap) Push(x interface{}) { *h = append(*h, x.(timedFunc)) } 25 func (h *timedFuncHeap) Pop() interface{} { 26 old := *h 27 n := len(old) 28 x := old[n-1] 29 old[n-1].execute = nil // avoid memory leak 30 *h = old[0 : n-1] 31 return x 32 } 33 34 // TimedSched represents the control struct for timed parallel scheduler 35 type TimedSched struct { 36 // prepending tasks 37 prependTasks []timedFunc 38 prependLock sync.Mutex 39 chPrependNotify chan struct{} 40 41 // tasks will be distributed through chTask 42 chTask chan timedFunc 43 44 dieOnce sync.Once 45 die chan struct{} 46 } 47 48 // NewTimedSched creates a parallel-scheduler with given parallelization 49 func NewTimedSched(parallel int) *TimedSched { 50 ts := new(TimedSched) 51 ts.chTask = make(chan timedFunc) 52 ts.die = make(chan struct{}) 53 ts.chPrependNotify = make(chan struct{}, 1) 54 55 for i := 0; i < parallel; i++ { 56 go ts.sched() 57 } 58 go ts.prepend() 59 return ts 60 } 61 62 func (ts *TimedSched) sched() { 63 var tasks timedFuncHeap 64 timer := time.NewTimer(0) 65 for { 66 select { 67 case task := <-ts.chTask: 68 now := time.Now() 69 if now.After(task.ts) { 70 // already delayed! execute immediately 71 //log.Println("delay", now.Sub(task.ts)) 72 task.execute() 73 } else { 74 heap.Push(&tasks, task) 75 // activate timer if timer has hibernated due to 0 tasks. 76 if tasks.Len() == 1 { 77 timer.Reset(task.ts.Sub(now)) 78 } 79 } 80 case <-timer.C: 81 for tasks.Len() > 0 { 82 now := time.Now() 83 if now.After(tasks[0].ts) { 84 task := heap.Pop(&tasks).(timedFunc) 85 //log.Println("delay", now.Sub(task.ts)) 86 task.execute() 87 } else { 88 timer.Reset(tasks[0].ts.Sub(now)) 89 break 90 } 91 } 92 case <-ts.die: 93 return 94 } 95 } 96 } 97 98 func (ts *TimedSched) prepend() { 99 var tasks []timedFunc 100 for { 101 select { 102 case <-ts.chPrependNotify: 103 ts.prependLock.Lock() 104 // keep cap to reuse slice 105 if cap(tasks) < cap(ts.prependTasks) { 106 tasks = make([]timedFunc, 0, cap(ts.prependTasks)) 107 } 108 tasks = tasks[:len(ts.prependTasks)] 109 copy(tasks, ts.prependTasks) 110 for k := range ts.prependTasks { 111 ts.prependTasks[k].execute = nil // avoid memory leak 112 } 113 ts.prependTasks = ts.prependTasks[:0] 114 ts.prependLock.Unlock() 115 116 for k := range tasks { 117 select { 118 case ts.chTask <- tasks[k]: 119 tasks[k].execute = nil // avoid memory leak 120 case <-ts.die: 121 return 122 } 123 } 124 tasks = tasks[:0] 125 case <-ts.die: 126 return 127 } 128 } 129 } 130 131 // Put a function 'f' awaiting to be executed at 'deadline' 132 func (ts *TimedSched) Put(f func(), deadline time.Time) { 133 ts.prependLock.Lock() 134 ts.prependTasks = append(ts.prependTasks, timedFunc{f, deadline}) 135 ts.prependLock.Unlock() 136 137 select { 138 case ts.chPrependNotify <- struct{}{}: 139 default: 140 } 141 } 142 143 // Close terminates this scheduler 144 func (ts *TimedSched) Close() { ts.dieOnce.Do(func() { close(ts.die) }) }