mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
p2p/enode: implement SourceIterator for buffer and async filter
This commit is contained in:
parent
945d890350
commit
54f52bd604
1 changed files with 50 additions and 35 deletions
|
|
@ -60,6 +60,11 @@ func (it sourceIter) NodeSource() string {
|
||||||
return it.name
|
return it.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type iteratorItem struct {
|
||||||
|
n *Node
|
||||||
|
source string
|
||||||
|
}
|
||||||
|
|
||||||
// ReadNodes reads at most n nodes from the given iterator. The return value contains no
|
// ReadNodes reads at most n nodes from the given iterator. The return value contains no
|
||||||
// duplicates and no nil values. To prevent looping indefinitely for small repeating node
|
// duplicates and no nil values. To prevent looping indefinitely for small repeating node
|
||||||
// sequences, this function calls Next at most n times.
|
// sequences, this function calls Next at most n times.
|
||||||
|
|
@ -156,10 +161,10 @@ func (f *filterIter) Next() bool {
|
||||||
// asyncFilterIter wraps an iterator such that Next only returns nodes for which
|
// asyncFilterIter wraps an iterator such that Next only returns nodes for which
|
||||||
// the 'check' function returns a (possibly modified) node.
|
// the 'check' function returns a (possibly modified) node.
|
||||||
type asyncFilterIter struct {
|
type asyncFilterIter struct {
|
||||||
it Iterator // the iterator to filter
|
it SourceIterator // the iterator to filter
|
||||||
slots chan struct{} // the slots for parallel checking
|
slots chan struct{} // the slots for parallel checking
|
||||||
passed chan *Node // channel to collect passed nodes
|
passed chan iteratorItem // channel to collect passed nodes
|
||||||
buffer *Node // buffer to serve the Node call
|
cur iteratorItem // buffer to serve the Node call
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
closeOnce sync.Once
|
closeOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
@ -169,9 +174,9 @@ type AsyncFilterFunc func(context.Context, *Node) *Node
|
||||||
// AsyncFilter creates an iterator which checks nodes in parallel.
|
// AsyncFilter creates an iterator which checks nodes in parallel.
|
||||||
func AsyncFilter(it Iterator, check AsyncFilterFunc, workers int) Iterator {
|
func AsyncFilter(it Iterator, check AsyncFilterFunc, workers int) Iterator {
|
||||||
f := &asyncFilterIter{
|
f := &asyncFilterIter{
|
||||||
it: it,
|
it: ensureSourceIter(it),
|
||||||
slots: make(chan struct{}, workers+1),
|
slots: make(chan struct{}, workers+1),
|
||||||
passed: make(chan *Node),
|
passed: make(chan iteratorItem),
|
||||||
}
|
}
|
||||||
for range cap(f.slots) {
|
for range cap(f.slots) {
|
||||||
f.slots <- struct{}{}
|
f.slots <- struct{}{}
|
||||||
|
|
@ -189,13 +194,16 @@ func AsyncFilter(it Iterator, check AsyncFilterFunc, workers int) Iterator {
|
||||||
// when a node is checked, it will be sent to the passed channel
|
// when a node is checked, it will be sent to the passed channel
|
||||||
// and the slot will be released
|
// and the slot will be released
|
||||||
for f.it.Next() {
|
for f.it.Next() {
|
||||||
n := f.it.Node()
|
node := f.it.Node()
|
||||||
<-f.slots
|
nodeSource := f.it.NodeSource()
|
||||||
|
|
||||||
// check the node async, in a separate goroutine
|
// check the node async, in a separate goroutine
|
||||||
|
<-f.slots
|
||||||
go func() {
|
go func() {
|
||||||
if nn := check(ctx, n); nn != nil {
|
if nn := check(ctx, node); nn != nil {
|
||||||
|
item := iteratorItem{nn, nodeSource}
|
||||||
select {
|
select {
|
||||||
case f.passed <- nn:
|
case f.passed <- item:
|
||||||
case <-ctx.Done(): // bale out if downstream is already closed and not calling Next
|
case <-ctx.Done(): // bale out if downstream is already closed and not calling Next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -211,13 +219,19 @@ func AsyncFilter(it Iterator, check AsyncFilterFunc, workers int) Iterator {
|
||||||
|
|
||||||
// Next blocks until a node is available or the iterator is closed.
|
// Next blocks until a node is available or the iterator is closed.
|
||||||
func (f *asyncFilterIter) Next() bool {
|
func (f *asyncFilterIter) Next() bool {
|
||||||
f.buffer = <-f.passed
|
var ok bool
|
||||||
return f.buffer != nil
|
f.cur, ok = <-f.passed
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node returns the current node.
|
// Node returns the current node.
|
||||||
func (f *asyncFilterIter) Node() *Node {
|
func (f *asyncFilterIter) Node() *Node {
|
||||||
return f.buffer
|
return f.cur.n
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeSource implements IteratorSource.
|
||||||
|
func (f *asyncFilterIter) NodeSource() string {
|
||||||
|
return f.cur.source
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close ends the iterator, also closing the wrapped iterator.
|
// Close ends the iterator, also closing the wrapped iterator.
|
||||||
|
|
@ -236,17 +250,17 @@ func (f *asyncFilterIter) Close() {
|
||||||
// bufferIter wraps an iterator and buffers the nodes it returns.
|
// bufferIter wraps an iterator and buffers the nodes it returns.
|
||||||
// The buffer is pre-filled with the given size from the wrapped iterator.
|
// The buffer is pre-filled with the given size from the wrapped iterator.
|
||||||
type bufferIter struct {
|
type bufferIter struct {
|
||||||
it Iterator
|
it SourceIterator
|
||||||
buffer chan *Node
|
buffer chan iteratorItem
|
||||||
head *Node
|
head iteratorItem
|
||||||
closeOnce sync.Once
|
closeOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBufferIter creates a new pre-fetch buffer of a given size.
|
// NewBufferIter creates a new pre-fetch buffer of a given size.
|
||||||
func NewBufferIter(it Iterator, size int) Iterator {
|
func NewBufferIter(it Iterator, size int) Iterator {
|
||||||
b := bufferIter{
|
b := bufferIter{
|
||||||
it: it,
|
it: ensureSourceIter(it),
|
||||||
buffer: make(chan *Node, size),
|
buffer: make(chan iteratorItem, size),
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
|
@ -254,25 +268,31 @@ func NewBufferIter(it Iterator, size int) Iterator {
|
||||||
defer close(b.buffer)
|
defer close(b.buffer)
|
||||||
// If instead the bufferIterator is closed, we bail out of the loop.
|
// If instead the bufferIterator is closed, we bail out of the loop.
|
||||||
for b.it.Next() {
|
for b.it.Next() {
|
||||||
b.buffer <- b.it.Node()
|
item := iteratorItem{b.it.Node(), b.it.NodeSource()}
|
||||||
|
b.buffer <- item
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return &b
|
return &b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bufferIter) Next() bool {
|
func (b *bufferIter) Next() bool {
|
||||||
b.head = <-b.buffer
|
var ok bool
|
||||||
return b.head != nil
|
b.head, ok = <-b.buffer
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bufferIter) Node() *Node {
|
func (b *bufferIter) Node() *Node {
|
||||||
return b.head
|
return b.head.n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bufferIter) NodeSource() string {
|
||||||
|
return b.head.source
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bufferIter) Close() {
|
func (b *bufferIter) Close() {
|
||||||
b.closeOnce.Do(func() {
|
b.closeOnce.Do(func() {
|
||||||
b.it.Close()
|
b.it.Close()
|
||||||
// Wait for Next to terminate.
|
// Drain buffer and wait for the goroutine to end.
|
||||||
for range b.buffer {
|
for range b.buffer {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -290,9 +310,9 @@ func (b *bufferIter) Close() {
|
||||||
// It's safe to call AddSource and Close concurrently with Next.
|
// It's safe to call AddSource and Close concurrently with Next.
|
||||||
type FairMix struct {
|
type FairMix struct {
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
fromAny chan mixItem
|
fromAny chan iteratorItem
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
cur mixItem
|
cur iteratorItem
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
closed chan struct{}
|
closed chan struct{}
|
||||||
|
|
@ -302,15 +322,10 @@ type FairMix struct {
|
||||||
|
|
||||||
type mixSource struct {
|
type mixSource struct {
|
||||||
it SourceIterator
|
it SourceIterator
|
||||||
next chan mixItem
|
next chan iteratorItem
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
type mixItem struct {
|
|
||||||
n *Node
|
|
||||||
source string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFairMix creates a mixer.
|
// NewFairMix creates a mixer.
|
||||||
//
|
//
|
||||||
// The timeout specifies how long the mixer will wait for the next fairly-chosen source
|
// The timeout specifies how long the mixer will wait for the next fairly-chosen source
|
||||||
|
|
@ -319,7 +334,7 @@ type mixItem struct {
|
||||||
// timeout makes the mixer completely fair.
|
// timeout makes the mixer completely fair.
|
||||||
func NewFairMix(timeout time.Duration) *FairMix {
|
func NewFairMix(timeout time.Duration) *FairMix {
|
||||||
m := &FairMix{
|
m := &FairMix{
|
||||||
fromAny: make(chan mixItem),
|
fromAny: make(chan iteratorItem),
|
||||||
closed: make(chan struct{}),
|
closed: make(chan struct{}),
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
}
|
}
|
||||||
|
|
@ -337,7 +352,7 @@ func (m *FairMix) AddSource(it Iterator) {
|
||||||
m.wg.Add(1)
|
m.wg.Add(1)
|
||||||
source := &mixSource{
|
source := &mixSource{
|
||||||
it: ensureSourceIter(it),
|
it: ensureSourceIter(it),
|
||||||
next: make(chan mixItem),
|
next: make(chan iteratorItem),
|
||||||
timeout: m.timeout,
|
timeout: m.timeout,
|
||||||
}
|
}
|
||||||
m.sources = append(m.sources, source)
|
m.sources = append(m.sources, source)
|
||||||
|
|
@ -365,7 +380,7 @@ func (m *FairMix) Close() {
|
||||||
|
|
||||||
// Next returns a node from a random source.
|
// Next returns a node from a random source.
|
||||||
func (m *FairMix) Next() bool {
|
func (m *FairMix) Next() bool {
|
||||||
m.cur = mixItem{}
|
m.cur = iteratorItem{}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
source := m.pickSource()
|
source := m.pickSource()
|
||||||
|
|
@ -453,7 +468,7 @@ func (m *FairMix) runSource(closed chan struct{}, s *mixSource) {
|
||||||
defer m.wg.Done()
|
defer m.wg.Done()
|
||||||
defer close(s.next)
|
defer close(s.next)
|
||||||
for s.it.Next() {
|
for s.it.Next() {
|
||||||
item := mixItem{s.it.Node(), s.it.NodeSource()}
|
item := iteratorItem{s.it.Node(), s.it.NodeSource()}
|
||||||
select {
|
select {
|
||||||
case s.next <- item:
|
case s.next <- item:
|
||||||
case m.fromAny <- item:
|
case m.fromAny <- item:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue