refactor: simplify select to exclude task handling

This commit is contained in:
Arran Schlosberg 2024-11-27 10:38:43 +00:00
parent ed463c1ba2
commit 86da86c4ac
No known key found for this signature in database
GPG key ID: 5DD5567C12C5F312

View file

@ -18,6 +18,7 @@ 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"
@ -367,106 +368,89 @@ func (sf *subfetcher) loop() {
return return
} }
var tasks []*subfetcherTask work := make(chan *subfetcherTask)
// Adding a default case to the select statement would spin the for loop so go func() {
// we instead have a `work` channel signaller. A necessary invariant is that var wg sync.WaitGroup
// there is a buffered item i.f.f. there are tasks; therefore only for {
// 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 {
select {
case ts := <-sf.tasks:
addTasks(ts)
case <-work:
for _, task := range tasks {
if task.addr != nil {
key := *task.addr
if task.read {
if _, ok := sf.seenReadAddr[key]; ok {
sf.dupsRead++
continue
}
if _, ok := sf.seenWriteAddr[key]; ok {
sf.dupsCross++
continue
}
} else {
if _, ok := sf.seenReadAddr[key]; ok {
sf.dupsCross++
continue
}
if _, ok := sf.seenWriteAddr[key]; ok {
sf.dupsWrite++
continue
}
}
} else {
key := *task.slot
if task.read {
if _, ok := sf.seenReadSlot[key]; ok {
sf.dupsRead++
continue
}
if _, ok := sf.seenWriteSlot[key]; ok {
sf.dupsCross++
continue
}
} else {
if _, ok := sf.seenReadSlot[key]; ok {
sf.dupsCross++
continue
}
if _, ok := sf.seenWriteSlot[key]; ok {
sf.dupsWrite++
continue
}
}
}
if task.addr != nil {
sf.trie.GetAccount(*task.addr)
} else {
sf.trie.GetStorage(sf.addr, (*task.slot)[:])
}
if task.read {
if task.addr != nil {
sf.seenReadAddr[*task.addr] = struct{}{}
} else {
sf.seenReadSlot[*task.slot] = struct{}{}
}
} else {
if task.addr != nil {
sf.seenWriteAddr[*task.addr] = struct{}{}
} else {
sf.seenWriteSlot[*task.slot] = struct{}{}
}
}
}
tasks = tasks[:0] // avoid reallocation
case <-sf.stop:
// Termination is requested, abort if no more tasks are pending. If
// there are some, exhaust them first.
if len(tasks) > 0 {
// See earlier invariant that guarantees a receive on `work` to clear `tasks`.
continue
}
select { select {
case ts := <-sf.tasks: case tasks := <-sf.tasks:
addTasks(ts) wg.Add(1)
default: go func() {
defer wg.Done()
for _, t := range tasks {
work <- t
}
}()
case <-sf.stop:
wg.Wait() // guarantee of no more sends on `work`
close(work)
return return
} }
} }
}()
for task := range work {
if task.addr != nil {
key := *task.addr
if task.read {
if _, ok := sf.seenReadAddr[key]; ok {
sf.dupsRead++
continue
}
if _, ok := sf.seenWriteAddr[key]; ok {
sf.dupsCross++
continue
}
} else {
if _, ok := sf.seenReadAddr[key]; ok {
sf.dupsCross++
continue
}
if _, ok := sf.seenWriteAddr[key]; ok {
sf.dupsWrite++
continue
}
}
} else {
key := *task.slot
if task.read {
if _, ok := sf.seenReadSlot[key]; ok {
sf.dupsRead++
continue
}
if _, ok := sf.seenWriteSlot[key]; ok {
sf.dupsCross++
continue
}
} else {
if _, ok := sf.seenReadSlot[key]; ok {
sf.dupsCross++
continue
}
if _, ok := sf.seenWriteSlot[key]; ok {
sf.dupsWrite++
continue
}
}
}
if task.addr != nil {
sf.trie.GetAccount(*task.addr)
} else {
sf.trie.GetStorage(sf.addr, (*task.slot)[:])
}
if task.read {
if task.addr != nil {
sf.seenReadAddr[*task.addr] = struct{}{}
} else {
sf.seenReadSlot[*task.slot] = struct{}{}
}
} else {
if task.addr != nil {
sf.seenWriteAddr[*task.addr] = struct{}{}
} else {
sf.seenWriteSlot[*task.slot] = struct{}{}
}
}
} }
} }