Skip to content
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 on mass Ant project open. #7989

Merged
merged 1 commit into from
Dec 1, 2024

Conversation

mbien
Copy link
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

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
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
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
38 checks passed
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