mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +00:00
core/state: lock-free subfetcher
This commit is contained in:
parent
e3d61e6db0
commit
ed463c1ba2
1 changed files with 38 additions and 36 deletions
|
|
@ -18,7 +18,6 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
@ -230,10 +229,8 @@ type subfetcher struct {
|
||||||
addr common.Address // Address of the account that the trie belongs to
|
addr common.Address // Address of the account that the trie belongs to
|
||||||
trie Trie // Trie being populated with nodes
|
trie Trie // Trie being populated with nodes
|
||||||
|
|
||||||
tasks []*subfetcherTask // Items queued up for retrieval
|
tasks chan []*subfetcherTask // Queue items for retrieval
|
||||||
lock sync.Mutex // Lock protecting the task queue
|
|
||||||
|
|
||||||
wake chan struct{} // Wake channel if a new task is scheduled
|
|
||||||
stop chan struct{} // Channel to interrupt processing
|
stop chan struct{} // Channel to interrupt processing
|
||||||
term chan struct{} // Channel to signal interruption
|
term chan struct{} // Channel to signal interruption
|
||||||
|
|
||||||
|
|
@ -267,7 +264,7 @@ func newSubfetcher(db Database, state common.Hash, owner common.Hash, root commo
|
||||||
owner: owner,
|
owner: owner,
|
||||||
root: root,
|
root: root,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
wake: make(chan struct{}, 1),
|
tasks: make(chan []*subfetcherTask),
|
||||||
stop: make(chan struct{}),
|
stop: make(chan struct{}),
|
||||||
term: make(chan struct{}),
|
term: make(chan struct{}),
|
||||||
seenReadAddr: make(map[common.Address]struct{}),
|
seenReadAddr: make(map[common.Address]struct{}),
|
||||||
|
|
@ -281,30 +278,20 @@ func newSubfetcher(db Database, state common.Hash, owner common.Hash, root commo
|
||||||
|
|
||||||
// schedule adds a batch of trie keys to the queue to prefetch.
|
// schedule adds a batch of trie keys to the queue to prefetch.
|
||||||
func (sf *subfetcher) schedule(addrs []common.Address, slots []common.Hash, read bool) error {
|
func (sf *subfetcher) schedule(addrs []common.Address, slots []common.Hash, read bool) error {
|
||||||
// Ensure the subfetcher is still alive
|
tasks := make([]*subfetcherTask, 0, len(addrs)+len(slots))
|
||||||
select {
|
|
||||||
case <-sf.term:
|
|
||||||
return errTerminated
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
// Append the tasks to the current queue
|
|
||||||
sf.lock.Lock()
|
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
sf.tasks = append(sf.tasks, &subfetcherTask{read: read, addr: &addr})
|
tasks = append(tasks, &subfetcherTask{read: read, addr: &addr})
|
||||||
}
|
}
|
||||||
for _, slot := range slots {
|
for _, slot := range slots {
|
||||||
sf.tasks = append(sf.tasks, &subfetcherTask{read: read, slot: &slot})
|
tasks = append(tasks, &subfetcherTask{read: read, slot: &slot})
|
||||||
}
|
}
|
||||||
sf.lock.Unlock()
|
|
||||||
|
|
||||||
// Notify the background thread to execute scheduled tasks
|
|
||||||
select {
|
select {
|
||||||
case sf.wake <- struct{}{}:
|
case sf.tasks <- tasks:
|
||||||
// Wake signal sent
|
return nil
|
||||||
default:
|
case <-sf.term:
|
||||||
// Wake signal not sent as a previous one is already queued
|
return errTerminated
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait blocks until the subfetcher terminates. This method is used to block on
|
// wait blocks until the subfetcher terminates. This method is used to block on
|
||||||
|
|
@ -379,15 +366,29 @@ func (sf *subfetcher) loop() {
|
||||||
if err := sf.openTrie(); err != nil {
|
if err := sf.openTrie(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tasks []*subfetcherTask
|
||||||
|
// Adding a default case to the select statement would spin the for loop so
|
||||||
|
// we instead have a `work` channel signaller. A necessary invariant is that
|
||||||
|
// there is a buffered item i.f.f. there are tasks; therefore only
|
||||||
|
// addTasks() may append to the above slice and only the <-work branch may
|
||||||
|
// deplete it.
|
||||||
|
work := make(chan struct{}, 1)
|
||||||
|
defer close(work)
|
||||||
|
addTasks := func(ts []*subfetcherTask) {
|
||||||
|
tasks = append(tasks, ts...)
|
||||||
|
select {
|
||||||
|
case work <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-sf.wake:
|
case ts := <-sf.tasks:
|
||||||
// Execute all remaining tasks in a single run
|
addTasks(ts)
|
||||||
sf.lock.Lock()
|
|
||||||
tasks := sf.tasks
|
|
||||||
sf.tasks = nil
|
|
||||||
sf.lock.Unlock()
|
|
||||||
|
|
||||||
|
case <-work:
|
||||||
for _, task := range tasks {
|
for _, task := range tasks {
|
||||||
if task.addr != nil {
|
if task.addr != nil {
|
||||||
key := *task.addr
|
key := *task.addr
|
||||||
|
|
@ -451,20 +452,21 @@ func (sf *subfetcher) loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tasks = tasks[:0] // avoid reallocation
|
||||||
|
|
||||||
case <-sf.stop:
|
case <-sf.stop:
|
||||||
// Termination is requested, abort if no more tasks are pending. If
|
// Termination is requested, abort if no more tasks are pending. If
|
||||||
// there are some, exhaust them first.
|
// there are some, exhaust them first.
|
||||||
sf.lock.Lock()
|
if len(tasks) > 0 {
|
||||||
done := sf.tasks == nil
|
// See earlier invariant that guarantees a receive on `work` to clear `tasks`.
|
||||||
sf.lock.Unlock()
|
continue
|
||||||
|
}
|
||||||
if done {
|
select {
|
||||||
|
case ts := <-sf.tasks:
|
||||||
|
addTasks(ts)
|
||||||
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Some tasks are pending, loop and pick them up (that wake branch
|
|
||||||
// will be selected eventually, whilst stop remains closed to this
|
|
||||||
// branch will also run afterwards).
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue