-
Notifications
You must be signed in to change notification settings - Fork 839
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix ConcurrentModificationException #6732 #6746
Changes from all commits
ab84470
b115c5a
af48863
329c05d
d736406
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -7,6 +7,8 @@ | |||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
import java.util.Locale; | ||||||||||||||||||||||||||||
import java.util.Map; | ||||||||||||||||||||||||||||
import java.util.Objects; | ||||||||||||||||||||||||||||
import java.util.Properties; | ||||||||||||||||||||||||||||
import javax.annotation.Nullable; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||
|
@@ -33,12 +35,17 @@ private ConfigUtil() {} | |||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||
public static String getString(String key, String defaultValue) { | ||||||||||||||||||||||||||||
String normalizedKey = normalizePropertyKey(key); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// Cloning in order to avoid ConcurrentModificationException | ||||||||||||||||||||||||||||
// see https://github.com/open-telemetry/opentelemetry-java/issues/6732 | ||||||||||||||||||||||||||||
String systemProperty = | ||||||||||||||||||||||||||||
neugartf marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||
System.getProperties().entrySet().stream() | ||||||||||||||||||||||||||||
.filter(entry -> normalizedKey.equals(normalizePropertyKey(entry.getKey().toString()))) | ||||||||||||||||||||||||||||
.map(entry -> entry.getValue().toString()) | ||||||||||||||||||||||||||||
.findFirst() | ||||||||||||||||||||||||||||
.orElse(null); | ||||||||||||||||||||||||||||
((Properties) System.getProperties().clone()) | ||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to have a test recreating this CME and demonstrating that this fixes it. However, I spent a decent amount of time trying to hammer this with concurrent reads and writes and was unable to recreate it, so it could be specific to some version of java or the jdk. I'm content give this a shot even without strong proof that it resolves it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like it does not reproduces on jdk 11+ probably because of https://github.com/openjdk/jdk11u/blob/cee8535a9d3de8558b4b5028d68e397e508bef71/src/java.base/share/classes/java/util/Properties.java#L159-L165 import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static void main(String[] args) {
int threads = 4;
AtomicInteger counter = new AtomicInteger();
Executor executor = Executors.newFixedThreadPool(threads);
for (int i = 0; i < threads; i++) {
executor.execute(new Runnable() {
@Override
public void run() {
while (true) {
String property = "prop " + counter.getAndIncrement();
System.setProperty(property, "a");
System.getProperties().remove(property);
}
}
});
}
while (true) {
System.getProperties().entrySet().stream()
.filter(
entry -> "x".equals(entry.getKey().toString())
)
.map(entry -> entry.getValue().toString())
.findFirst()
.orElse(null);
}
}
}
Properties properties = System.getProperties();
synchronized (properties) {
properties.entrySet().stream()
.filter(
entry -> "x".equals(entry.getKey().toString())
)
.map(entry -> entry.getValue().toString())
.findFirst()
.orElse(null);
} either. Unlike the proposed fix this version does not make extra copies of system properties. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good find @laurit! I know there was some skepticism about taking a lock on |
||||||||||||||||||||||||||||
.stringPropertyNames().stream() | ||||||||||||||||||||||||||||
.filter(propertyName -> normalizedKey.equals(normalizePropertyKey(propertyName))) | ||||||||||||||||||||||||||||
.map(System::getProperty) | ||||||||||||||||||||||||||||
.filter(Objects::nonNull) | ||||||||||||||||||||||||||||
.findFirst() | ||||||||||||||||||||||||||||
.orElse(null); | ||||||||||||||||||||||||||||
Comment on lines
+42
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unmodifiable set is superfluous since there's no attempt to modify the contents. |
||||||||||||||||||||||||||||
if (systemProperty != null) { | ||||||||||||||||||||||||||||
return systemProperty; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.