Bug 251370 - Concurrent Modification Exception in AliasManager.updateAliases due to refresh
Summary: Concurrent Modification Exception in AliasManager.updateAliases due to refresh
Status: CLOSED WONTFIX
Alias: None
Product: Platform
Classification: Eclipse Project
Component: Resources (show other bugs)
Version: 3.4   Edit
Hardware: PC Linux-GTK
: P3 major (vote)
Target Milestone: ---   Edit
Assignee: Platform-Resources-Inbox CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords:
: 251408 (view as bug list)
Depends on: 245412
Blocks: 265233
  Show dependency tree
 
Reported: 2008-10-20 06:46 EDT by James Blackburn CLA
Modified: 2019-11-27 07:25 EST (History)
3 users (show)

See Also:


Attachments
Patch to iterate over a clone of the set (1.11 KB, patch)
2008-10-20 08:01 EDT, James Blackburn CLA
no flags Details | Diff
Patch for LinkedResourceTest attached (5.77 KB, patch)
2008-10-20 09:33 EDT, James Blackburn CLA
no flags Details | Diff
Test 2 (5.40 KB, patch)
2008-12-05 05:24 EST, James Blackburn CLA
no flags Details | Diff
Patch 2 for this & bug 251408 (2.07 KB, patch)
2009-02-17 16:58 EST, James Blackburn CLA
no flags Details | Diff
Test 3 (this & bug251408 (9.57 KB, patch)
2009-02-17 17:03 EST, James Blackburn CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description James Blackburn CLA 2008-10-20 06:46:35 EDT
Build ID: 3.4..0

Steps To Reproduce:
As described in Bug229633 we see the following concurrent modification exception when we have one project nested in another (with linked resources between them) and the inner project is opened after the .project file has been modified on disk. 
[ This occurs after a CVS update in the outer project, for example.]

Exception attached.  As you can see the refresh method call is modifying the aliases set while it's being iterated over in updateAliases.

Test workspace attached to bug229633. Reproduction steps in bug229633 comment 28

More information:
AliasManager$AddToCollectionDoit.doit(IResource) line: 56       <-- modified
AliasManager$LocationMap.overLappingResourcesDo(AliasManager$Doit) line: 237    
AliasManager.buildAliasedProjectsSet() line: 380        
AliasManager.updateStructureChanges() line: 711 
AliasManager.hasNoAliases(IResource) line: 578  
AliasManager.computeAliases(IResource, IFileStore) line: 425    
RefreshLocalAliasVisitor.createResource(UnifiedTreeNode, Resource) line: 34     
RefreshLocalAliasVisitor(RefreshLocalVisitor).synchronizeExistence(UnifiedTreeNode,
Resource) line: 216 
RefreshLocalAliasVisitor(RefreshLocalVisitor).visit(UnifiedTreeNode) line: 290  
UnifiedTree.accept(IUnifiedTreeVisitor, int) line: 99   
FileSystemResourceManager.refreshResource(IResource, int, boolean,
IProgressMonitor) line: 788  
FileSystemResourceManager.refresh(IResource, int, boolean, IProgressMonitor)
line: 772  
Folder(Resource).refreshLocal(int, IProgressMonitor) line: 1594 
Folder(Resource).createLink(URI, int, IProgressMonitor) line: 695       
Project.reconcileLinksAndGroups(ProjectDescription) line: 1035  
Project.updateDescription() line: 1187  
File.updateMetadataFiles() line: 411    
RefreshLocalVisitor.visit(UnifiedTreeNode) line: 306    
UnifiedTree.accept(IUnifiedTreeVisitor, int) line: 99   
FileSystemResourceManager.refreshResource(IResource, int, boolean,
IProgressMonitor) line: 788  
FileSystemResourceManager.refresh(IResource, int, boolean, IProgressMonitor)
line: 772  
AliasManager.updateAliases(IResource, IFileStore, int, IProgressMonitor) line:
682      <-- Iteration over aliases
Project.open(int, IProgressMonitor) line: 906   
Project.open(IProgressMonitor) line: 922        
OpenResourceAction.invokeOperation(IResource, IProgressMonitor) line: 153       
OpenResourceAction(WorkspaceAction).execute(List, IProgressMonitor) line: 162   
WorkspaceAction$2.runInWorkspace(IProgressMonitor) line: 483    
WorkspaceAction$2(InternalWorkspaceJob).run(IProgressMonitor) line: 38  
Worker.run() line: 55
Comment 1 James Blackburn CLA 2008-10-20 07:51:37 EDT
(In reply to comment #34)
>  I see what your saying, something strange though is that the variable change
> in the following code:

Whoops. That was clearly an incorrect backtrace.

How about this one:

Thread [Worker-12] (Suspended (entry into method clear in HashMap))	
	owns: HashSet<E>  (id=4024)	
	HashMap<K,V>.clear() line: 656 [local variables unavailable]	
	HashSet<E>.clear() line: 211 [local variables unavailable]	
	AliasManager.computeAliases(IResource, IFileStore) line: 429	
	RefreshLocalAliasVisitor.createResource(UnifiedTreeNode, Resource) line: 34	
	RefreshLocalAliasVisitor(RefreshLocalVisitor).synchronizeExistence(UnifiedTreeNode, Resource) line: 216	
	RefreshLocalAliasVisitor(RefreshLocalVisitor).visit(UnifiedTreeNode) line: 290	
	UnifiedTree.accept(IUnifiedTreeVisitor, int) line: 99	
	FileSystemResourceManager.refreshResource(IResource, int, boolean, IProgressMonitor) line: 788	
	FileSystemResourceManager.refresh(IResource, int, boolean, IProgressMonitor) line: 772	
	Folder(Resource).refreshLocal(int, IProgressMonitor) line: 1594	
	Folder(Resource).createLink(URI, int, IProgressMonitor) line: 695	
	Project.reconcileLinksAndGroups(ProjectDescription) line: 1035	
	Project.updateDescription() line: 1187	
	File.updateMetadataFiles() line: 411	
	RefreshLocalVisitor.visit(UnifiedTreeNode) line: 306	
	UnifiedTree.accept(IUnifiedTreeVisitor, int) line: 99	
	FileSystemResourceManager.refreshResource(IResource, int, boolean, IProgressMonitor) line: 788	
	FileSystemResourceManager.refresh(IResource, int, boolean, IProgressMonitor) line: 772	
	AliasManager.updateAliases(IResource, IFileStore, int, IProgressMonitor) line: 682	
	Project.open(int, IProgressMonitor) line: 906	
	Project.open(IProgressMonitor) line: 922	
Comment 2 Szymon Brandys CLA 2008-10-20 07:54:41 EDT
James or Serge, could you attach the test case for the issue? Are you going to investigate and fix it?
Comment 3 James Blackburn CLA 2008-10-20 08:01:47 EDT
Created attachment 115550 [details]
Patch to iterate over a clone of the set

As per Serge's comments in bug 229633 this patch iterates over a clone of the map.  Doesn't add any synchronization as it's not (to me) clear that this is needed.
Comment 4 James Blackburn CLA 2008-10-20 08:06:10 EDT
(In reply to comment #2)
> James or Serge, could you attach the test case for the issue? Are you going to
> investigate and fix it?

Hi Szymon, I'll attempt to attach a test as well.

Comment 5 James Blackburn CLA 2008-10-20 09:33:20 EDT
Created attachment 115557 [details]
Patch for LinkedResourceTest attached

(In reply to comment #2)
> James or Serge, could you attach the test case for the issue? Are you going to
> investigate and fix it?

Attached is a patch to the LinkedResourceTest which attempts to automate performing the tests listed in the other bug.

I basically create two .project files with slightly different linked resource definitions, replacing the .project file of the inner project with each in turn.  I do this in a loop 0 -> 100 and on linux the test fails sometimes between 0 & ~20... I've tried to make it fail first time, but have so far failed -- it occurs every time when I perform the steps manually...  The supplied patch makes the test pass each time.

I can't make the test fail on Windows but then that receives resource deltas from the filesystem, doesn't it?


Also as serge described previously there are exceptions in the test cleanup when performing these linked resource tests with nested projects:

org.eclipse.core.internal.resources.ResourceException: Problems encountered while deleting resources.
	at org.eclipse.core.internal.localstore.FileSystemResourceManager.delete(FileSystemResourceManager.java:230)
	at org.eclipse.core.internal.resources.ResourceTree.internalDeleteFolder(ResourceTree.java:350)
	at org.eclipse.core.internal.resources.ResourceTree.internalDeleteProject(ResourceTree.java:385)
	at org.eclipse.core.internal.resources.ResourceTree.standardDeleteProject(ResourceTree.java:835)
	at org.eclipse.core.internal.resources.Resource.unprotectedDelete(Resource.java:1846)
	at org.eclipse.core.internal.resources.Resource.delete(Resource.java:782)
	at org.eclipse.core.tests.resources.ResourceTest$1.run(ResourceTest.java:379)
	at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:1934)
	at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:1916)
	at org.eclipse.core.tests.resources.ResourceTest.cleanup(ResourceTest.java:377)
	at org.eclipse.core.tests.resources.ResourceTest.tearDown(ResourceTest.java:908)
	at org.eclipse.core.tests.resources.LinkedResourceTest.tearDown(LinkedResourceTest.java:133)
	at junit.framework.TestCase.runBare(TestCase.java:136)
	at junit.framework.TestResult$1.protect(TestResult.java:106)
	at junit.framework.TestResult.runProtected(TestResult.java:124)
	at junit.framework.TestResult.run(TestResult.java:109)

But I think this is probably a problem with the testsuite rather than a problem with the test itself...
Comment 6 James Blackburn CLA 2008-10-20 11:33:26 EDT
I've just found another problem with AliasManager bug251408.  Basically if you override and update inner-project's .project file from outer project while inner-project is open (using CVS) you get an illegal rule push.  

This is because CVS grabs the scheduling rule for the project in which the update is happening, and AliasManager.updateAliases(...) when it performs localManager.refresh(...) may attempt to update linked resources in the nested project.  I've attached a patch to bug 251408 to run project level / .project updates in a separate WorkspaceJob.  

Running project level refreshes in a separate job probably negates the need to do the clone() as provided by this patch...
Comment 7 Martin Oberhuber CLA 2008-10-30 15:42:38 EDT
I think that this should be fixed at the root cause - by disallowing project overlap as suggested by bug 245412 comment 4 (masking out the folder of the physically nested project from its container).
Comment 8 James Blackburn CLA 2008-12-05 05:24:21 EST
Created attachment 119602 [details]
Test 2

(In reply to comment #5)
> I do this in a loop 0 -> 100 and on linux the test fails sometimes
> between 0 & ~20... I've tried to make it fail first time, but have so far
> failed -- it occurs every time when I perform the steps manually...  The
> supplied patch makes the test pass each time.

So it looks like on Linux File.lastModified() uses the stat system call and thus only has second granularity despite the API stating: "A long value representing the time the file was last modified, measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970), or 0L if the file does not exist or if an I/O error occurs."

Updated the test to ensure that the modified time has changed when overwriting the .project file externally.  This removes the need for a loop.
Comment 9 Szymon Brandys CLA 2008-12-05 09:20:52 EST
James, thanks for the test. Any volunteers to work on it?
Comment 10 John Arthorne CLA 2008-12-05 09:43:16 EST
Some file systems have up to two second granularity. We have a helper method ResourceTest.ensureOutOfSync for when we need to make a file out of sync during a test.
Comment 11 James Blackburn CLA 2008-12-05 10:50:09 EST
(In reply to comment #9)
> James, thanks for the test. Any volunteers to work on it?

There are a couple of patches which fix this.  The first patch here does that, though a better patch is the one on bug251408.

bug251408 -- alias manager should refresh project description file resource as a separate job as, in the case of overlaping projects, updating linked resources may violate already held scheduling rules (for example if the .project file of an inner project is cvs updated in the outer project).

Another related bug it would be good to have fixed / have the patches reviewed:
bug246221 -- patches (test and fix) for alias manager not noticing outer project closed
Comment 12 James Blackburn CLA 2009-02-17 16:58:37 EST
Created attachment 125950 [details]
Patch 2 for this & bug 251408

Instead of clone() the set I schedule IProject or project description changes with the refreshmanager.  This is also a simple fix for bug251408.
Comment 13 James Blackburn CLA 2009-02-17 17:03:13 EST
Created attachment 125951 [details]
Test 3 (this & bug251408

Test 3.
This is a test for this bug and the incorrect nested scheduling rule bug 251408.

NB the test bug 251408 currently has a sleep in it. Without the sleep, the test passes. This makes me think that if you do setContents(...) within a few milliseconds of the last resource change, the resource's alias's don't receive resource change notifications...

(In reply to comment #10)
> Some file systems have up to two second granularity. We have a helper method
> ResourceTest.ensureOutOfSync for when we need to make a file out of sync during
> a test.

Thanks, I had a look at this, unfortunately it also modifies the file you want changed.  In these two edge cases, it's the project description that's being written and aliased in two projects.
Comment 14 James Blackburn CLA 2009-02-17 17:06:10 EST
*** Bug 251408 has been marked as a duplicate of this bug. ***
Comment 15 James Blackburn CLA 2009-02-17 19:23:40 EST
(In reply to comment #13)
> NB the test bug 251408 currently has a sleep in it. 
I've filed a separate bug + test for this, bug 265233.


I've run the full core.tests.resources testsuite. All the tests that passed before on Mac OS X continue to pass after the small change made in this patch.

Szymon / John, can you review?

Comment 16 Szymon Brandys CLA 2009-03-03 06:41:59 EST
testChangingLinksWithNestedProjects() passes without the patch too. I'm on Win XP. Is the issue Linux specific?
Comment 17 James Blackburn CLA 2009-03-03 06:52:52 EST
(In reply to comment #16)
> testChangingLinksWithNestedProjects() passes without the patch too. I'm on Win
> XP. Is the issue Linux specific?

Only so much as the issues are timing related. The resource needs to be out of sync when you open the project.  Do you have refresh automatically on / is the windows refresh provider enabled?
Comment 18 James Blackburn CLA 2009-03-03 07:02:46 EST
BTW a quick note on my chosen fix:

I always schedule refresh of a project level / project description resource by the refresh manager.  This prevents both the concurrent modification exception, and the invalid scheduling rule error.

An alternative fix would be to address the two issues separately, i.e. 
 - clone() the hashset, as serge has done in e4, to prevent the first issue
 - check the current thread's scheduling rule (if it has one) contains the resource in the second instance, if not, schedule a separate refresh.

The fix I chose fixes both issues by delegating away to refreshManager...

Comment 19 Szymon Brandys CLA 2009-03-03 07:35:03 EST
(In reply to comment #17) 
> Only so much as the issues are timing related. The resource needs to be out of
> sync when you open the project.  Do you have refresh automatically on / is the
> windows refresh provider enabled?

It is green because of the fix for bug 265810.
Comment 20 James Blackburn CLA 2009-03-03 07:50:19 EST
(In reply to comment #19)
> It is green because of the fix for bug 265810.

That'll teach me for hypothesizing...  Surely this is odd because the two links have different Raw Locations, I would have thought that the link would therefore be recreated during the reconcileLinks()...
Comment 21 James Blackburn CLA 2009-03-05 06:16:55 EST
I don't think I understand how/why the AliasManager is allowed to refresh, update the sync state on aliased resources when the caller may not hold a scheduling rule which contains all the aliased resources...
Comment 22 James Blackburn CLA 2009-03-05 12:34:39 EST
(In reply to comment #21)
> I don't think I understand how/why the AliasManager is allowed to refresh,
> update the sync state on aliased resources when the caller may not hold a
> scheduling rule which contains all the aliased resources...

John or Szymon, can you comment on this? How is the alias manager allowed update aliased IResources irrespective of currently held scheduling rules?  Should the alias manager be allowed to create/remove/update all aliases irrespective of the current scheduling rule (such as when a .project has been modified)?
Comment 23 Szymon Brandys CLA 2009-05-14 17:13:47 EDT
Moving the target milestone to 3.6.
Comment 24 Szymon Brandys CLA 2010-05-25 11:22:59 EDT
Moving to 3.7 for further investigation.
Comment 25 John Arthorne CLA 2010-10-06 14:39:23 EDT
(In reply to comment #22)
> John or Szymon, can you comment on this? How is the alias manager allowed
> update aliased IResources irrespective of currently held scheduling rules? 
> Should the alias manager be allowed to create/remove/update all aliases
> irrespective of the current scheduling rule (such as when a .project has been
> modified)?

The AliasManager performs a refresh without using any rules. This is technically "illegal", but the rationale is that the resources on disk have already changed and all we are doing is aligning the workspace tree state in memory with reality. See RefreshLocalAliasVisitor for how this is done.
Comment 26 Szymon Brandys CLA 2012-04-25 11:25:21 EDT
We most likely will not address these bugs during this dev cycle.
Comment 27 Lars Vogel CLA 2019-11-27 07:25:27 EST
This bug hasn't had any activity in quite some time. Maybe the problem got
resolved, was a duplicate of something else, or became less pressing for some
reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it.
The information can be, for example, that the problem still occurs, that you
still want the feature, that more information is needed, or that the bug is
(for whatever reason) no longer relevant.

If the bug is still relevant, please remove the stalebug whiteboard tag.