github.com/hata/goseq@v0.0.0-20150316021154-a5ca66a92399/handlergroup.go (about) 1 /* 2 Package sequence is to run small tasks sequencially using Goroutine. 3 4 The current version of Go language cannot use generic type without type 5 assertion. So, this library doesn't manage efficient usage for context 6 instances. Instead of using a context instance, this library provides 7 'index' value to use a cache index. The provided index is 8 generated based on configured 'size' parameter for TaskManager and 9 it is reused after finishing a previous task which uses 'index'. 10 */ 11 package goseq 12 13 import ( 14 "runtime" 15 "sync" 16 ) 17 18 const ( 19 channelBufferSize = 512 20 stopCurrentHandlerGroupOnly = -1 21 stopAllHandlerGroups = -2 22 ) 23 24 // HandlerGroup is to manage several TaskHandler instances. 25 // Added TaskHandlers are run at the same time when a new 26 // SequenceID is put. After finishing all tasks for the SequenceID, 27 // then next HandlerGroups are received the finished SequenceID. 28 type HandlerGroup interface { 29 AddHandler(handler TaskHandler, handlers ...TaskHandler) 30 AddHandlers(handlers []TaskHandler) 31 Then(handler TaskHandler, handlers ...TaskHandler) HandlerGroup 32 LastProcessedID() SequenceID 33 34 start() 35 stop() 36 startAll() 37 stopAll() 38 waitStop() 39 waitStopAll() 40 41 process(id SequenceID) 42 addNextGroup(nextGroup HandlerGroup) 43 addNextGroups(nextGroup HandlerGroup, nextGroups ...HandlerGroup) 44 45 lastHandlerGroups() []HandlerGroup 46 47 numOfHandlers() int 48 } 49 50 type handlerGroup struct { 51 name string 52 nextGroups []HandlerGroup 53 handlers []TaskHandler 54 inChannels []chan SequenceID 55 outChannels []chan SequenceID 56 lastProcessedID Sequence 57 seqToIndexFunc sequenceIDToIndexFunc 58 waitingStart sync.WaitGroup 59 waitingStop sync.WaitGroup 60 } 61 62 func newHandlerGroup(toIndexFunc sequenceIDToIndexFunc) (group *handlerGroup) { 63 group = new(handlerGroup) 64 group.handlers = make([]TaskHandler, 0, 2) 65 group.nextGroups = make([]HandlerGroup, 0, 2) 66 group.lastProcessedID = NewSequence() 67 group.seqToIndexFunc = toIndexFunc 68 return 69 } 70 71 func (group *handlerGroup) process(id SequenceID) { 72 for _, ch := range group.inChannels { 73 ch <- id 74 } 75 } 76 77 // Add a TaskHandler or some TaskHandlers. 78 func (group *handlerGroup) AddHandler(handler TaskHandler, handlers ...TaskHandler) { 79 group.handlers = append(group.handlers, handler) 80 if len(handlers) > 0 { 81 group.handlers = append(group.handlers, handlers...) 82 } 83 } 84 85 // Add TaskHandlers from an slice of TaskHandler. 86 func (group *handlerGroup) AddHandlers(handlers []TaskHandler) { 87 if handlers != nil { 88 group.handlers = append(group.handlers, handlers...) 89 } 90 } 91 92 func (group *handlerGroup) addNextGroup(nextGroup HandlerGroup) { 93 group.nextGroups = append(group.nextGroups, nextGroup) 94 } 95 96 func (group *handlerGroup) addNextGroups(nextGroup HandlerGroup, nextGroups ...HandlerGroup) { 97 group.nextGroups = append(group.nextGroups, nextGroup) 98 if nextGroups != nil { 99 group.nextGroups = append(group.nextGroups, nextGroups...) 100 } 101 } 102 103 func (group *handlerGroup) processHandler(handler TaskHandler, inChannel <-chan SequenceID, outChannel chan<- SequenceID) { 104 group.waitingStart.Done() 105 defer group.waitingStop.Done() 106 for { 107 id := <-inChannel 108 if id == stopCurrentHandlerGroupOnly || id == stopAllHandlerGroups { 109 outChannel <- id 110 break 111 } 112 handler(id, group.seqToIndexFunc(id)) 113 outChannel <- id 114 runtime.Gosched() 115 } 116 } 117 118 func (group *handlerGroup) sendToNextGroups() { 119 group.waitingStart.Done() 120 defer group.waitingStop.Done() 121 var id SequenceID 122 if len(group.outChannels) > 0 { 123 for { 124 // This loop expects all ids are the same. 125 for _, ch := range group.outChannels { 126 id = <-ch 127 } 128 if id == stopCurrentHandlerGroupOnly { 129 break 130 } 131 for _, nextGroup := range group.nextGroups { 132 nextGroup.process(id) 133 } 134 if id == stopAllHandlerGroups { 135 break 136 } 137 group.lastProcessedID.Set(id) 138 } 139 } 140 } 141 142 func (group *handlerGroup) start() { 143 length := len(group.handlers) 144 group.waitingStart.Add(length + 1) 145 group.waitingStop.Add(length + 1) 146 group.inChannels = make([]chan SequenceID, length) 147 group.outChannels = make([]chan SequenceID, length) 148 149 for i, handler := range group.handlers { 150 group.inChannels[i] = make(chan SequenceID, channelBufferSize) 151 group.outChannels[i] = make(chan SequenceID, channelBufferSize) 152 go group.processHandler(handler, group.inChannels[i], group.outChannels[i]) 153 } 154 155 go group.sendToNextGroups() 156 group.waitingStart.Wait() 157 } 158 159 func (group *handlerGroup) startAll() { 160 for _, nextGroup := range group.nextGroups { 161 nextGroup.startAll() 162 } 163 group.start() 164 } 165 166 func (group *handlerGroup) stop() { 167 group.process(stopCurrentHandlerGroupOnly) 168 group.waitStop() 169 } 170 171 func (group *handlerGroup) stopAll() { 172 group.process(stopAllHandlerGroups) 173 group.waitStopAll() 174 } 175 176 func (group *handlerGroup) waitStop() { 177 group.waitingStop.Wait() 178 defer func() { 179 group.inChannels = nil 180 group.outChannels = nil 181 }() 182 183 for _, ch := range group.inChannels { 184 close(ch) 185 } 186 for _, ch := range group.outChannels { 187 close(ch) 188 } 189 } 190 191 func (group *handlerGroup) waitStopAll() { 192 group.waitStop() 193 for _, nextGroup := range group.nextGroups { 194 nextGroup.waitStopAll() 195 } 196 } 197 198 func (group *handlerGroup) lastHandlerGroups() []HandlerGroup { 199 groups := make([]HandlerGroup, 0, len(group.nextGroups)) 200 if len(group.nextGroups) == 0 { 201 groups = append(groups, group) 202 } else { 203 for _, next := range group.nextGroups { 204 groups = append(groups, next.lastHandlerGroups()...) 205 } 206 } 207 return groups 208 } 209 210 func (group *handlerGroup) numOfHandlers() int { 211 return len(group.handlers) 212 } 213 214 // Create a new HandlerGroup and then add new TaskHandler instances to the 215 // new HandlerGroup. And then return the new handler. This new added handlers 216 // are run after running current(group instance) HandlerGroup's TaskHandlers. 217 func (group *handlerGroup) Then(handler TaskHandler, handlers ...TaskHandler) HandlerGroup { 218 newGroup := newHandlerGroup(group.seqToIndexFunc) 219 newGroup.AddHandler(handler, handlers...) 220 group.addNextGroups(newGroup) 221 return newGroup 222 } 223 224 // Get finished SequenceID. The returned value means that all tasks are finished 225 // for the specific returned value or more smaller SequenceIDs. 226 func (group *handlerGroup) LastProcessedID() SequenceID { 227 return group.lastProcessedID.Get() 228 }