> Although its name might suggest otherwise, > java.util.ConcurrentModificationException (CME) is not necessarily thrown in > multithreaded context. For example, a map might throw CME if it detects > modification in a midst of an operation which is supposed to be exclusive: > > jshell> Map<Integer, Integer> m = new HashMap<>(); > ...> m.computeIfAbsent(0, key -> m.put(1, 2)); > m ==> {} > | Exception java.util.ConcurrentModificationException > | at HashMap.computeIfAbsent (HashMap.java:1229) > | at (#2:1) > > Somewhat similar happens in the reproducer: a computation of a tree path for > a package element eventually modifies the very map that the result of that > computation is to be put in. The map detects that and throws CME. > > Here are more details. The tree path is to be found for the `bar` package > element. The tree path is computed and put into the cache in one go > (HashMap.computeIfAbsent). Usually that works fine, but not this time. > Because the `bar` package resides in an implicitly referred module > (test.bar), that package is discovered late. The computation triggers the > `bar` package completion/parsing, which eventually calls back to > JavadocEnter.visitTopLevel, which in turn puts an alternatively computed tree > path for the `bar` package into that same cache. When the call stack returns > to computeIfAbsent, that cache modification is detected and CME is thrown. > > I can see three obvious ways of fixing this bug: > > 1. Replace computeIfAbsent with an equivalent combination of simpler > methods. > 2. Use a map whose computeIfAbsent can withstand non-exclusive > modifications. > 3. Restructure the code so it avoids such modifications altogether. > > (3) Is quite risky/complicated and not really needed at this time. (2) While > it's unlikely to have noticeable performance overhead, ConcurrentHashMap > feels like symptomatic treatment that is also out of place given that JavaDoc > is single-threaded. > > (1) seems the most reasonable. One potential problem with (1) though is that > future refactoring may reintroduce the issue again, but in a way that the > regression test won't catch.
Pavel Rappo has updated the pull request with a new target base due to a merge or a rebase. The incremental webrev excludes the unrelated changes brought in by the merge/rebase. The pull request contains three additional commits since the last revision: - Merge branch 'master' into 8304878 - Check exceptions explicitly - Initial commit ------------- Changes: - all: https://git.openjdk.org/jdk/pull/14310/files - new: https://git.openjdk.org/jdk/pull/14310/files/5fa14506..f2aaa0f1 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=14310&range=01 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=14310&range=00-01 Stats: 2975 lines in 96 files changed: 2422 ins; 151 del; 402 mod Patch: https://git.openjdk.org/jdk/pull/14310.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/14310/head:pull/14310 PR: https://git.openjdk.org/jdk/pull/14310