diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 4779f9756b..37747ab9a1 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -18,6 +18,7 @@ package catalyst import ( + "context" "errors" "fmt" "strconv" @@ -33,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/internal/syncx" "github.com/ethereum/go-ethereum/internal/version" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/miner" @@ -151,8 +153,8 @@ type ConsensusAPI struct { lastNewPayloadUpdate time.Time lastNewPayloadLock sync.Mutex - forkchoiceLock sync.Mutex // Lock for the forkChoiceUpdated method - newPayloadLock sync.Mutex // Lock for the NewPayload method + forkchoiceLock sync.Mutex // Lock for the forkChoiceUpdated method + newPayloadLock syncx.ClosableMutex // Lock for the NewPayload method } // NewConsensusAPI creates a new consensus api for the given backend. @@ -832,7 +834,11 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe // sequentially. // Hence, we use a lock here, to be sure that the previous call has finished before we // check whether we already have the block locally. - api.newPayloadLock.Lock() + ctx, _ := context.WithTimeout(context.Background(), time.Minute) + if !api.newPayloadLock.TryLockWithContext(ctx) { + // Lock not acquired. + return engine.PayloadStatusV1{Status: engine.SYNCING}, engine.GenericServerError + } defer api.newPayloadLock.Unlock() log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash) diff --git a/internal/syncx/mutex.go b/internal/syncx/mutex.go index 96a21986c6..5c6b63f132 100644 --- a/internal/syncx/mutex.go +++ b/internal/syncx/mutex.go @@ -17,6 +17,8 @@ // Package syncx contains exotic synchronization primitives. package syncx +import "context" + // ClosableMutex is a mutex that can also be closed. // Once closed, it can never be taken again. type ClosableMutex struct { @@ -36,6 +38,18 @@ func (cm *ClosableMutex) TryLock() bool { return ok } +// TryLockWithContext attempts to lock tm, but also respecting the context +// cancelling. This can be used to set a timeout on a locking-attempt. +// If the mutex is closed, or context cancelled, TryLock returns false. +func (cm *ClosableMutex) TryLockWithContext(ctx context.Context) bool { + select { + case _, ok := <-cm.ch: + return ok + case <-ctx.Done(): + return false + } +} + // MustLock locks cm. // If the mutex is closed, MustLock panics. func (cm *ClosableMutex) MustLock() {