Community
Participate
Working Groups
Build Identifier: 3.6.1 M20100909-0800 org.eclipse.dltk.core.mixin.MixinModel is only partially synchronized. We have multiple threads attempting to use the index and they get into strife calling get(String) on MixinModel. The reason is simple: [code] public IMixinElement get(String key) { if (DLTKCore.VERBOSE) { System.out.println("MixinModel.get(" + key + ')'); //$NON-NLS-1$ } if (notExistKeysCache.contains(key)) { return null; } if (removes == 0) { if (cache.get(key) == null) { return null; } } MixinElement element = getCreateEmpty(key); if (DLTKCore.VERBOSE) { System.out.println("Filling ratio:" + this.cache.fillingRatio()); //$NON-NLS-1$ this.cache.printStats(); } buildElementTree(element); if (element.isFinal() && element.sourceModules.size() > 0) { existKeysCache.add(key); return element; } notExistKeysCache.add(key); synchronized (this.cache) { cache.remove(element.key); cache.resetSpaceLimit(CACHE_LIMIT, element); } return null; } [/code] The class members existKeysCache and notExistKeysCache are not synchronized. Multiple threads can access them. The calls to add(key) on the HashMap result in two threads trying to modify the map simultaneously. Specifically a rehash(). This seems to cause the HashMap to either blow up or infinitely loop inside AnalyzeMap(). The solution would be to synchronize these other members as well as the cache. - Carl. Thread 1 stack: Thread [Worker-6] (Suspended) HashMap<K,V>.rehash(int) line: not available HashMap<K,V>.rehash() line: not available HashMap<K,V>.putImpl(K, V) line: not available HashMap<K,V>.put(K, V) line: not available HashSet<E>.add(E) line: not available MixinModel.get(String) line: 147 UMixinModelUtils.safeGet(MixinModel, String, String) line: 34 MethodNameAndArgsMatchSearchPredicate(AbstractNameMatchSearchPredicate).search(ISourceModule) line: 180 UValidationEngine.validate(ISourceModule) line: 106 UValidationEngine.processIncludes(List<ISourceModule>, List<ISourceModule>, List<String>) line: 119 UValidationEngine.fullyValidate(ISourceModule) line: 89 USourceElementValidator.endvisit(ModuleDeclaration) line: 214 UModuleDeclaration(ModuleDeclaration).traverse(ASTVisitor) line: 72 UValidationBuildParticipant$UBuildParticipant.build(IBuildContext) line: 45 StandardScriptBuilder.buildModule(IBuildContext) line: 219 StandardScriptBuilder.buildNatureModules(IScriptProject, int, List<ISourceModule>, IProgressMonitor) line: 167 StandardScriptBuilder.buildModules(IScriptProject, List<ISourceModule>, int, IProgressMonitor) line: 136 StandardScriptBuilder.buildModelElements(IScriptProject, List<ISourceModule>, IProgressMonitor, int) line: 52 ScriptBuilder.buildElements(ScriptBuilder$BuilderState[], IProgressMonitor, int) line: 858 ScriptBuilder.incrementalBuild(IResourceDelta, IProgressMonitor) line: 697 ScriptBuilder.build(int, Map, IProgressMonitor) line: 274 BuildManager$2.run() line: 629 SafeRunner.run(ISafeRunnable) line: 42 BuildManager.basicBuild(int, IncrementalProjectBuilder, Map, MultiStatus, IProgressMonitor) line: 172 BuildManager.basicBuild(IProject, int, ICommand[], MultiStatus, IProgressMonitor) line: 203 BuildManager$1.run() line: 255 SafeRunner.run(ISafeRunnable) line: 42 BuildManager.basicBuild(IProject, int, MultiStatus, IProgressMonitor) line: 258 BuildManager.basicBuildLoop(IProject[], IProject[], int, MultiStatus, IProgressMonitor) line: 311 BuildManager.build(int, IProgressMonitor) line: 343 AutoBuildJob.doBuild(IProgressMonitor) line: 144 AutoBuildJob.run(IProgressMonitor) line: 242 Worker.run() line: 54 Thread 2 stack: Daemon Thread [org.eclipse.dltk.internal.ui.text.ScriptReconciler] (Suspended) HashMap<K,V>.analyzeMap() line: not available HashMap<K,V>.rehash(int) line: not available HashMap<K,V>.rehash() line: not available HashMap<K,V>.putImpl(K, V) line: not available HashMap<K,V>.put(K, V) line: not available HashSet<E>.add(E) line: not available MixinModel.get(String) line: 147 UMixinModelUtils.safeGet(MixinModel, String, String) line: 34 MethodNameAndArgsMatchSearchPredicate(AbstractNameMatchSearchPredicate).search(ISourceModule) line: 180 UValidationEngine.validate(ISourceModule) line: 106 UValidationEngine.processIncludes(List<ISourceModule>, List<ISourceModule>, List<String>) line: 119 UValidationEngine.fullyValidate(ISourceModule) line: 89 USourceElementValidator.endvisit(ModuleDeclaration) line: 214 UModuleDeclaration(ModuleDeclaration).traverse(ASTVisitor) line: 72 UValidationBuildParticipant$UBuildParticipant.build(IBuildContext) line: 45 StructureBuilder.build(String, ISourceModule, AccumulatingProblemReporter) line: 73 ReconcileWorkingCopyOperation.makeConsistent(SourceModule, IProblemRequestor) line: 90 ReconcileWorkingCopyOperation.executeOperation() line: 58 ReconcileWorkingCopyOperation(ModelOperation).run(IProgressMonitor) line: 698 ReconcileWorkingCopyOperation(ModelOperation).runOperation(IProgressMonitor) line: 764 SourceModule.reconcile(boolean, WorkingCopyOwner, IProgressMonitor) line: 309 ScriptReconcilingStrategy.reconcile(ISourceModule, boolean) line: 164 ScriptReconcilingStrategy.access$0(ScriptReconcilingStrategy, ISourceModule, boolean) line: 153 ScriptReconcilingStrategy$1.run() line: 125 SafeRunner.run(ISafeRunnable) line: 42 ScriptReconcilingStrategy.reconcile(boolean) line: 120 ScriptReconcilingStrategy.reconcile(IRegion) line: 192 ScriptCompositeReconcilingStrategy(CompositeReconcilingStrategy).reconcile(IRegion) line: 97 ScriptCompositeReconcilingStrategy.reconcile(IRegion) line: 74 ScriptReconciler(MonoReconciler).process(DirtyRegion) line: 77 AbstractReconciler$BackgroundThread.run() line: 206 Reproducible: Sometimes Steps to Reproduce: 1. In more than one thread, call MixinModel.get(String). 2. If the timing is just right, they both attempt to modify existKeysCache or notExistKeysCache simultaneously. 3. If the two attemp a rehash of the map, neither thread completes.
After some research I noticed that cache and requestCache are not synchronized either. Maybe it's worth to make synchronization strategy as simple as possible. As far as I understand, the only long-running operation called from MixinModel is SearchEngine.searchMixinSources, everything else can use one lock. (Because simple things work, and have less overhead)