all: fix a bunch of inconsequential goroutine leaks (#20667)
The leaks were mostly in unit tests, and could all be resolved by adding suitably-sized channel buffers or by restructuring the test to not send on a channel after an error has occurred. There is an unavoidable goroutine leak in Console.Interactive: when we receive a signal, the line reader cannot be unblocked and will get stuck. This leak is now documented and I've tried to make it slightly less bad by adding a one-element buffer to the output channels of the line-reading loop. Should the reader eventually awake from its blocked state (i.e. when stdin is closed), at least it won't get stuck trying to send to the interpreter loop which has quit long ago. Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
@ -17,7 +17,6 @@
|
||||
package miner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
@ -210,49 +209,37 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) {
|
||||
w, b := newTestWorker(t, chainConfig, engine, db, 0)
|
||||
defer w.close()
|
||||
|
||||
// This test chain imports the mined blocks.
|
||||
db2 := rawdb.NewMemoryDatabase()
|
||||
b.genesis.MustCommit(db2)
|
||||
chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
var (
|
||||
loopErr = make(chan error)
|
||||
newBlock = make(chan struct{})
|
||||
subscribe = make(chan struct{})
|
||||
)
|
||||
listenNewBlock := func() {
|
||||
sub := w.mux.Subscribe(core.NewMinedBlockEvent{})
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
subscribe <- struct{}{}
|
||||
for item := range sub.Chan() {
|
||||
block := item.Data.(core.NewMinedBlockEvent).Block
|
||||
_, err := chain.InsertChain([]*types.Block{block})
|
||||
if err != nil {
|
||||
loopErr <- fmt.Errorf("failed to insert new mined block:%d, error:%v", block.NumberU64(), err)
|
||||
}
|
||||
newBlock <- struct{}{}
|
||||
}
|
||||
}
|
||||
// Ignore empty commit here for less noise
|
||||
// Ignore empty commit here for less noise.
|
||||
w.skipSealHook = func(task *task) bool {
|
||||
return len(task.receipts) == 0
|
||||
}
|
||||
go listenNewBlock()
|
||||
|
||||
<-subscribe // Ensure the subscription is created
|
||||
w.start() // Start mining!
|
||||
// Wait for mined blocks.
|
||||
sub := w.mux.Subscribe(core.NewMinedBlockEvent{})
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
// Start mining!
|
||||
w.start()
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
b.txPool.AddLocal(b.newRandomTx(true))
|
||||
b.txPool.AddLocal(b.newRandomTx(false))
|
||||
w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()})
|
||||
w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()})
|
||||
|
||||
select {
|
||||
case e := <-loopErr:
|
||||
t.Fatal(e)
|
||||
case <-newBlock:
|
||||
case <-time.NewTimer(3 * time.Second).C: // Worker needs 1s to include new changes.
|
||||
case ev := <-sub.Chan():
|
||||
block := ev.Data.(core.NewMinedBlockEvent).Block
|
||||
if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
|
||||
t.Fatalf("failed to insert new mined block %d: %v", block.NumberU64(), err)
|
||||
}
|
||||
case <-time.After(3 * time.Second): // Worker needs 1s to include new changes.
|
||||
t.Fatalf("timeout")
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user