From 46765cdd0263a5230d11e962db293a61ea2ee25e Mon Sep 17 00:00:00 2001 From: Matt Whitehead Date: Tue, 4 Mar 2025 10:46:15 +0000 Subject: [PATCH] Safely handle race condition between isEmpty() and lastKey() being called (#8362) * Safely handle race condition between isEmpty() and lastKey() being called Signed-off-by: Matthew Whitehead * Fix not needed in AbstractTransactionsLayer Signed-off-by: Matthew Whitehead * Reinstate final modifier Signed-off-by: Matthew Whitehead --------- Signed-off-by: Matthew Whitehead --- .../sorter/PendingTransactionsForSender.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/PendingTransactionsForSender.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/PendingTransactionsForSender.java index 2ac0ca609..3d42b2c3d 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/PendingTransactionsForSender.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/PendingTransactionsForSender.java @@ -20,6 +20,7 @@ import org.hyperledger.besu.evm.account.Account; import java.util.List; import java.util.Map; import java.util.NavigableMap; +import java.util.NoSuchElementException; import java.util.Optional; import java.util.OptionalLong; import java.util.TreeMap; @@ -96,7 +97,15 @@ public class PendingTransactionsForSender { if (pendingTransactions.isEmpty()) { return OptionalLong.empty(); } else { - return nextGap.isEmpty() ? OptionalLong.of(pendingTransactions.lastKey() + 1) : nextGap; + try { + return nextGap.isEmpty() ? OptionalLong.of(pendingTransactions.lastKey() + 1) : nextGap; + } catch (NoSuchElementException nse) { + // There is a timing condition where isEmpty() returns false, then another thread removes + // the last key before our call to lastKey(). There's no benefit to us adding a mutex to + // prevent the other thread updating the map so we just catch the exception and treat the + // map as empty. + return OptionalLong.empty(); + } } }