Skip to content

Fix ConcurrentModificationException on mass Ant project open.#7989

Merged
mbien merged 1 commit intoapache:masterfrom
mbien:fix-evaluator-cme
Dec 1, 2024
Merged

Fix ConcurrentModificationException on mass Ant project open.#7989
mbien merged 1 commit intoapache:masterfrom
mbien:fix-evaluator-cme

Conversation

@mbien
Copy link
Copy Markdown
Member

@mbien mbien commented Nov 25, 2024

Evaluators synchronize their inner workings on a per-instance basis. The static cache however is shared between instances and should use a sharable collection.

ConcurrentHashMap is probably the best choice here, since the potentially long running computeIfAbsent would only lock the item, not the map. (although in practice it probably doesn't have enough keys to leverage this feature)

Exception during NB startup while it is trying to open ~100 NB modules

Details
SEVERE [org.openide.util.RequestProcessor]: Error in RequestProcessor org.netbeans.modules.java.JavaNode$IconTask$SourceIcon
java.util.ConcurrentModificationException
	at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1230)
	at org.netbeans.modules.apisupport.project.Evaluator.getLimitModules(Evaluator.java:894)
	at org.netbeans.modules.apisupport.project.Evaluator.createEvaluator(Evaluator.java:460)
	at org.netbeans.modules.apisupport.project.Evaluator$2.run(Evaluator.java:236)
	at org.netbeans.modules.apisupport.project.Evaluator$2.run(Evaluator.java:223)
	at org.netbeans.modules.openide.util.DefaultMutexImplementation.readAccess(DefaultMutexImplementation.java:188)
	at org.openide.util.Mutex.readAccess(Mutex.java:199)
	at org.netbeans.modules.apisupport.project.Evaluator.reset(Evaluator.java:223)
	at org.netbeans.modules.apisupport.project.Evaluator$1.run(Evaluator.java:207)
	at org.netbeans.modules.apisupport.project.Evaluator$1.run(Evaluator.java:204)
	at org.netbeans.modules.openide.util.DefaultMutexImplementation.readAccess(DefaultMutexImplementation.java:188)
	at org.openide.util.Mutex.readAccess(Mutex.java:199)
	at org.netbeans.modules.apisupport.project.Evaluator.delegatingEvaluator(Evaluator.java:204)
	at org.netbeans.modules.apisupport.project.Evaluator.getProperty(Evaluator.java:157)
	at org.netbeans.spi.java.project.classpath.support.ProjectClassPathImplementation.getPath(ProjectClassPathImplementation.java:118)
	at org.netbeans.spi.java.project.classpath.support.ProjectClassPathImplementation.<init>(ProjectClassPathImplementation.java:70)
	at org.netbeans.spi.java.project.classpath.support.ProjectClassPathSupport.createPropertyBasedClassPathImplementation(ProjectClassPathSupport.java:51)
	at org.netbeans.modules.apisupport.project.queries.ClassPathProviderImpl.createPathFromProperty(ClassPathProviderImpl.java:420)
	at org.netbeans.modules.apisupport.project.queries.ClassPathProviderImpl.createCompileClasspath(ClassPathProviderImpl.java:431)
	at org.netbeans.modules.apisupport.project.queries.ClassPathProviderImpl$2.run(ClassPathProviderImpl.java:170)
	at org.netbeans.modules.apisupport.project.queries.ClassPathProviderImpl$2.run(ClassPathProviderImpl.java:166)
	at org.netbeans.modules.apisupport.project.queries.ClassPathProviderImpl$15.run(ClassPathProviderImpl.java:776)
	at org.netbeans.modules.openide.util.DefaultMutexImplementation.readAccess(DefaultMutexImplementation.java:188)
	at org.openide.util.Mutex.readAccess(Mutex.java:199)
	at org.netbeans.modules.apisupport.project.queries.ClassPathProviderImpl.runGuarded(ClassPathProviderImpl.java:772)
	at org.netbeans.modules.apisupport.project.queries.ClassPathProviderImpl.findClassPath(ClassPathProviderImpl.java:166)
	at org.netbeans.modules.java.project.ProjectClassPathProvider.findClassPath(ProjectClassPathProvider.java:50)
	at org.netbeans.api.java.classpath.ClassPath.getClassPath(ClassPath.java:667)
	at org.netbeans.api.java.source.ClasspathInfo.create(ClasspathInfo.java:416)
	at org.netbeans.api.java.source.ClasspathInfo.create(ClasspathInfo.java:287)
	at org.netbeans.modules.java.source.parsing.JavacParser.init(JavacParser.java:276)
	at org.netbeans.modules.java.source.parsing.JavacParser.parseImpl(JavacParser.java:422)
	at org.netbeans.modules.java.source.parsing.JavacParser.parse(JavacParser.java:361)
	at org.netbeans.modules.parsing.impl.TaskProcessor.callParse(TaskProcessor.java:598)
	at org.netbeans.modules.parsing.impl.SourceCache.getResult(SourceCache.java:230)
	at org.netbeans.modules.parsing.api.ResultIterator.getParserResult(ResultIterator.java:115)
	at org.netbeans.api.java.source.JavaSource$MultiTask.run(JavaSource.java:496)
	at org.netbeans.modules.parsing.impl.TaskProcessor.callUserTask(TaskProcessor.java:586)
	at org.netbeans.modules.parsing.api.ParserManager$UserTaskAction.run(ParserManager.java:197)
	at org.netbeans.modules.parsing.api.ParserManager$UserTaskAction.run(ParserManager.java:180)
	at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:181)
	at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:178)
	at org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager.priorityIO(FileChangedManager.java:153)
	at org.netbeans.modules.masterfs.providers.ProvidedExtensions.priorityIO(ProvidedExtensions.java:335)
	at org.netbeans.modules.parsing.nb.DataObjectEnvFactory.runPriorityIO(DataObjectEnvFactory.java:118)
	at org.netbeans.modules.parsing.impl.Utilities.runPriorityIO(Utilities.java:67)
	at org.netbeans.modules.parsing.impl.TaskProcessor.runUserTask(TaskProcessor.java:178)
	at org.netbeans.modules.parsing.api.ParserManager.parse(ParserManager.java:83)
	at org.netbeans.api.java.source.JavaSource.runUserActionTaskImpl(JavaSource.java:454)
	at org.netbeans.api.java.source.JavaSource.runUserActionTask(JavaSource.java:425)
	at org.netbeans.modules.java.JavaNode$IconTask$SourceIcon.computeIcon(JavaNode.java:636)
	at org.netbeans.modules.java.JavaNode$IconTask.run(JavaNode.java:598)
	at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1403)
	at org.netbeans.modules.openide.util.GlobalLookup.execute(GlobalLookup.java:45)
	at org.openide.util.lookup.Lookups.executeWith(Lookups.java:287)
