Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean Michel-Lemieux2002-04-16 19:20:23 +0000
committerJean Michel-Lemieux2002-04-16 19:20:23 +0000
commit97d9531ceff9158b3c56cada0eadf72e0060d755 (patch)
treea92be020e270e668d6f1cf55e649b45a52bd8931
parent9e3387ffaa0cb4db1e844821b0c91e8c508bac72 (diff)
downloadeclipse.platform.team-97d9531ceff9158b3c56cada0eadf72e0060d755.tar.gz
eclipse.platform.team-97d9531ceff9158b3c56cada0eadf72e0060d755.tar.xz
eclipse.platform.team-97d9531ceff9158b3c56cada0eadf72e0060d755.zip
Bug 13105 : EclipseSynchronizer not thread safe
-rw-r--r--bundles/org.eclipse.team.cvs.core/.options6
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Policy.java2
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java98
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ReentrantLock.java71
4 files changed, 140 insertions, 37 deletions
diff --git a/bundles/org.eclipse.team.cvs.core/.options b/bundles/org.eclipse.team.cvs.core/.options
index fe4f0424e..7d0f28730 100644
--- a/bundles/org.eclipse.team.cvs.core/.options
+++ b/bundles/org.eclipse.team.cvs.core/.options
@@ -4,7 +4,9 @@
org.eclipse.team.cvs.core/debug=true
# Shows when meta-files are modified by a 3rd party
-org.eclipse.team.cvs.core/metafiles=true
+org.eclipse.team.cvs.core/metafiles=false
# Shows cvs client/server protocol
-org.eclipse.team.cvs.core/cvsprotocol=true # Shows stream debugging information org.eclipse.team.cvs.core/streams=true \ No newline at end of file
+org.eclipse.team.cvs.core/cvsprotocol=true # Shows stream debugging information org.eclipse.team.cvs.core/streams=false
+# Shows stream debugging information
+org.eclipse.team.cvs.core/threading=false \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Policy.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Policy.java
index 512b681b6..1f667a4ed 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Policy.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Policy.java
@@ -23,6 +23,7 @@ public class Policy {
public static boolean DEBUG_METAFILE_CHANGES = false;
public static boolean DEBUG_CVS_PROTOCOL = false;
public static boolean DEBUG_STREAMS = false;
+ public static boolean DEBUG_THREADING = false;
static {
//init debug options
@@ -30,6 +31,7 @@ public class Policy {
DEBUG_METAFILE_CHANGES = "true".equalsIgnoreCase(Platform.getDebugOption(CVSProviderPlugin.ID + "/metafiles"));//$NON-NLS-1$ //$NON-NLS-2$
DEBUG_CVS_PROTOCOL = "true".equalsIgnoreCase(Platform.getDebugOption(CVSProviderPlugin.ID + "/cvsprotocol"));//$NON-NLS-1$ //$NON-NLS-2$
DEBUG_STREAMS = "true".equalsIgnoreCase(Platform.getDebugOption(CVSProviderPlugin.ID + "/streams"));//$NON-NLS-1$ //$NON-NLS-2$
+ DEBUG_THREADING = "true".equalsIgnoreCase(Platform.getDebugOption(CVSProviderPlugin.ID + "/threading"));//$NON-NLS-1$ //$NON-NLS-2$
}
}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java
index f1a34d36f..ef43f23f8 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java
@@ -29,6 +29,7 @@ import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
import org.eclipse.team.internal.ccvs.core.CVSStatus;
import org.eclipse.team.internal.ccvs.core.Policy;
import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
+import org.eclipse.team.internal.ccvs.core.syncinfo.ReentrantLock;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
import org.eclipse.team.internal.ccvs.core.util.Assert;
import org.eclipse.team.internal.ccvs.core.util.SyncFileWriter;
@@ -56,7 +57,8 @@ public class EclipseSynchronizer {
private static EclipseSynchronizer instance;
// track resources that have changed in a given operation
- private int nestingCount = 0;
+ private ReentrantLock lock = new ReentrantLock();
+
private Set changedResources = new HashSet();
private Set changedFolders = new HashSet();
@@ -225,7 +227,12 @@ public class EclipseSynchronizer {
*/
public String[] getIgnored(IContainer folder) throws CVSException {
if (folder.getType() == IResource.ROOT || ! folder.exists()) return NULL_IGNORES;
- return cacheFolderIgnores(folder);
+ try {
+ beginOperation(null);
+ return cacheFolderIgnores(folder);
+ } finally {
+ endOperation(null);
+ }
}
/**
@@ -239,21 +246,26 @@ public class EclipseSynchronizer {
throw new CVSException(IStatus.ERROR, CVSException.UNABLE,
Policy.bind("EclipseSynchronizer.ErrorSettingIgnorePattern", folder.getFullPath().toString())); //$NON-NLS-1$
}
- String[] ignores = cacheFolderIgnores(folder);
- if (ignores != null) {
- String[] oldIgnores = ignores;
- ignores = new String[oldIgnores.length + 1];
- System.arraycopy(oldIgnores, 0, ignores, 0, oldIgnores.length);
- ignores[oldIgnores.length] = pattern;
- } else {
- ignores = new String[] { pattern };
+ try {
+ beginOperation(null);
+ String[] ignores = cacheFolderIgnores(folder);
+ if (ignores != null) {
+ String[] oldIgnores = ignores;
+ ignores = new String[oldIgnores.length + 1];
+ System.arraycopy(oldIgnores, 0, ignores, 0, oldIgnores.length);
+ ignores[oldIgnores.length] = pattern;
+ } else {
+ ignores = new String[] { pattern };
+ }
+ setCachedFolderIgnores(folder, ignores);
+ SyncFileWriter.addCVSIgnoreEntries(folder, ignores);
+ // broadcast changes to unmanaged children - they are the only candidates for being ignored
+ List possibleIgnores = new ArrayList();
+ accumulateNonManagedChildren(folder, possibleIgnores);
+ CVSProviderPlugin.broadcastResourceStateChanges((IResource[])possibleIgnores.toArray(new IResource[possibleIgnores.size()]));
+ } finally {
+ endOperation(null);
}
- setCachedFolderIgnores(folder, ignores);
- SyncFileWriter.addCVSIgnoreEntries(folder, ignores);
- // broadcast changes to unmanaged children - they are the only candidates for being ignored
- List possibleIgnores = new ArrayList();
- accumulateNonManagedChildren(folder, possibleIgnores);
- CVSProviderPlugin.broadcastResourceStateChanges((IResource[])possibleIgnores.toArray(new IResource[possibleIgnores.size()]));
}
/**
@@ -265,7 +277,8 @@ public class EclipseSynchronizer {
*/
public IResource[] members(IContainer folder) throws CVSException {
if (! folder.exists()) return new IResource[0];
- try {
+ try {
+ beginOperation(null);
if (folder.getType() == IResource.ROOT) return folder.members();
cacheResourceSyncForChildren(folder);
Collection infos = getCachedResourceSyncForChildren(folder);
@@ -284,6 +297,8 @@ public class EclipseSynchronizer {
return (IResource[])childResources.toArray(new IResource[childResources.size()]);
} catch (CoreException e) {
throw CVSException.wrapException(e);
+ } finally {
+ endOperation(null);
}
}
@@ -293,8 +308,9 @@ public class EclipseSynchronizer {
* @param monitor the progress monitor, may be null
*/
public void beginOperation(IProgressMonitor monitor) throws CVSException {
- nestingCount += 1;
- if (nestingCount == 1) {
+ lock.acquire();
+
+ if (lock.getNestingCount() == 1) {
prepareCache(monitor);
}
}
@@ -314,15 +330,14 @@ public class EclipseSynchronizer {
public void endOperation(IProgressMonitor monitor) throws CVSException {
try {
IStatus status = STATUS_OK;
- if (nestingCount == 1) {
+ if (lock.getNestingCount() == 1) {
status = commitCache(monitor);
}
if (status != STATUS_OK) {
throw new CVSException(status);
}
} finally {
- nestingCount -= 1;
- Assert.isTrue(nestingCount>= 0);
+ lock.release();
}
}
@@ -345,23 +360,36 @@ public class EclipseSynchronizer {
* @param deep purge sync from child folders
* @param monitor the progress monitor, may be null
*/
- public void flush(IContainer root, boolean purgeCache, boolean deep, IProgressMonitor monitor)
- throws CVSException {
+ public void flush(IContainer root, boolean purgeCache, boolean deep, IProgressMonitor monitor) throws CVSException {
// flush unwritten sync info to disk
- IStatus status = STATUS_OK;
- if (nestingCount != 0) status = commitCache(monitor);
-
- // purge from memory too if we were asked to
- if (purgeCache) purgeCache(root, deep);
-
- // prepare for the operation again if we cut the last one short
- if (nestingCount != 0) prepareCache(monitor);
-
- if (status != STATUS_OK) {
- throw new CVSException(status);
+ monitor = Policy.monitorFor(monitor);
+ monitor.beginTask(null, 10);
+ try {
+ beginOperation(Policy.subMonitorFor(monitor, 1));
+
+ IStatus status = commitCache(Policy.subMonitorFor(monitor, 7));
+
+ // purge from memory too if we were asked to
+ if (purgeCache) purgeCache(root, deep);
+
+ // prepare for the operation again if we cut the last one short
+ prepareCache(Policy.subMonitorFor(monitor, 1));
+
+ if (status != STATUS_OK) {
+ throw new CVSException(status);
+ }
+ } finally {
+ endOperation(Policy.subMonitorFor(monitor, 1));
+ monitor.done();
}
}
+ /**
+ * Called to notify the synchronizer that meta files have changed on disk, outside
+ * of the workbench. The cache will be flushed for this folder and it's immediate
+ * children and appropriate state change events are broadcasts to state change
+ * listeners.
+ */
public void syncFilesChanged(IContainer[] roots) throws CVSException {
try {
for (int i = 0; i < roots.length; i++) {
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ReentrantLock.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ReentrantLock.java
new file mode 100644
index 000000000..dae4bd4e2
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ReentrantLock.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2002 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ *
+ * Contributors:
+ * IBM - Initial implementation
+ ******************************************************************************/
+package org.eclipse.team.internal.ccvs.core.syncinfo;
+
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+
+/**
+ * Provides a per-thread nested locking mechanism. A thread can acquire a
+ * lock and then call acquire() multiple times. Other threads that try
+ * and acquire the lock will be blocked until the first thread releases all
+ * it's nested locks.
+ */
+public class ReentrantLock {
+
+ private final static boolean DEBUG = Policy.DEBUG_THREADING;
+ private Thread thread;
+ private int nestingCount;
+
+ public ReentrantLock() {
+ this.thread = null;
+ this.nestingCount = 0;
+ }
+
+ public synchronized void acquire() {
+ // stop early if we've been interrupted -- don't enter the lock anew
+ Thread thisThread = Thread.currentThread();
+
+ // race for access to the lock -- does not guarantee fairness
+ if (thread != thisThread) {
+ while (nestingCount != 0) {
+ try {
+ if(DEBUG) System.out.println("["+ thisThread.getName() + "] waiting for CVS synchronizer lock");
+ wait();
+ } catch(InterruptedException e) {
+ // keep waiting for the lock
+ if(DEBUG) System.out.println("["+ thisThread.getName() + "] interrupted in CVS synchronizer lock");
+ }
+ }
+ thread = thisThread;
+ if(DEBUG) System.out.println("[" + thisThread.getName() + "] acquired CVS synchronizer lock");
+ }
+ nestingCount++;
+ }
+
+ public synchronized void release() {
+ Thread thisThread = Thread.currentThread();
+ Assert.isLegal(thread == thisThread,
+ "Thread attempted to release a lock it did not own");
+ if (--nestingCount == 0) {
+ if(DEBUG) System.out.println("[" + thread.getName() + "] released CVS synchronizer lock");
+ thread = null;
+ notifyAll();
+ }
+ }
+
+ public int getNestingCount() {
+ Thread thisThread = Thread.currentThread();
+ Assert.isLegal(thread == thisThread,
+ "Thread attempted to read nesting count of a lock it did not own");
+ return nestingCount;
+ }
+}

Back to the top