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