On Mon, 5 Jun 2023 10:47:59 GMT, Pavel Rappo <pra...@openjdk.org> wrote:

> 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.

This pull request has now been integrated.

Changeset: 01455a07
Author:    Pavel Rappo <pra...@openjdk.org>
URL:       
https://git.openjdk.org/jdk/commit/01455a07a7e1f15aed43cd47222047810c826abd
Stats:     77 lines in 2 files changed: 76 ins; 1 del; 0 mod

8304878: ConcurrentModificationException in javadoc tool

Reviewed-by: jjg

-------------

PR: https://git.openjdk.org/jdk/pull/14310

Reply via email to