github.com/igggame/nebulas-go@v2.1.0+incompatible/common/dag/dispatcher.go (about) 1 // it under the terms of the GNU General Public License as published by 2 // the Free Software Foundation, either version 3 of the License, or 3 // (at your option) any later version. 4 // 5 // the go-nebulas library is distributed in the hope that it will be useful, 6 // but WITHOUT ANY WARRANTY; without even the implied warranty of 7 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 // GNU General Public License for more details. 9 // 10 // You should have received a copy of the GNU General Public License 11 // along with the go-nebulas library. If not, see <http://www.gnu.org/licenses/>. 12 // 13 14 package dag 15 16 import ( 17 "errors" 18 "sync" 19 "time" 20 21 "github.com/nebulasio/go-nebulas/util/logging" 22 "github.com/sirupsen/logrus" 23 ) 24 25 //type value interface{} 26 27 // Callback func node 28 type Callback func(*Node, interface{}) error 29 30 // Task struct 31 type Task struct { 32 dependence int 33 node *Node 34 } 35 36 // Errors 37 var ( 38 ErrDagHasCirclular = errors.New("dag hava circlular") 39 ErrTimeout = errors.New("dispatcher execute timeout") 40 ) 41 42 // Dispatcher struct a message dispatcher dag. 43 type Dispatcher struct { 44 concurrency int 45 cb Callback 46 muTask sync.Mutex 47 dag *Dag 48 elapseInMs int64 49 quitCh chan bool 50 queueCh chan *Node 51 tasks map[interface{}]*Task 52 queueCounter int 53 completedCounter int 54 isFinsih bool 55 finishCH chan bool 56 context interface{} 57 } 58 59 // NewDispatcher create Dag Dispatcher instance. 60 func NewDispatcher(dag *Dag, concurrency int, elapseInMs int64, context interface{}, cb Callback) *Dispatcher { 61 dp := &Dispatcher{ 62 concurrency: concurrency, 63 elapseInMs: elapseInMs, 64 dag: dag, 65 cb: cb, 66 tasks: make(map[interface{}]*Task), 67 queueCounter: 0, 68 quitCh: make(chan bool, concurrency), 69 queueCh: make(chan *Node, dag.Len()), 70 completedCounter: 0, 71 finishCH: make(chan bool, 1), 72 isFinsih: false, 73 context: context, 74 } 75 return dp 76 } 77 78 // Run dag dispatch goroutine. 79 func (dp *Dispatcher) Run() error { 80 logging.VLog().Debug("Starting Dag Dispatcher...") 81 82 vertices := dp.dag.GetNodes() 83 84 rootCounter := 0 85 for _, node := range vertices { 86 task := &Task{ 87 dependence: node.parentCounter, 88 node: node, 89 } 90 task.dependence = node.parentCounter 91 dp.tasks[node.key] = task 92 93 if task.dependence == 0 { 94 rootCounter++ 95 dp.push(node) 96 } 97 } 98 99 if rootCounter == 0 && len(vertices) > 0 { 100 return ErrDagHasCirclular 101 } 102 103 return dp.execute() 104 } 105 106 // execute callback 107 func (dp *Dispatcher) execute() error { 108 logging.VLog().Debug("loop Dag Dispatcher.") 109 110 //timerChan := time.NewTicker(time.Second).C 111 112 if dp.dag.Len() < dp.concurrency { 113 dp.concurrency = dp.dag.Len() 114 } 115 if dp.concurrency == 0 { 116 return nil 117 } 118 119 var err error 120 go func() { 121 for i := 0; i < dp.concurrency; i++ { 122 go func() { 123 for { 124 select { 125 case <-dp.quitCh: 126 logging.VLog().Debug("Stoped Dag Dispatcher.") 127 return 128 case msg := <-dp.queueCh: 129 err = dp.cb(msg, dp.context) 130 131 if err != nil { 132 dp.Stop() 133 } else { 134 isFinish, err := dp.onCompleteParentTask(msg) 135 if err != nil { 136 logging.VLog().WithFields(logrus.Fields{ 137 "err": err, 138 }).Debug("Stoped Dag Dispatcher.") 139 dp.Stop() 140 } 141 if isFinish { 142 dp.Stop() 143 } 144 } 145 } 146 } 147 }() 148 } 149 150 if dp.elapseInMs > 0 { 151 deadlineTimer := time.NewTimer(time.Duration(dp.elapseInMs) * time.Millisecond) 152 <-deadlineTimer.C 153 err = ErrTimeout 154 dp.Stop() 155 } 156 }() 157 158 <-dp.finishCH 159 return err 160 } 161 162 // Stop stop goroutine. 163 func (dp *Dispatcher) Stop() { 164 logging.VLog().Debug("Stopping dag Dispatcher...") 165 dp.muTask.Lock() 166 defer dp.muTask.Unlock() 167 if dp.isFinsih { 168 return 169 } 170 dp.isFinsih = true 171 172 for i := 0; i < dp.concurrency; i++ { 173 select { 174 case dp.quitCh <- true: 175 default: 176 } 177 } 178 dp.finishCH <- true 179 } 180 181 // push queue channel 182 func (dp *Dispatcher) push(vertx *Node) { 183 dp.queueCounter++ 184 dp.queueCh <- vertx 185 } 186 187 // CompleteParentTask completed parent tasks 188 func (dp *Dispatcher) onCompleteParentTask(node *Node) (bool, error) { 189 dp.muTask.Lock() 190 defer dp.muTask.Unlock() 191 192 key := node.key 193 194 vertices := dp.dag.GetChildrenNodes(key) 195 for _, node := range vertices { 196 err := dp.updateDependenceTask(node.key) 197 if err != nil { 198 return false, err 199 } 200 } 201 202 dp.completedCounter++ 203 204 if dp.completedCounter == dp.queueCounter { 205 if dp.queueCounter < dp.dag.Len() { 206 return false, ErrDagHasCirclular 207 } 208 return true, nil 209 } 210 211 return false, nil 212 } 213 214 // updateDependenceTask task counter 215 func (dp *Dispatcher) updateDependenceTask(key interface{}) error { 216 if _, ok := dp.tasks[key]; ok { 217 dp.tasks[key].dependence-- 218 if dp.tasks[key].dependence == 0 { 219 dp.push(dp.tasks[key].node) 220 } 221 if dp.tasks[key].dependence < 0 { 222 return ErrDagHasCirclular 223 } 224 } 225 return nil 226 }