mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
simplify AsyncFilter design
Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com> # Conflicts: # eth/backend.go
This commit is contained in:
parent
835a4e5943
commit
a80c579d7f
2 changed files with 51 additions and 42 deletions
|
|
@ -18,6 +18,7 @@
|
||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -503,10 +504,13 @@ func (s *Ethereum) setupDiscovery() error {
|
||||||
|
|
||||||
// Add DHT nodes from discv4.
|
// Add DHT nodes from discv4.
|
||||||
if s.p2pServer.DiscoveryV4() != nil {
|
if s.p2pServer.DiscoveryV4() != nil {
|
||||||
asyncFilter := s.p2pServer.DiscoveryV4().RequestENR
|
iter := s.p2pServer.DiscoveryV4().RandomNodes()
|
||||||
filter := eth.NewNodeFilter(s.blockchain)
|
resolverFunc := func(ctx context.Context, enr *enode.Node) (*enode.Node, error) {
|
||||||
iter := enode.AsyncFilter(s.p2pServer.DiscoveryV4().RandomNodes(), asyncFilter, maxParallelENRRequests)
|
// RequestENR does not yet support context. It will simply time out.
|
||||||
iter = enode.Filter(iter, filter)
|
return s.p2pServer.DiscoveryV4().RequestENR(enr)
|
||||||
|
}
|
||||||
|
iter = enode.AsyncFilter(iter, resolverFunc, maxParallelENRRequests)
|
||||||
|
iter = enode.Filter(iter, eth.NewNodeFilter(s.blockchain))
|
||||||
s.discmix.AddSource(iter)
|
s.discmix.AddSource(iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package enode
|
package enode
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -155,51 +156,53 @@ 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 Iterator // the iterator to filter
|
||||||
check func(*Node) (*Node, error) // the blocking check function
|
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 *Node // channel to collect passed nodes
|
buffer *Node // buffer to serve the Node call
|
||||||
closed chan struct{} // channel to override passed when closing
|
cancel context.CancelFunc
|
||||||
buffer *Node // buffer to serve the Node call
|
closeOnce sync.Once
|
||||||
}
|
}
|
||||||
|
type AsyncFilterFunc func(context.Context, *Node) (*Node, error)
|
||||||
|
|
||||||
// AsyncFilter creates an iterator which checks nodes in parallel.
|
// AsyncFilter creates an iterator which checks nodes in parallel.
|
||||||
func AsyncFilter(it Iterator, check func(*Node) (*Node, error), workers int) Iterator {
|
func AsyncFilter(it Iterator, check AsyncFilterFunc, workers int) Iterator {
|
||||||
f := &AsyncFilterIter{it, check, make(chan struct{}, workers), make(chan *Node), make(chan struct{}), nil}
|
f := &AsyncFilterIter{
|
||||||
|
it: it,
|
||||||
// create slots
|
slots: make(chan struct{}, workers+1),
|
||||||
|
passed: make(chan *Node),
|
||||||
|
}
|
||||||
for range cap(f.slots) {
|
for range cap(f.slots) {
|
||||||
f.slots <- struct{}{}
|
f.slots <- struct{}{}
|
||||||
}
|
}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
f.cancel = cancel
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-f.slots:
|
||||||
|
}
|
||||||
// read from the iterator and start checking nodes in parallel
|
// read from the iterator and start checking nodes in parallel
|
||||||
// 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 range f.slots {
|
for f.it.Next() {
|
||||||
if f.it.Next() {
|
n := f.it.Node()
|
||||||
if n := f.it.Node(); n != nil {
|
<-f.slots
|
||||||
// check the node async, in a separate goroutine
|
// check the node async, in a separate goroutine
|
||||||
go func() {
|
go func() {
|
||||||
if nn, err := f.check(n); err == nil {
|
if nn, err := check(ctx, n); err == nil {
|
||||||
select {
|
select {
|
||||||
case f.passed <- nn:
|
case f.passed <- nn:
|
||||||
case <-f.closed: // 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
|
||||||
}
|
}
|
||||||
}
|
|
||||||
f.slots <- struct{}{}
|
|
||||||
}()
|
|
||||||
} else {
|
|
||||||
// this is not supposed to happen
|
|
||||||
f.slots <- struct{}{}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// the iterator has ended
|
|
||||||
f.slots <- struct{}{}
|
f.slots <- struct{}{}
|
||||||
break
|
}()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// the iterator has ended
|
||||||
|
f.slots <- struct{}{}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
@ -218,13 +221,15 @@ func (f *AsyncFilterIter) Node() *Node {
|
||||||
|
|
||||||
// Close ends the iterator, also closing the wrapped iterator.
|
// Close ends the iterator, also closing the wrapped iterator.
|
||||||
func (f *AsyncFilterIter) Close() {
|
func (f *AsyncFilterIter) Close() {
|
||||||
f.it.Close()
|
f.closeOnce.Do(func() {
|
||||||
close(f.closed) // override the passed channel
|
f.it.Close()
|
||||||
for range cap(f.slots) {
|
f.cancel()
|
||||||
<-f.slots
|
for range cap(f.slots) {
|
||||||
}
|
<-f.slots
|
||||||
close(f.slots)
|
}
|
||||||
close(f.passed)
|
close(f.slots)
|
||||||
|
close(f.passed)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// FairMix aggregates multiple node iterators. The mixer itself is an iterator which ends
|
// FairMix aggregates multiple node iterators. The mixer itself is an iterator which ends
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue