Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean Michel-Lemieux2003-08-14 13:21:20 -0400
committerJean Michel-Lemieux2003-08-14 13:21:20 -0400
commit2b643929ca709b6b84cf641c41913b1e547645b9 (patch)
tree09be9852b6fd0f107c8d8101a1467238dc32db76
parentdbe7042f056d8f5a0d75e6486b1ed394593fddc8 (diff)
downloadeclipse.platform.team-2b643929ca709b6b84cf641c41913b1e547645b9.tar.gz
eclipse.platform.team-2b643929ca709b6b84cf641c41913b1e547645b9.tar.xz
eclipse.platform.team-2b643929ca709b6b84cf641c41913b1e547645b9.zip
Bug 41495: ConcurrentModificationException in Synch view (tree mode)
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SyncSet.java25
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SyncSetInputFromSubscriber.java13
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/AllTestsTeamSubscriber.java1
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/SyncSetTests.java109
4 files changed, 126 insertions, 22 deletions
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SyncSet.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SyncSet.java
index 9b5aea5e8..8debeb4a2 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SyncSet.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SyncSet.java
@@ -11,6 +11,7 @@
package org.eclipse.team.internal.ui.sync.sets;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -28,22 +29,22 @@ import org.eclipse.team.core.subscribers.SyncInfo;
import org.eclipse.team.internal.ui.TeamUIPlugin;
/**
- * This class keeps track of a set of resources that are associated with
- * a sychronization view/operation.
+ * This class keeps track of a set of resources and their associated synchronization
+ * information. It is optimized so that retrieving out-of-sync children is fast.
*/
public class SyncSet {
// fields used to hold resources of interest
// {IPath -> SyncInfo}
- protected Map resources = new HashMap();
+ protected Map resources = Collections.synchronizedMap(new HashMap());
// {IPath -> Set of deep out of sync child IResources}
// weird thing is that the child set will include the
// parent if the parent is out of sync
- protected Map parents = new HashMap();
+ protected Map parents = Collections.synchronizedMap(new HashMap());
// fields used for change notification
protected SyncSetChangedEvent changes;
- protected Set listeners = new HashSet();
+ protected Set listeners = Collections.synchronizedSet(new HashSet());
protected SyncInfoStatistics statistics = new SyncInfoStatistics();
@@ -85,7 +86,7 @@ public class SyncSet {
listeners.remove(listener);
}
- public void add(SyncInfo info) {
+ public synchronized void add(SyncInfo info) {
internalAddSyncInfo(info);
changes.added(info);
IResource local = info.getLocal();
@@ -100,7 +101,7 @@ public class SyncSet {
}
}
- protected void remove(IResource local) {
+ protected synchronized void remove(IResource local) {
IPath path = local.getFullPath();
SyncInfo info = (SyncInfo)resources.remove(path);
changes.removed(local);
@@ -108,7 +109,7 @@ public class SyncSet {
removeFromParents(local, local);
}
- protected void changed(SyncInfo info) {
+ protected synchronized void changed(SyncInfo info) {
internalAddSyncInfo(info);
changes.changed(info);
}
@@ -116,7 +117,7 @@ public class SyncSet {
/**
* Reset the sync set so it is empty
*/
- public void reset() {
+ public synchronized void reset() {
resources.clear();
parents.clear();
changes.reset();
@@ -270,14 +271,10 @@ public class SyncSet {
return (SyncInfo[]) resources.values().toArray(new SyncInfo[resources.size()]);
}
- protected void removeAllChildren(IResource resource) {
+ protected synchronized void removeAllChildren(IResource resource) {
// The parent map contains a set of all out-of-sync children
Set allChildren = (Set)parents.get(resource.getFullPath());
if (allChildren == null) return;
- removeAll(allChildren);
- }
-
- protected void removeAll(Set allChildren) {
IResource [] removed = (IResource[]) allChildren.toArray(new IResource[allChildren.size()]);
for (int i = 0; i < removed.length; i++) {
remove(removed[i]);
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SyncSetInputFromSubscriber.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SyncSetInputFromSubscriber.java
index 15c64f637..d1bab0727 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SyncSetInputFromSubscriber.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SyncSetInputFromSubscriber.java
@@ -10,14 +10,13 @@
*******************************************************************************/
package org.eclipse.team.internal.ui.sync.sets;
-import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.subscribers.TeamSubscriber;
/**
- * This class translates resource deltas and subscriber events into the effects
- * on a sync set
+ * Records resource synchronization changes from a Team subscriber. The actual changes
+ * are calculated via the SubscriberEventHandler and stored in this input.
*/
public class SyncSetInputFromSubscriber extends SyncSetInput {
@@ -34,14 +33,12 @@ public class SyncSetInputFromSubscriber extends SyncSetInput {
return subscriber;
}
- protected IResource[] getRoots() {
- return getSubscriber().roots();
- }
-
/* (non-Javadoc)
* @see org.eclipse.team.internal.ui.sync.views.SyncSetInput#fetchInput(org.eclipse.core.runtime.IProgressMonitor)
*/
protected void fetchInput(IProgressMonitor monitor) throws TeamException {
- // don't calculate changes unless
+ // don't calculate changes. The SubscriberEventHandler will fetch the
+ // input in a job and update this sync set when the changes are
+ // calculated.
}
}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/AllTestsTeamSubscriber.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/AllTestsTeamSubscriber.java
index 21d69c0fb..eaab5ee77 100644
--- a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/AllTestsTeamSubscriber.java
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/AllTestsTeamSubscriber.java
@@ -29,6 +29,7 @@ public class AllTestsTeamSubscriber extends EclipseTest {
TestSuite suite = new TestSuite();
suite.addTest(CVSMergeSubscriberTest.suite());
suite.addTest(CVSWorkspaceSubscriberTest.suite());
+ suite.addTest(SyncSetTests.suite());
CVSSyncSubscriberTest.setSyncSource(new SyncInfoSource());
return new CVSTestSetup(suite);
}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/SyncSetTests.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/SyncSetTests.java
new file mode 100644
index 000000000..dfcc4360d
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/SyncSetTests.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.tests.ccvs.core.subscriber;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.subscribers.SyncInfo;
+import org.eclipse.team.internal.ui.sync.sets.SyncSet;
+import org.eclipse.team.tests.ccvs.core.CVSTestSetup;
+
+
+public class SyncSetTests extends CVSSyncSubscriberTest {
+
+ public SyncSetTests() {
+ super();
+ }
+
+ public SyncSetTests(String name) {
+ super(name);
+ }
+
+ public static Test suite() {
+ String testName = System.getProperty("eclipse.cvs.testName");
+ if (testName == null) {
+ TestSuite suite = new TestSuite(SyncSetTests.class);
+ return new CVSTestSetup(suite);
+ } else {
+ return new CVSTestSetup(new SyncSetTests(testName));
+ }
+ }
+
+ class TestSyncInfo extends SyncInfo {
+ protected int calculateKind(IProgressMonitor progress) throws TeamException {
+ return 0;
+ }
+ public TestSyncInfo() throws TeamException {
+ super(ResourcesPlugin.getWorkspace().getRoot(), null, null, null, null);
+ }
+ }
+
+ /**
+ * Test that ensures that SyncSet can be modified concurrently. This is a quick test
+ * that doesn't validate the actual contents of the sync set.
+ */
+ public void testConcurrentAccessToSyncSet() throws Throwable {
+ final SyncSet set = new SyncSet();
+ final boolean[] done = {false};
+ final IStatus[] error = {null};
+
+ for(int numJobs = 0; numJobs < 10; numJobs++) {
+ Job job = new Job("SyncSetTests" + numJobs) {
+ public IStatus run(IProgressMonitor monitor) {
+ while(! done[0]) {
+ try {
+ set.add(new TestSyncInfo());
+ set.getOutOfSyncDescendants(ResourcesPlugin.getWorkspace().getRoot());
+ set.getSyncInfo(ResourcesPlugin.getWorkspace().getRoot());
+ set.allMembers();
+ } catch (Exception e) {
+ error[0] = new Status(IStatus.ERROR, "this", 1, "", e);
+ return error[0];
+ }
+ }
+ return Status.OK_STATUS;
+ }
+ };
+
+ job.addJobChangeListener(new JobChangeAdapter() {
+ public void done(IJobChangeEvent event) {
+ if(event.getResult() != Status.OK_STATUS) {
+ error[0] = event.getResult();
+ }
+ }
+ });
+
+ job.schedule();
+ }
+
+ for(int i = 0; i < 10000; i++) {
+ set.add(new TestSyncInfo());
+ set.getOutOfSyncDescendants(ResourcesPlugin.getWorkspace().getRoot());
+ set.getSyncInfo(ResourcesPlugin.getWorkspace().getRoot());
+ set.allMembers();
+ set.members(ResourcesPlugin.getWorkspace().getRoot());
+ set.reset();
+ }
+ done[0] = true;
+ if(error[0] != null) {
+ throw error[0].getException();
+ }
+ }
+}

Back to the top