Skip to content

Commit

Permalink
Makes context use ConcurrentHashMap
Browse files Browse the repository at this point in the history
computeIfAbsent might throw ConcurrentModificationException when working on multiple threads

fixes gh-3874
  • Loading branch information
marcingrzejszczak committed Sep 27, 2023
1 parent db78517 commit d2b7ca4
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -864,7 +865,7 @@ default boolean isNoop() {
@SuppressWarnings("unchecked")
class Context implements ContextView {

private final Map<Object, Object> map = new HashMap<>();
private final Map<Object, Object> map = new ConcurrentHashMap<>();

private String name;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@

import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;

import java.util.ConcurrentModificationException;
import java.util.concurrent.TimeUnit;

import java.util.concurrent.atomic.AtomicBoolean;

import org.awaitility.Awaitility;
import io.micrometer.observation.Observation.Context;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -324,6 +331,34 @@ void instrumentationAndCustomAndDefaultAndGlobalConventionShouldResolveToInstrum
KeyValue.of("instrumentation", "present"), KeyValue.of("low", "instrumentation"));
}

@Test
void contextShouldWorkOnMultipleThreads() {
Observation.Context context = new Observation.Context();
AtomicBoolean exception = new AtomicBoolean();

new Thread(() -> {
try {
for (int i = 0; i < 1000; i++) {
context.computeIfAbsent("foo", o -> "bar");
}
}
catch (ConcurrentModificationException ex) {
exception.set(true);
}
}).start();
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
context.clear();
}
context.put("thread", "done");
}).start();

Awaitility.await().atMost(150, TimeUnit.MILLISECONDS).pollDelay(10, TimeUnit.MILLISECONDS).untilAsserted(() -> {
assertThat(exception).as("ConcurrentModificationException must not be thrown").isFalse();
assertThat((String) context.get("thread")).isEqualTo("done");
});
}

@Test
void observe() {
String result;
Expand Down

0 comments on commit d2b7ca4

Please sign in to comment.