diff --git a/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java b/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java index c5b925b64c8d..ce5d3383e89d 100644 --- a/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java +++ b/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java @@ -441,10 +441,13 @@ public static class SpringCoreBlockHoundIntegration implements BlockHoundIntegra public void applyTo(BlockHound.Builder builder) { // Avoid hard references potentially anywhere in spring-core (no need for structural dependency) - String className = "org.springframework.util.ConcurrentReferenceHashMap$Segment"; - builder.allowBlockingCallsInside(className, "doTask"); - builder.allowBlockingCallsInside(className, "clear"); - builder.allowBlockingCallsInside(className, "restructure"); + String segmentClassName = "org.springframework.util.ConcurrentReferenceHashMap$Segment"; + builder.allowBlockingCallsInside(segmentClassName, "doTask"); + builder.allowBlockingCallsInside(segmentClassName, "clear"); + builder.allowBlockingCallsInside(segmentClassName, "restructure"); + + String referenceManagerClassName = "org.springframework.util.ConcurrentReferenceHashMap$ReferenceManager"; + builder.allowBlockingCallsInside(referenceManagerClassName, "pollForPurge"); } } diff --git a/spring-core/src/test/java/org/springframework/core/SpringCoreBlockHoundIntegrationTests.java b/spring-core/src/test/java/org/springframework/core/SpringCoreBlockHoundIntegrationTests.java index a6872f168ba1..1b88dc2147b1 100644 --- a/spring-core/src/test/java/org/springframework/core/SpringCoreBlockHoundIntegrationTests.java +++ b/spring-core/src/test/java/org/springframework/core/SpringCoreBlockHoundIntegrationTests.java @@ -66,7 +66,7 @@ void blockHoundIsInstalled() { } @Test - void concurrentReferenceHashMap() { + void concurrentReferenceHashMapSegmentDoTask() { int size = 10000; Map map = new ConcurrentReferenceHashMap<>(size); @@ -88,6 +88,29 @@ void concurrentReferenceHashMap() { assertThat(map).hasSize(size); } + @Test + void concurrentReferenceHashMapSegmentClear() { + int size = 10000; + Map map = new ConcurrentReferenceHashMap<>(size); + + CompletableFuture future1 = new CompletableFuture<>(); + testNonBlockingTask(() -> { + for (int i = 0; i < size / 2; i++) { + map.put("a" + i, "bar"); + } + }, future1); + + CompletableFuture future2 = new CompletableFuture<>(); + testNonBlockingTask(() -> { + for (int i = 0; i < size; i++) { + map.clear(); + } + }, future2); + + CompletableFuture.allOf(future1, future2).join(); + assertThat(map).isEmpty(); + } + private void testNonBlockingTask(NonBlockingTask task) { CompletableFuture future = new CompletableFuture<>(); testNonBlockingTask(task, future);