GH-707 Fix Concurrent Modification exception on lookup

Rare condition but it appears that in some runtimes there could be multiple threads invoking lookup operation
Added test to validate

Resolves #707
This commit is contained in:
Oleg Zhurakousky
2021-06-08 14:07:32 +02:00
parent 1e1b4482bb
commit 378f0d33ab
2 changed files with 25 additions and 2 deletions

View File

@@ -25,12 +25,12 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -90,7 +90,7 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect
private final Field headersField;
private final Set<FunctionRegistration<?>> functionRegistrations = new HashSet<>();
private final Set<FunctionRegistration<?>> functionRegistrations = new CopyOnWriteArraySet<>();
private final Map<String, FunctionInvocationWrapper> wrappedFunctionDefinitions = new HashMap<>();

View File

@@ -26,6 +26,10 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -626,7 +630,26 @@ public class BeanFactoryAwareFunctionRegistryTests {
assertThat(result.size()).isEqualTo(3);
}
@Test
// see GH-707
public void testConcurrencyOnLookup() throws Exception {
AtomicInteger counter = new AtomicInteger();
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
FunctionCatalog catalog = this.configureCatalog(SampleFunctionConfiguration.class);
for (int y = 0; y < 10; y++) {
executor.execute(() -> {
assertThat((FunctionInvocationWrapper) catalog.lookup("uppercase|reverse", "application/json")).isNotNull();
counter.incrementAndGet();
});
}
}
executor.shutdown();
executor.awaitTermination(10000, TimeUnit.MILLISECONDS);
assertThat(counter.get()).isEqualTo(100);
}
@EnableAutoConfiguration
public static class PojoToMessageFunctionCompositionConfiguration {