From f6e96a24d449630ded4ed41346935480da4952a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Basl=C3=A9?= Date: Tue, 17 Sep 2024 16:33:29 +0200 Subject: [PATCH 1/2] Add BlockHound exception for ConcurrentReferenceHashMap$ReferenceManager This commit adds a SpringCoreBlockHoundIntegration configuration to allows `pollForPurge` method to block. Closes gh-33450 --- .../springframework/core/ReactiveAdapterRegistry.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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 994b6e036e..afc77e0225 100644 --- a/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java +++ b/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java @@ -442,10 +442,13 @@ public class ReactiveAdapterRegistry { 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"); } } From de4ff4b44bbe0147009e3d98d31b6bc5f2373a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Basl=C3=A9?= Date: Tue, 17 Sep 2024 16:34:18 +0200 Subject: [PATCH 2/2] Polishing tests This commit adds more test coverage of SpringCoreBlockHoundIntegration. See gh-33450 --- .../SpringCoreBlockHoundIntegrationTests.java | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) 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 a6872f168b..1b88dc2147 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 @@ class SpringCoreBlockHoundIntegrationTests { } @Test - void concurrentReferenceHashMap() { + void concurrentReferenceHashMapSegmentDoTask() { int size = 10000; Map map = new ConcurrentReferenceHashMap<>(size); @@ -88,6 +88,29 @@ class SpringCoreBlockHoundIntegrationTests { 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);