github.com/hata/goseq@v0.0.0-20150316021154-a5ca66a92399/taskmanager.go (about) 1 package goseq 2 3 import ( 4 "time" 5 ) 6 7 const ( 8 initialTasksCap = 4 9 ) 10 11 // This type is defined acceptable function. 1st argument is to set 12 // a current SequenceID and 2nd 'index' value is to generate from 13 // 'id' value. index is between 0 and (size -1). 14 type TaskHandler func(id SequenceID, index int) 15 16 // Manage several TaskHandlers. 17 // Create a new instance using NewTaskManager() and then 18 // add TaskHandlers. And then, call Start() method to setup 19 // channels for each handlers. After that, call PutTask 20 // to initiate a new SequenceID and run tasks. 21 // Current version can support a single thread to call Put method. 22 type TaskManager interface { 23 Put(initHandler TaskHandler) SequenceID 24 AddHandler(handler TaskHandler, handlers ...TaskHandler) HandlerGroup 25 AddHandlers(handlers []TaskHandler) HandlerGroup 26 Start() 27 Stop() 28 } 29 30 type sequenceIDToIndexFunc func(id SequenceID) (index int) 31 32 type taskManager struct { 33 seqToIndexFunc sequenceIDToIndexFunc 34 handlerGroups []HandlerGroup 35 size SequenceID 36 indexMask SequenceID 37 currentID SequenceID 38 cachedMinSequenceID SequenceID 39 } 40 41 // Create a new TaskManager instance. 42 // size is required to set 2^x like 2,4,8,16, ... 43 func NewTaskManager(size int) TaskManager { 44 return newTaskManager(size) 45 } 46 47 func newTaskManager(size int) (tm *taskManager) { 48 tm = new(taskManager) 49 tm.currentID = initialSequenceValue 50 tm.cachedMinSequenceID = initialSequenceValue 51 tm.size = SequenceID(size) 52 tm.indexMask = SequenceID(size - 1) 53 tm.handlerGroups = make([]HandlerGroup, 0, initialTasksCap) 54 tm.seqToIndexFunc = func(id SequenceID) int { 55 return int(tm.indexMask & id) 56 } 57 return 58 } 59 60 // This method gets a new SequenceID and then call initHandler to initialize 61 // for the new SequenceID/index parameters. And then start calling TaskHandlers 62 // in different Goroutine. Current method can support only for a single thread 63 // usage to call this method. If 'index' is still used, then this method 64 // block the call until 'index' becomes available state. 65 func (tm *taskManager) Put(initHandler TaskHandler) SequenceID { 66 current := tm.currentID 67 nextID := current + 1 68 wrapPoint := nextID - tm.size 69 cachedMinSequenceID := tm.cachedMinSequenceID 70 71 if wrapPoint > cachedMinSequenceID || cachedMinSequenceID > current { 72 var minSequenceID SequenceID 73 for { 74 minSequenceID := tm.getMinimumLastProcessedID(current) 75 if wrapPoint > minSequenceID { 76 time.Sleep(1) 77 } else { 78 break 79 } 80 } 81 tm.cachedMinSequenceID = minSequenceID 82 } 83 tm.currentID = nextID 84 85 defer tm.put(nextID) 86 87 if initHandler != nil { 88 initHandler(nextID, tm.seqToIndexFunc(nextID)) 89 } 90 return nextID 91 } 92 93 // This can support a single thread operation only because 94 // multi thread call may put different order. 95 func (tm *taskManager) put(id SequenceID) { 96 for _, group := range tm.handlerGroups { 97 group.process(id) 98 } 99 } 100 101 func (tm *taskManager) AddHandler(handler TaskHandler, handlers ...TaskHandler) HandlerGroup { 102 group := newHandlerGroup(tm.seqToIndexFunc) 103 if handler != nil { 104 group.AddHandler(handler) 105 } 106 if len(handlers) > 0 { 107 group.AddHandlers(handlers) 108 } 109 tm.handlerGroups = append(tm.handlerGroups, group) 110 return group 111 } 112 113 func (tm *taskManager) AddHandlers(handlers []TaskHandler) HandlerGroup { 114 return tm.AddHandler(nil, handlers...) 115 } 116 117 // Start all configured channels. Don't add new handlers/groups after starting handlers. 118 func (tm *taskManager) Start() { 119 for _, group := range tm.handlerGroups { 120 group.startAll() 121 } 122 } 123 124 // Stop all configured channels. This is blocked until finishing all goroutines. 125 func (tm *taskManager) Stop() { 126 for _, group := range tm.handlerGroups { 127 group.stopAll() 128 } 129 } 130 131 func (tm *taskManager) getMinimumLastProcessedID(minimum SequenceID) SequenceID { 132 processedID := minimum 133 for _, handlerGroup := range tm.handlerGroups { 134 for _, lastGroup := range handlerGroup.lastHandlerGroups() { 135 n := lastGroup.LastProcessedID() 136 if n < processedID { 137 processedID = n 138 } 139 } 140 } 141 return processedID 142 }