- Notifications
You must be signed in to change notification settings - Fork 915
Fix ConcurrentModificationException #6732 #6746
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
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()) | ||||||||||||||||||||||||||||
| Member 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. Contributor 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. Member 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 Contributor 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
Member 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.