From 24ba349c13952e333eabbc89dbcfe4241e4fce55 Mon Sep 17 00:00:00 2001 From: Enrico Del Fante Date: Mon, 11 Dec 2023 21:28:17 +0100 Subject: [PATCH] fix potential concurrent modification exception in PendingPool (#7813) --- .../statetransition/util/PendingPool.java | 8 ++++++-- .../statetransition/util/PendingPoolTest.java | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/PendingPool.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/PendingPool.java index ea5ba18c791..b6dc6a8ac58 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/PendingPool.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/PendingPool.java @@ -97,6 +97,7 @@ public synchronized void add(T item) { final Bytes32 itemRoot = hashTreeRootFunction.apply(item); final Collection requiredRoots = requiredBlockRootsFunction.apply(item); + final ArrayList newRequiredRoots = new ArrayList<>(); requiredRoots.forEach( requiredRoot -> @@ -106,12 +107,15 @@ public synchronized void add(T item) { requiredRoot, (key) -> { final Set dependants = new HashSet<>(); - requiredBlockRootSubscribers.forEach( - c -> c.onRequiredBlockRoot(requiredRoot)); + newRequiredRoots.add(requiredRoot); return dependants; }) .add(itemRoot)); + newRequiredRoots.forEach( + requiredRoot -> + requiredBlockRootSubscribers.forEach(s -> s.onRequiredBlockRoot(requiredRoot))); + // Index item by root if (pendingItems.putIfAbsent(itemRoot, item) == null) { LOG.trace( diff --git a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/PendingPoolTest.java b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/PendingPoolTest.java index ed29f0a8f5f..bb5d7280a64 100644 --- a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/PendingPoolTest.java +++ b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/PendingPoolTest.java @@ -59,6 +59,25 @@ private void setSlot(final UInt64 slot) { pendingPool.onSlot(slot); } + @Test + public void add_shouldDeferSubscribersCallToAvoidConcurrentModificationException() { + final SignedBeaconBlock block = + dataStructureUtil.randomSignedBeaconBlock(currentSlot.longValue()); + + final SignedBeaconBlock block2 = + dataStructureUtil.randomSignedBeaconBlock(currentSlot.longValue()); + + pendingPool.subscribeRequiredBlockRoot( + blockRoot -> { + if (blockRoot.equals(block.getParentRoot())) { + pendingPool.add(block2); + } + }); + pendingPool.add(block); + + assertThat(pendingPool.contains(block2)).isTrue(); + } + @Test public void add_blockForCurrentSlot() { final SignedBeaconBlock block =