github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/enode/iter.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 package enode 19 20 import ( 21 "sync" 22 "time" 23 ) 24 25 // Iterator represents a sequence of nodes. The Next method moves to the next node in the 26 // sequence. It returns false when the sequence has ended or the iterator is closed. Close 27 // may be called concurrently with Next and Node, and interrupts Next if it is blocked. 28 type Iterator interface { 29 Next() bool // moves to next node 30 Node() *Node // returns current node 31 Close() // ends the iterator 32 } 33 34 // ReadNodes reads at most n nodes from the given iterator. The return value contains no 35 // duplicates and no nil values. To prevent looping indefinitely for small repeating node 36 // sequences, this function calls Next at most n times. 37 func ReadNodes(it Iterator, n int) []*Node { 38 seen := make(map[ID]*Node, n) 39 for i := 0; i < n && it.Next(); i++ { 40 // Remove duplicates, keeping the node with higher seq. 41 node := it.Node() 42 prevNode, ok := seen[node.ID()] 43 if ok && prevNode.Seq() > node.Seq() { 44 continue 45 } 46 seen[node.ID()] = node 47 } 48 result := make([]*Node, 0, len(seen)) 49 for _, node := range seen { 50 result = append(result, node) 51 } 52 return result 53 } 54 55 // IterNodes makes an iterator which runs through the given nodes once. 56 func IterNodes(nodes []*Node) Iterator { 57 return &sliceIter{nodes: nodes, index: -1} 58 } 59 60 // CycleNodes makes an iterator which cycles through the given nodes indefinitely. 61 func CycleNodes(nodes []*Node) Iterator { 62 return &sliceIter{nodes: nodes, index: -1, cycle: true} 63 } 64 65 type sliceIter struct { 66 mu sync.Mutex 67 nodes []*Node 68 index int 69 cycle bool 70 } 71 72 func (it *sliceIter) Next() bool { 73 it.mu.Lock() 74 defer it.mu.Unlock() 75 76 if len(it.nodes) == 0 { 77 return false 78 } 79 it.index++ 80 if it.index == len(it.nodes) { 81 if it.cycle { 82 it.index = 0 83 } else { 84 it.nodes = nil 85 return false 86 } 87 } 88 return true 89 } 90 91 func (it *sliceIter) Node() *Node { 92 if len(it.nodes) == 0 { 93 return nil 94 } 95 return it.nodes[it.index] 96 } 97 98 func (it *sliceIter) Close() { 99 it.mu.Lock() 100 defer it.mu.Unlock() 101 102 it.nodes = nil 103 } 104 105 // Filter wraps an iterator such that Next only returns nodes for which 106 // the 'check' function returns true. 107 func Filter(it Iterator, check func(*Node) bool) Iterator { 108 return &filterIter{it, check} 109 } 110 111 type filterIter struct { 112 Iterator 113 check func(*Node) bool 114 } 115 116 func (f *filterIter) Next() bool { 117 for f.Iterator.Next() { 118 if f.check(f.Node()) { 119 return true 120 } 121 } 122 return false 123 } 124 125 // FairMix aggregates multiple node iterators. The mixer itself is an iterator which ends 126 // only when Close is called. Source iterators added via AddSource are removed from the 127 // mix when they end. 128 // 129 // The distribution of nodes returned by Next is approximately fair, i.e. FairMix 130 // attempts to draw from all sources equally often. However, if a certain source is slow 131 // and doesn't return a node within the configured timeout, a node from any other source 132 // will be returned. 133 // 134 // It's safe to call AddSource and Close concurrently with Next. 135 type FairMix struct { 136 wg sync.WaitGroup 137 fromAny chan *Node 138 timeout time.Duration 139 cur *Node 140 141 mu sync.Mutex 142 closed chan struct{} 143 sources []*mixSource 144 last int 145 } 146 147 type mixSource struct { 148 it Iterator 149 next chan *Node 150 timeout time.Duration 151 } 152 153 // NewFairMix creates a mixer. 154 // 155 // The timeout specifies how long the mixer will wait for the next fairly-chosen source 156 // before giving up and taking a node from any other source. A good way to set the timeout 157 // is deciding how long you'd want to wait for a node on average. Passing a negative 158 // timeout makes the mixer completely fair. 159 func NewFairMix(timeout time.Duration) *FairMix { 160 m := &FairMix{ 161 fromAny: make(chan *Node), 162 closed: make(chan struct{}), 163 timeout: timeout, 164 } 165 return m 166 } 167 168 // AddSource adds a source of nodes. 169 func (m *FairMix) AddSource(it Iterator) { 170 m.mu.Lock() 171 defer m.mu.Unlock() 172 173 if m.closed == nil { 174 return 175 } 176 m.wg.Add(1) 177 source := &mixSource{it, make(chan *Node), m.timeout} 178 m.sources = append(m.sources, source) 179 go m.runSource(m.closed, source) 180 } 181 182 // Close shuts down the mixer and all current sources. 183 // Calling this is required to release resources associated with the mixer. 184 func (m *FairMix) Close() { 185 m.mu.Lock() 186 defer m.mu.Unlock() 187 188 if m.closed == nil { 189 return 190 } 191 for _, s := range m.sources { 192 s.it.Close() 193 } 194 close(m.closed) 195 m.wg.Wait() 196 close(m.fromAny) 197 m.sources = nil 198 m.closed = nil 199 } 200 201 // Next returns a node from a random source. 202 func (m *FairMix) Next() bool { 203 m.cur = nil 204 205 var timeout <-chan time.Time 206 if m.timeout >= 0 { 207 timer := time.NewTimer(m.timeout) 208 timeout = timer.C 209 defer timer.Stop() 210 } 211 for { 212 source := m.pickSource() 213 if source == nil { 214 return m.nextFromAny() 215 } 216 select { 217 case n, ok := <-source.next: 218 if ok { 219 m.cur = n 220 source.timeout = m.timeout 221 return true 222 } 223 // This source has ended. 224 m.deleteSource(source) 225 case <-timeout: 226 source.timeout /= 2 227 return m.nextFromAny() 228 } 229 } 230 } 231 232 // Node returns the current node. 233 func (m *FairMix) Node() *Node { 234 return m.cur 235 } 236 237 // nextFromAny is used when there are no sources or when the 'fair' choice 238 // doesn't turn up a node quickly enough. 239 func (m *FairMix) nextFromAny() bool { 240 n, ok := <-m.fromAny 241 if ok { 242 m.cur = n 243 } 244 return ok 245 } 246 247 // pickSource chooses the next source to read from, cycling through them in order. 248 func (m *FairMix) pickSource() *mixSource { 249 m.mu.Lock() 250 defer m.mu.Unlock() 251 252 if len(m.sources) == 0 { 253 return nil 254 } 255 m.last = (m.last + 1) % len(m.sources) 256 return m.sources[m.last] 257 } 258 259 // deleteSource deletes a source. 260 func (m *FairMix) deleteSource(s *mixSource) { 261 m.mu.Lock() 262 defer m.mu.Unlock() 263 264 for i := range m.sources { 265 if m.sources[i] == s { 266 copy(m.sources[i:], m.sources[i+1:]) 267 m.sources[len(m.sources)-1] = nil 268 m.sources = m.sources[:len(m.sources)-1] 269 break 270 } 271 } 272 } 273 274 // runSource reads a single source in a loop. 275 func (m *FairMix) runSource(closed chan struct{}, s *mixSource) { 276 defer m.wg.Done() 277 defer close(s.next) 278 for s.it.Next() { 279 n := s.it.Node() 280 select { 281 case s.next <- n: 282 case m.fromAny <- n: 283 case <-closed: 284 return 285 } 286 } 287 }