[catch] at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2018)

Evaluators synchronize their inner workings on a per-instance basis.
The static cache however is shared between instances and should use a
sharable collection.

ConcurrentHashMap is probably the best choice here, since
the potentially long running computeIfAbsent would only lock the
item, not the map.
@mbien mbien added Java [ci] enable extra Java tests (java.completion, java.source.base, java.hints, refactoring.java, form) Ant [ci] enable "build tools" tests labels Nov 25, 2024
@mbien mbien added this to the NB25 milestone Nov 25, 2024
@mbien mbien requested a review from lahodaj November 25, 2024 23:31
Comment on lines +893 to 899
/**
* cache shared between Evaluator instances.
*/
private static final Map<String, String> limitModulesCache = new ConcurrentHashMap<>();

private static String getLimitModules(String javacRelease) {
return limitModulesCache.computeIfAbsent(javacRelease, release -> {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on second thought: this probably could be a plain old synchronized map? The keys are javac release versions, so it won't use the per-item lock feature of the CMH very often.

Copy link
Copy Markdown
Contributor

@matthiasblaesing matthiasblaesing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me this looks sane as is. If I'm not mistaken, the map should quickly become read-only (all values are cached). I would expect this to be more performant on a ConcurrentHashMap. But I think it does not mapper to much.

@mbien mbien merged commit c2ec6e4 into apache:master Dec 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ant [ci] enable "build tools" tests Java [ci] enable extra Java tests (java.completion, java.source.base, java.hints, refactoring.java, form)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants