mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 23:26:44 +00:00
fix : go-routine leak in commitInterrupt channel (#851)
* fix : go-routine leak in commitInterrupt channel * fix : fix lint * rm : remove t.Parallel() from TestGenerateBlockAndImport tests * fix : minor optimisations in test * fix : BenchmarkBorMining
This commit is contained in:
parent
51cbcea2d8
commit
1995521521
4 changed files with 55 additions and 59 deletions
|
|
@ -212,7 +212,7 @@ var (
|
||||||
// Test accounts
|
// Test accounts
|
||||||
testBankKey, _ = crypto.GenerateKey()
|
testBankKey, _ = crypto.GenerateKey()
|
||||||
TestBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
|
TestBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
|
||||||
testBankFunds = big.NewInt(1000000000000000000)
|
testBankFunds = big.NewInt(9000000000000000000)
|
||||||
|
|
||||||
testUserKey, _ = crypto.GenerateKey()
|
testUserKey, _ = crypto.GenerateKey()
|
||||||
testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey)
|
testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey)
|
||||||
|
|
|
||||||
|
|
@ -358,16 +358,14 @@ func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint) {
|
||||||
txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, cmath.FromBig(w.current.header.BaseFee))
|
txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, cmath.FromBig(w.current.header.BaseFee))
|
||||||
tcount := w.current.tcount
|
tcount := w.current.tcount
|
||||||
|
|
||||||
interruptCh, stopFn := getInterruptTimer(ctx, w.current, w.chain.CurrentBlock())
|
//nolint:contextcheck
|
||||||
w.commitTransactionsWithDelay(w.current, txset, nil, interruptCh, delay)
|
w.commitTransactions(w.current, txset, nil, context.Background())
|
||||||
|
|
||||||
// Only update the snapshot if any new transactions were added
|
// Only update the snapshot if any new transactions were added
|
||||||
// to the pending block
|
// to the pending block
|
||||||
if tcount != w.current.tcount {
|
if tcount != w.current.tcount {
|
||||||
w.updateSnapshot(w.current)
|
w.updateSnapshot(w.current)
|
||||||
}
|
}
|
||||||
|
|
||||||
stopFn()
|
|
||||||
} else {
|
} else {
|
||||||
// Special case, if the consensus engine is 0 period clique(dev mode),
|
// Special case, if the consensus engine is 0 period clique(dev mode),
|
||||||
// submit sealing work here since all empty submission will be rejected
|
// submit sealing work here since all empty submission will be rejected
|
||||||
|
|
@ -393,7 +391,7 @@ func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:gocognit
|
// nolint:gocognit
|
||||||
func (w *worker) commitTransactionsWithDelay(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32, interruptCh chan struct{}, delay uint) bool {
|
func (w *worker) commitTransactionsWithDelay(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32, interruptCtx context.Context, delay uint) bool {
|
||||||
gasLimit := env.header.GasLimit
|
gasLimit := env.header.GasLimit
|
||||||
if env.gasPool == nil {
|
if env.gasPool == nil {
|
||||||
env.gasPool = new(core.GasPool).AddGas(gasLimit)
|
env.gasPool = new(core.GasPool).AddGas(gasLimit)
|
||||||
|
|
@ -419,13 +417,17 @@ func (w *worker) commitTransactionsWithDelay(env *environment, txs *types.Transa
|
||||||
|
|
||||||
mainloop:
|
mainloop:
|
||||||
for {
|
for {
|
||||||
|
// case of interrupting by timeout
|
||||||
|
if interruptCtx != nil {
|
||||||
// case of interrupting by timeout
|
// case of interrupting by timeout
|
||||||
select {
|
select {
|
||||||
case <-interruptCh:
|
case <-interruptCtx.Done():
|
||||||
commitInterruptCounter.Inc(1)
|
commitInterruptCounter.Inc(1)
|
||||||
|
log.Warn("Tx Level Interrupt")
|
||||||
break mainloop
|
break mainloop
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// In the following three cases, we will interrupt the execution of the transaction.
|
// In the following three cases, we will interrupt the execution of the transaction.
|
||||||
// (1) new head block event arrival, the interrupt signal is 1
|
// (1) new head block event arrival, the interrupt signal is 1
|
||||||
// (2) worker start or restart, the interrupt signal is 1
|
// (2) worker start or restart, the interrupt signal is 1
|
||||||
|
|
@ -581,7 +583,8 @@ func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noem
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var interruptCh chan struct{}
|
//nolint:contextcheck
|
||||||
|
var interruptCtx = context.Background()
|
||||||
|
|
||||||
stopFn := func() {}
|
stopFn := func() {}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -589,7 +592,7 @@ func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noem
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if !noempty {
|
if !noempty {
|
||||||
interruptCh, stopFn = getInterruptTimer(ctx, work, w.chain.CurrentBlock())
|
interruptCtx, stopFn = getInterruptTimer(ctx, work, w.chain.CurrentBlock())
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, span := tracing.StartSpan(ctx, "commitWork")
|
ctx, span := tracing.StartSpan(ctx, "commitWork")
|
||||||
|
|
@ -610,7 +613,7 @@ func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noem
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill pending transactions from the txpool
|
// Fill pending transactions from the txpool
|
||||||
w.fillTransactionsWithDelay(ctx, interrupt, work, interruptCh, delay)
|
w.fillTransactionsWithDelay(ctx, interrupt, work, interruptCtx, delay)
|
||||||
|
|
||||||
err = w.commit(ctx, work.copy(), w.fullTaskHook, true, start)
|
err = w.commit(ctx, work.copy(), w.fullTaskHook, true, start)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -627,7 +630,7 @@ func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noem
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:gocognit
|
// nolint:gocognit
|
||||||
func (w *worker) fillTransactionsWithDelay(ctx context.Context, interrupt *int32, env *environment, interruptCh chan struct{}, delay uint) {
|
func (w *worker) fillTransactionsWithDelay(ctx context.Context, interrupt *int32, env *environment, interruptCtx context.Context, delay uint) {
|
||||||
ctx, span := tracing.StartSpan(ctx, "fillTransactions")
|
ctx, span := tracing.StartSpan(ctx, "fillTransactions")
|
||||||
defer tracing.EndSpan(span)
|
defer tracing.EndSpan(span)
|
||||||
|
|
||||||
|
|
@ -751,7 +754,7 @@ func (w *worker) fillTransactionsWithDelay(ctx context.Context, interrupt *int32
|
||||||
})
|
})
|
||||||
|
|
||||||
tracing.Exec(ctx, "", "worker.LocalCommitTransactions", func(ctx context.Context, span trace.Span) {
|
tracing.Exec(ctx, "", "worker.LocalCommitTransactions", func(ctx context.Context, span trace.Span) {
|
||||||
committed = w.commitTransactionsWithDelay(env, txs, interrupt, interruptCh, delay)
|
committed = w.commitTransactionsWithDelay(env, txs, interrupt, interruptCtx, delay)
|
||||||
})
|
})
|
||||||
|
|
||||||
if committed {
|
if committed {
|
||||||
|
|
@ -774,7 +777,7 @@ func (w *worker) fillTransactionsWithDelay(ctx context.Context, interrupt *int32
|
||||||
})
|
})
|
||||||
|
|
||||||
tracing.Exec(ctx, "", "worker.RemoteCommitTransactions", func(ctx context.Context, span trace.Span) {
|
tracing.Exec(ctx, "", "worker.RemoteCommitTransactions", func(ctx context.Context, span trace.Span) {
|
||||||
committed = w.commitTransactionsWithDelay(env, txs, interrupt, interruptCh, delay)
|
committed = w.commitTransactionsWithDelay(env, txs, interrupt, interruptCtx, delay)
|
||||||
})
|
})
|
||||||
|
|
||||||
if committed {
|
if committed {
|
||||||
|
|
|
||||||
|
|
@ -654,9 +654,8 @@ func (w *worker) mainLoop(ctx context.Context) {
|
||||||
txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, cmath.FromBig(w.current.header.BaseFee))
|
txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, cmath.FromBig(w.current.header.BaseFee))
|
||||||
tcount := w.current.tcount
|
tcount := w.current.tcount
|
||||||
|
|
||||||
var interruptCh chan struct{}
|
//nolint:contextcheck
|
||||||
|
w.commitTransactions(w.current, txset, nil, context.Background())
|
||||||
w.commitTransactions(w.current, txset, nil, interruptCh)
|
|
||||||
|
|
||||||
// Only update the snapshot if any new transactions were added
|
// Only update the snapshot if any new transactions were added
|
||||||
// to the pending block
|
// to the pending block
|
||||||
|
|
@ -945,7 +944,7 @@ func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gocognit
|
//nolint:gocognit
|
||||||
func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32, interruptCh chan struct{}) bool {
|
func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32, interruptCtx context.Context) bool {
|
||||||
gasLimit := env.header.GasLimit
|
gasLimit := env.header.GasLimit
|
||||||
if env.gasPool == nil {
|
if env.gasPool == nil {
|
||||||
env.gasPool = new(core.GasPool).AddGas(gasLimit)
|
env.gasPool = new(core.GasPool).AddGas(gasLimit)
|
||||||
|
|
@ -970,13 +969,17 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
|
||||||
|
|
||||||
mainloop:
|
mainloop:
|
||||||
for {
|
for {
|
||||||
|
// case of interrupting by timeout
|
||||||
|
if interruptCtx != nil {
|
||||||
// case of interrupting by timeout
|
// case of interrupting by timeout
|
||||||
select {
|
select {
|
||||||
case <-interruptCh:
|
case <-interruptCtx.Done():
|
||||||
commitInterruptCounter.Inc(1)
|
commitInterruptCounter.Inc(1)
|
||||||
|
log.Warn("Tx Level Interrupt")
|
||||||
break mainloop
|
break mainloop
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// In the following three cases, we will interrupt the execution of the transaction.
|
// In the following three cases, we will interrupt the execution of the transaction.
|
||||||
// (1) new head block event arrival, the interrupt signal is 1
|
// (1) new head block event arrival, the interrupt signal is 1
|
||||||
|
|
@ -1266,7 +1269,7 @@ func startProfiler(profile string, filepath string, number uint64) (func() error
|
||||||
// be customized with the plugin in the future.
|
// be customized with the plugin in the future.
|
||||||
//
|
//
|
||||||
//nolint:gocognit
|
//nolint:gocognit
|
||||||
func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *environment, interruptCh chan struct{}) {
|
func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *environment, interruptCtx context.Context) {
|
||||||
ctx, span := tracing.StartSpan(ctx, "fillTransactions")
|
ctx, span := tracing.StartSpan(ctx, "fillTransactions")
|
||||||
defer tracing.EndSpan(span)
|
defer tracing.EndSpan(span)
|
||||||
|
|
||||||
|
|
@ -1390,7 +1393,7 @@ func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *en
|
||||||
})
|
})
|
||||||
|
|
||||||
tracing.Exec(ctx, "", "worker.LocalCommitTransactions", func(ctx context.Context, span trace.Span) {
|
tracing.Exec(ctx, "", "worker.LocalCommitTransactions", func(ctx context.Context, span trace.Span) {
|
||||||
committed = w.commitTransactions(env, txs, interrupt, interruptCh)
|
committed = w.commitTransactions(env, txs, interrupt, interruptCtx)
|
||||||
})
|
})
|
||||||
|
|
||||||
if committed {
|
if committed {
|
||||||
|
|
@ -1413,7 +1416,7 @@ func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *en
|
||||||
})
|
})
|
||||||
|
|
||||||
tracing.Exec(ctx, "", "worker.RemoteCommitTransactions", func(ctx context.Context, span trace.Span) {
|
tracing.Exec(ctx, "", "worker.RemoteCommitTransactions", func(ctx context.Context, span trace.Span) {
|
||||||
committed = w.commitTransactions(env, txs, interrupt, interruptCh)
|
committed = w.commitTransactions(env, txs, interrupt, interruptCtx)
|
||||||
})
|
})
|
||||||
|
|
||||||
if committed {
|
if committed {
|
||||||
|
|
@ -1438,10 +1441,10 @@ func (w *worker) generateWork(ctx context.Context, params *generateParams) (*typ
|
||||||
}
|
}
|
||||||
defer work.discard()
|
defer work.discard()
|
||||||
|
|
||||||
interruptCh, stopFn := getInterruptTimer(ctx, work, w.chain.CurrentBlock())
|
interruptCtx, stopFn := getInterruptTimer(ctx, work, w.chain.CurrentBlock())
|
||||||
defer stopFn()
|
defer stopFn()
|
||||||
|
|
||||||
w.fillTransactions(ctx, nil, work, interruptCh)
|
w.fillTransactions(ctx, nil, work, interruptCtx)
|
||||||
|
|
||||||
return w.engine.FinalizeAndAssemble(ctx, w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts)
|
return w.engine.FinalizeAndAssemble(ctx, w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts)
|
||||||
}
|
}
|
||||||
|
|
@ -1479,7 +1482,8 @@ func (w *worker) commitWork(ctx context.Context, interrupt *int32, noempty bool,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var interruptCh chan struct{}
|
//nolint:contextcheck
|
||||||
|
var interruptCtx = context.Background()
|
||||||
|
|
||||||
stopFn := func() {}
|
stopFn := func() {}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -1487,7 +1491,7 @@ func (w *worker) commitWork(ctx context.Context, interrupt *int32, noempty bool,
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if !noempty {
|
if !noempty {
|
||||||
interruptCh, stopFn = getInterruptTimer(ctx, work, w.chain.CurrentBlock())
|
interruptCtx, stopFn = getInterruptTimer(ctx, work, w.chain.CurrentBlock())
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, span := tracing.StartSpan(ctx, "commitWork")
|
ctx, span := tracing.StartSpan(ctx, "commitWork")
|
||||||
|
|
@ -1508,7 +1512,7 @@ func (w *worker) commitWork(ctx context.Context, interrupt *int32, noempty bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill pending transactions from the txpool
|
// Fill pending transactions from the txpool
|
||||||
w.fillTransactions(ctx, interrupt, work, interruptCh)
|
w.fillTransactions(ctx, interrupt, work, interruptCtx)
|
||||||
|
|
||||||
err = w.commit(ctx, work.copy(), w.fullTaskHook, true, start)
|
err = w.commit(ctx, work.copy(), w.fullTaskHook, true, start)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1524,28 +1528,25 @@ func (w *worker) commitWork(ctx context.Context, interrupt *int32, noempty bool,
|
||||||
w.current = work
|
w.current = work
|
||||||
}
|
}
|
||||||
|
|
||||||
func getInterruptTimer(ctx context.Context, work *environment, current *types.Block) (chan struct{}, func()) {
|
func getInterruptTimer(ctx context.Context, work *environment, current *types.Block) (context.Context, func()) {
|
||||||
delay := time.Until(time.Unix(int64(work.header.Time), 0))
|
delay := time.Until(time.Unix(int64(work.header.Time), 0))
|
||||||
|
|
||||||
timeoutTimer := time.NewTimer(delay)
|
interruptCtx, cancel := context.WithTimeout(context.Background(), delay)
|
||||||
stopFn := func() {
|
|
||||||
timeoutTimer.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
blockNumber := current.NumberU64() + 1
|
blockNumber := current.NumberU64() + 1
|
||||||
interruptCh := make(chan struct{})
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
select {
|
select {
|
||||||
case <-timeoutTimer.C:
|
case <-interruptCtx.Done():
|
||||||
|
if interruptCtx.Err() != context.Canceled {
|
||||||
log.Info("Commit Interrupt. Pre-committing the current block", "block", blockNumber)
|
log.Info("Commit Interrupt. Pre-committing the current block", "block", blockNumber)
|
||||||
|
cancel()
|
||||||
close(interruptCh)
|
}
|
||||||
case <-ctx.Done(): // nothing to do
|
case <-ctx.Done(): // nothing to do
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return interruptCh, stopFn
|
return interruptCtx, cancel
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit runs any post-transaction state modifications, assembles the final block
|
// commit runs any post-transaction state modifications, assembles the final block
|
||||||
|
|
|
||||||
|
|
@ -43,21 +43,18 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/tests/bor/mocks"
|
"github.com/ethereum/go-ethereum/tests/bor/mocks"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// nolint : paralleltest
|
||||||
func TestGenerateBlockAndImportEthash(t *testing.T) {
|
func TestGenerateBlockAndImportEthash(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testGenerateBlockAndImport(t, false, false)
|
testGenerateBlockAndImport(t, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint : paralleltest
|
||||||
func TestGenerateBlockAndImportClique(t *testing.T) {
|
func TestGenerateBlockAndImportClique(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testGenerateBlockAndImport(t, true, false)
|
testGenerateBlockAndImport(t, true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint : paralleltest
|
||||||
func TestGenerateBlockAndImportBor(t *testing.T) {
|
func TestGenerateBlockAndImportBor(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testGenerateBlockAndImport(t, false, true)
|
testGenerateBlockAndImport(t, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -627,18 +624,18 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:paralleltest
|
||||||
func TestCommitInterruptExperimentBor(t *testing.T) {
|
func TestCommitInterruptExperimentBor(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
// with 1 sec block time and 200 millisec tx delay we should get 5 txs per block
|
// with 1 sec block time and 200 millisec tx delay we should get 5 txs per block
|
||||||
testCommitInterruptExperimentBor(t, 200, 5)
|
testCommitInterruptExperimentBor(t, 200, 5)
|
||||||
|
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
// with 1 sec block time and 100 millisec tx delay we should get 10 txs per block
|
// with 1 sec block time and 100 millisec tx delay we should get 10 txs per block
|
||||||
testCommitInterruptExperimentBor(t, 100, 10)
|
testCommitInterruptExperimentBor(t, 100, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:thelper
|
||||||
func testCommitInterruptExperimentBor(t *testing.T, delay uint, txCount int) {
|
func testCommitInterruptExperimentBor(t *testing.T, delay uint, txCount int) {
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
engine consensus.Engine
|
engine consensus.Engine
|
||||||
chainConfig *params.ChainConfig
|
chainConfig *params.ChainConfig
|
||||||
|
|
@ -726,11 +723,6 @@ func BenchmarkBorMining(b *testing.B) {
|
||||||
chain, _ := core.NewBlockChain(db2, nil, back.chain.Config(), engine, vm.Config{}, nil, nil, nil)
|
chain, _ := core.NewBlockChain(db2, nil, back.chain.Config(), engine, vm.Config{}, nil, nil, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
// Ignore empty commit here for less noise.
|
|
||||||
w.skipSealHook = func(task *task) bool {
|
|
||||||
return len(task.receipts) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// fulfill tx pool
|
// fulfill tx pool
|
||||||
const (
|
const (
|
||||||
totalGas = testGas + params.TxGas
|
totalGas = testGas + params.TxGas
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue