Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Valenta2003-09-09 13:41:34 -0400
committerMichael Valenta2003-09-09 13:41:34 -0400
commit3fffcff95414dae9724718c7c283300a405b6769 (patch)
tree7e2cdf05383e10133814f5af0bef0401fe146224
parentc118ba05f74d1491b6dfec08da36a4352e5cc75b (diff)
downloadeclipse.platform.team-3fffcff95414dae9724718c7c283300a405b6769.tar.gz
eclipse.platform.team-3fffcff95414dae9724718c7c283300a405b6769.tar.xz
eclipse.platform.team-3fffcff95414dae9724718c7c283300a405b6769.zip
42566: ConcurrentModificationException during Team refresh with all repositoriesRoot_branch_mvalenta_launch_configurations
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSWorkspaceSubscriber.java24
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties1
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java568
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ReentrantLock.java200
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/CVSDateFormatter.java10
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/launchConfigurations/One CVS Test.launch17
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/cvsresources/BatchedTestSetup.java4
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/ConcurrencyTests.java8
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/LinkResourcesTest.java2
9 files changed, 517 insertions, 317 deletions
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSWorkspaceSubscriber.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSWorkspaceSubscriber.java
index 440752c4e..4af5c1c46 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSWorkspaceSubscriber.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSWorkspaceSubscriber.java
@@ -80,7 +80,7 @@ public class CVSWorkspaceSubscriber extends CVSSyncTreeSubscriber implements IRe
// TODO: hack for clearing the remote state when anything to the resource
// sync is changed. Should be able to set the *right* remote/base based on
// the sync being set.
- // TODO: This will throw exceptions if performed during the POST_CHANGE delta phase!!!
+ // IMPORTANT NOTE: This will throw exceptions if performed during the POST_CHANGE delta phase!!!
for (int i = 0; i < changedResources.length; i++) {
IResource resource = changedResources[i];
try {
@@ -107,7 +107,7 @@ public class CVSWorkspaceSubscriber extends CVSSyncTreeSubscriber implements IRe
* @see org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener#resourceModified(org.eclipse.core.resources.IResource[])
*/
public void resourceModified(IResource[] changedResources) {
- // TODO: This is only ever called from a delta POST_CHANGE
+ // This is only ever called from a delta POST_CHANGE
// which causes problems since the workspace tree is closed
// for modification and we flush the sync info in resourceSyncInfoChanged
@@ -158,7 +158,6 @@ public class CVSWorkspaceSubscriber extends CVSSyncTreeSubscriber implements IRe
final List result = new ArrayList();
for (int i = 0; i < resources.length; i++) {
IResource resource = resources[i];
- ICVSResource cvsResource = CVSWorkspaceRoot.getCVSResourceFor(resource);
final IProgressMonitor infinite = Policy.infiniteSubMonitorFor(monitor, 100);
try {
// We need to do a scheduling rule on the project because
@@ -167,17 +166,17 @@ public class CVSWorkspaceSubscriber extends CVSSyncTreeSubscriber implements IRe
Platform.getJobManager().beginRule(resource);
infinite.beginTask(null, 512);
resource.accept(new IResourceVisitor() {
- public boolean visit(IResource resource) throws CoreException {
+ public boolean visit(IResource innerResource) throws CoreException {
try {
- if (isOutOfSync(resource, infinite)) {
- SyncInfo info = getSyncInfo(resource, infinite);
+ if (isOutOfSync(innerResource, infinite)) {
+ SyncInfo info = getSyncInfo(innerResource, infinite);
if (info != null && info.getKind() != 0) {
result.add(info);
}
}
return true;
} catch (TeamException e) {
- // TODO: This is probably not the right thing to do here
+ // TODO:See bug 42795
throw new CoreException(e.getStatus());
}
}
@@ -193,7 +192,7 @@ public class CVSWorkspaceSubscriber extends CVSSyncTreeSubscriber implements IRe
return (SyncInfo[]) result.toArray(new SyncInfo[result.size()]);
}
- private boolean isOutOfSync(IResource resource, IProgressMonitor monitor) throws CVSException {
+ /* internal use only */ boolean isOutOfSync(IResource resource, IProgressMonitor monitor) throws CVSException {
return (hasIncomingChange(resource) || hasOutgoingChange(CVSWorkspaceRoot.getCVSResourceFor(resource), monitor));
}
@@ -201,18 +200,17 @@ public class CVSWorkspaceSubscriber extends CVSSyncTreeSubscriber implements IRe
if (resource.isFolder()) {
// A folder is an outgoing change if it is not a CVS folder and not ignored
ICVSFolder folder = (ICVSFolder)resource;
- // TODO: The parent caches the dirty state so we only need to check
- // the file if the parent is dirty.
- // TODO: Unfortunately, the modified check on the parent still loads
- // the CVS folder information so not much is gained
+ // OPTIMIZE: The following checks load the CVS folder information
if (folder.getParent().isModified(monitor)) {
return !folder.isCVSFolder() && !folder.isIgnored();
}
} else {
// A file is an outgoing change if it is modified
ICVSFile file = (ICVSFile)resource;
- // TODO: The parent chaches the dirty state so we only need to check
+ // The parent caches the dirty state so we only need to check
// the file if the parent is dirty
+ // OPTIMIZE: Unfortunately, the modified check on the parent still loads
+ // the CVS folder information so not much is gained
if (file.getParent().isModified(monitor)) {
return file.isModified(monitor);
}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties
index 3e08ef595..66a55176e 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties
@@ -328,3 +328,4 @@ CVSProviderPlugin.21=Synchronizes the CVS managed resources in your workspace wi
CVSSyncTreeSubscriber.0={0} is not a valid comparison criteria for subscriber {1}
CVSRevisionNumberCompareCriteria.1=Revision number comparison
RemoteTagSynchronizer.0=Refreshing {0}
+ReentrantLock.9=An error occurred writting CVS synchronization information to disk. Some information may be lost.
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 a449e146a..44fc513d6 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
@@ -28,6 +28,7 @@ import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
@@ -37,7 +38,6 @@ import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.jobs.ILock;
-import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
import org.eclipse.team.internal.ccvs.core.CVSStatus;
@@ -49,7 +49,10 @@ import org.eclipse.team.internal.ccvs.core.Policy;
import org.eclipse.team.internal.ccvs.core.syncinfo.BaserevInfo;
import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
import org.eclipse.team.internal.ccvs.core.syncinfo.NotifyInfo;
+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.syncinfo.ReentrantLock.IFlushOperation;
+import org.eclipse.team.internal.ccvs.core.syncinfo.ReentrantLock.ThreadInfo;
import org.eclipse.team.internal.ccvs.core.util.Assert;
import org.eclipse.team.internal.ccvs.core.util.FileNameMatcher;
import org.eclipse.team.internal.ccvs.core.util.SyncFileWriter;
@@ -67,10 +70,17 @@ import org.eclipse.team.internal.ccvs.core.util.SyncFileWriter;
* Special processing has been added for linked folders and their childen so
* that their CVS meta files are never read or written.
*
+ * IMPORTANT NOTICE: It is the reponsibility of the clients of EclipseSynchronizer
+ * to ensure that they have wrapped operations that may modify the workspace in
+ * an IWorkspaceRunnable. If this is not done, deltas may fore at inopertune times
+ * and corrupt the sync info. The wrapping could be done within the synchronizer
+ * itself but would require the creation of an inner class for each case that requires
+ * it.
+ *
* @see ResourceSyncInfo
* @see FolderSyncInfo
*/
-public class EclipseSynchronizer {
+public class EclipseSynchronizer implements IFlushOperation {
private static final String IS_DIRTY_INDICATOR = SyncInfoCache.IS_DIRTY_INDICATOR;
private static final String NOT_DIRTY_INDICATOR = SyncInfoCache.NOT_DIRTY_INDICATOR;
private static final String RECOMPUTE_INDICATOR = SyncInfoCache.RECOMPUTE_INDICATOR;
@@ -80,9 +90,7 @@ public class EclipseSynchronizer {
// track resources that have changed in a given operation
private ILock lock = Platform.getJobManager().newLock();
-
- private Set changedResources = new HashSet();
- private Set changedFolders = new HashSet();
+ private ReentrantLock resourceLock = new ReentrantLock();
private SessionPropertySyncInfoCache sessionPropertyCache = new SessionPropertySyncInfoCache();
private SynchronizerSyncInfoCache synchronizerCache = new SynchronizerSyncInfoCache();
@@ -132,18 +140,23 @@ public class EclipseSynchronizer {
Policy.bind("EclipseSynchronizer.ErrorSettingFolderSync", folder.getFullPath().toString())); //$NON-NLS-1$
}
try {
- beginOperation(folder, null);
- // get the old info
- FolderSyncInfo oldInfo = getFolderSync(folder);
- // set folder sync and notify
- getSyncInfoCacheFor(folder).setCachedFolderSync(folder, info);
- // if the sync info changed from null, we may need to adjust the ancestors
- if (oldInfo == null) {
- adjustDirtyStateRecursively(folder, RECOMPUTE_INDICATOR);
+ beginBatching(folder);
+ try {
+ beginOperation();
+ // get the old info
+ FolderSyncInfo oldInfo = getFolderSync(folder);
+ // set folder sync and notify
+ getSyncInfoCacheFor(folder).setCachedFolderSync(folder, info);
+ // if the sync info changed from null, we may need to adjust the ancestors
+ if (oldInfo == null) {
+ adjustDirtyStateRecursively(folder, RECOMPUTE_INDICATOR);
+ }
+ folderChanged(folder);
+ } finally {
+ endOperation();
}
- changedFolders.add(folder);
} finally {
- endOperation(null);
+ endBatching(null);
}
}
@@ -157,11 +170,11 @@ public class EclipseSynchronizer {
public FolderSyncInfo getFolderSync(IContainer folder) throws CVSException {
if (folder.getType() == IResource.ROOT || !isValid(folder)) return null;
try {
- beginOperation(folder, null);
+ beginOperation();
cacheFolderSync(folder);
return getSyncInfoCacheFor(folder).getCachedFolderSync(folder);
} finally {
- endOperation(null);
+ endOperation();
}
}
@@ -175,27 +188,40 @@ public class EclipseSynchronizer {
public void deleteFolderSync(IContainer folder) throws CVSException {
if (folder.getType() == IResource.ROOT || !isValid(folder)) return;
try {
- beginOperation(folder, null);
- // iterate over all children with sync info and prepare notifications
- // this is done first since deleting the folder sync may remove a phantom
- cacheResourceSyncForChildren(folder);
- IResource[] children = folder.members(true);
- for (int i = 0; i < children.length; i++) {
- IResource resource = children[i];
- changedResources.add(resource);
- // delete resource sync for all children
- getSyncInfoCacheFor(resource).setCachedSyncBytes(resource, null);
+ beginBatching(folder);
+ try {
+ beginOperation();
+ // iterate over all children with sync info and prepare notifications
+ // this is done first since deleting the folder sync may remove a phantom
+ cacheResourceSyncForChildren(folder);
+ IResource[] children = folder.members(true);
+ for (int i = 0; i < children.length; i++) {
+ IResource resource = children[i];
+ resourceChanged(resource);
+ // delete resource sync for all children
+ getSyncInfoCacheFor(resource).setCachedSyncBytes(resource, null);
+ }
+ // delete folder sync
+ getSyncInfoCacheFor(folder).setCachedFolderSync(folder, null);
+ folderChanged(folder);
+ } catch (CoreException e) {
+ throw CVSException.wrapException(e);
+ } finally {
+ endOperation();
}
- // delete folder sync
- getSyncInfoCacheFor(folder).setCachedFolderSync(folder, null);
- changedFolders.add(folder);
- } catch (CoreException e) {
- throw CVSException.wrapException(e);
} finally {
- endOperation(null);
+ endBatching(null);
}
}
+ private void folderChanged(IContainer folder) {
+ resourceLock.folderChanged(folder);
+ }
+
+ private void resourceChanged(IResource resource) {
+ resourceLock.resourceChanged(resource);
+ }
+
/**
* Sets the resource sync info for the specified resource.
* The parent folder must exist and must not be the workspace root.
@@ -212,13 +238,18 @@ public class EclipseSynchronizer {
Policy.bind("EclipseSynchronizer.ErrorSettingResourceSync", resource.getFullPath().toString())); //$NON-NLS-1$
}
try {
- beginOperation(resource, null);
- // cache resource sync for siblings, set for self, then notify
- cacheResourceSyncForChildren(parent);
- setCachedResourceSync(resource, info);
- changedResources.add(resource);
+ beginBatching(resource);
+ try {
+ beginOperation();
+ // cache resource sync for siblings, set for self, then notify
+ cacheResourceSyncForChildren(parent);
+ setCachedResourceSync(resource, info);
+ resourceChanged(resource);
+ } finally {
+ endOperation();
+ }
} finally {
- endOperation(null);
+ endBatching(null);
}
}
@@ -246,7 +277,7 @@ public class EclipseSynchronizer {
IContainer parent = resource.getParent();
if (parent == null || parent.getType() == IResource.ROOT || !isValid(parent)) return null;
try {
- beginOperation(resource, null);
+ beginOperation();
// cache resource sync for siblings, then return for self
try {
cacheResourceSyncForChildren(parent);
@@ -261,7 +292,7 @@ public class EclipseSynchronizer {
}
return getCachedSyncBytes(resource);
} finally {
- endOperation(null);
+ endOperation();
}
}
@@ -281,13 +312,18 @@ public class EclipseSynchronizer {
Policy.bind("EclipseSynchronizer.ErrorSettingResourceSync", resource.getFullPath().toString())); //$NON-NLS-1$
}
try {
- beginOperation(resource, null);
- // cache resource sync for siblings, set for self, then notify
- cacheResourceSyncForChildren(parent);
- setCachedSyncBytes(resource, syncBytes);
- changedResources.add(resource);
+ beginBatching(resource);
+ try {
+ beginOperation();
+ // cache resource sync for siblings, set for self, then notify
+ cacheResourceSyncForChildren(parent);
+ setCachedSyncBytes(resource, syncBytes);
+ resourceChanged(resource);
+ } finally {
+ endOperation();
+ }
} finally {
- endOperation(null);
+ endBatching(null);
}
}
@@ -301,16 +337,21 @@ public class EclipseSynchronizer {
IContainer parent = resource.getParent();
if (parent == null || parent.getType() == IResource.ROOT || !isValid(parent)) return;
try {
- beginOperation(resource, null);
- // cache resource sync for siblings, delete for self, then notify
- cacheResourceSyncForChildren(parent);
- if (getCachedSyncBytes(resource) != null) { // avoid redundant notifications
- setCachedSyncBytes(resource, null);
- clearDirtyIndicator(resource);
- changedResources.add(resource);
+ beginBatching(resource);
+ try {
+ beginOperation();
+ // cache resource sync for siblings, delete for self, then notify
+ cacheResourceSyncForChildren(parent);
+ if (getCachedSyncBytes(resource) != null) { // avoid redundant notifications
+ setCachedSyncBytes(resource, null);
+ clearDirtyIndicator(resource);
+ resourceChanged(resource);
+ }
+ } finally {
+ endOperation();
}
} finally {
- endOperation(null);
+ endBatching(null);
}
}
@@ -336,11 +377,11 @@ public class EclipseSynchronizer {
return false;
}
try {
- beginOperation(resource, null);
+ beginOperation();
FileNameMatcher matcher = cacheFolderIgnores(resource.getParent());
return matcher.match(resource.getName());
} finally {
- endOperation(null);
+ endOperation();
}
}
@@ -356,29 +397,34 @@ public class EclipseSynchronizer {
Policy.bind("EclipseSynchronizer.ErrorSettingIgnorePattern", folder.getFullPath().toString())); //$NON-NLS-1$
}
try {
- beginOperation(folder, null);
- String[] ignores = SyncFileWriter.readCVSIgnoreEntries(folder);
- if (ignores != null) {
- // verify that the pattern has not already been added
- for (int i = 0; i < ignores.length; i++) {
- if (ignores[i].equals(pattern)) return;
+ beginBatching(folder);
+ try {
+ beginOperation();
+ String[] ignores = SyncFileWriter.readCVSIgnoreEntries(folder);
+ if (ignores != null) {
+ // verify that the pattern has not already been added
+ for (int i = 0; i < ignores.length; i++) {
+ if (ignores[i].equals(pattern)) return;
+ }
+ // add the pattern
+ 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 };
}
- // add the pattern
- 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.writeCVSIgnoreEntries(folder, ignores);
+ // broadcast changes to unmanaged children - they are the only candidates for being ignored
+ List possibleIgnores = new ArrayList();
+ accumulateNonManagedChildren(folder, possibleIgnores);
+ CVSProviderPlugin.broadcastSyncInfoChanges((IResource[])possibleIgnores.toArray(new IResource[possibleIgnores.size()]));
+ } finally {
+ endOperation();
}
- setCachedFolderIgnores(folder, ignores);
- SyncFileWriter.writeCVSIgnoreEntries(folder, ignores);
- // broadcast changes to unmanaged children - they are the only candidates for being ignored
- List possibleIgnores = new ArrayList();
- accumulateNonManagedChildren(folder, possibleIgnores);
- CVSProviderPlugin.broadcastSyncInfoChanges((IResource[])possibleIgnores.toArray(new IResource[possibleIgnores.size()]));
} finally {
- endOperation(null);
+ endBatching(null);
}
}
@@ -392,7 +438,7 @@ public class EclipseSynchronizer {
public IResource[] members(IContainer folder) throws CVSException {
if (! isValid(folder)) return new IResource[0];
try {
- beginOperation(folder, null);
+ beginOperation();
if (folder.getType() != IResource.ROOT) {
// ensure that the sync info is cached so any required phantoms are created
cacheResourceSyncForChildren(folder);
@@ -401,35 +447,23 @@ public class EclipseSynchronizer {
} catch (CoreException e) {
throw CVSException.wrapException(e);
} finally {
- endOperation(null);
+ endOperation();
}
}
+
/**
* Begins a batch of operations.
*
* @param monitor the progress monitor, may be null
*/
- public void beginOperation(IResource resource, IProgressMonitor monitor) throws CVSException {
- // ensure locks are acquired in the same order: workspace then cvs
- // The scheduling rule is either the project or the resource's parent
- ISchedulingRule rule;
- if (resource.getType() == IResource.PROJECT || resource.getType() == IResource.ROOT) {
- rule = resource;
- } else {
- rule = resource.getParent();
- }
- Platform.getJobManager().beginRule(rule);
- lock.acquire();
-
- if (lock.getDepth() == 1) {
- prepareCache(monitor);
- }
+ public void beginBatching(IResource resource) {
+ resourceLock.acquire(resource, this /* IFlushOperation */);
}
/**
* Ends a batch of operations. Pending changes are committed only when
- * the number of calls to endOperation() balances those to beginOperation().
+ * the number of calls to endBatching() balances those to beginBatching().
* <p>
* Progress cancellation is ignored while writting the cache to disk. This
* is to ensure cache to disk consistency.
@@ -439,77 +473,103 @@ public class EclipseSynchronizer {
* @exception CVSException with a status with code <code>COMMITTING_SYNC_INFO_FAILED</code>
* if all the CVS sync information could not be written to disk.
*/
- public void endOperation(IProgressMonitor monitor) throws CVSException {
- try {
- IStatus status = SyncInfoCache.STATUS_OK;
- if (lock.getDepth() == 1) {
- status = commitCache(monitor);
- }
- if (!status.isOK()) {
- throw new CVSException(status);
+ public void endBatching(IProgressMonitor monitor) throws CVSException {
+ resourceLock.release(monitor);
+ }
+
+ /* (non-Javadoc)
+ *
+ * Callback which is invoked when the batching resource lock is released
+ * or when a flush is requested (see beginBatching(IResource)).
+ *
+ * @see org.eclipse.team.internal.ccvs.core.syncinfo.ReentrantLock.IRunnableOnExit#run(org.eclipse.team.internal.ccvs.core.syncinfo.ReentrantLock.ThreadInfo, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void flush(final ThreadInfo info, IProgressMonitor monitor) throws CVSException {
+ if (info != null && !info.isEmpty()) {
+ try {
+ beginOperation();
+ ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
+ public void run(IProgressMonitor pm) throws CoreException {
+ IStatus status = commitCache(info, pm);
+ if (!status.isOK()) {
+ throw new CVSException(status);
+ }
+ }
+ }, null, monitor);
+ } catch (CoreException e) {
+ throw CVSException.wrapException(e);
+ } finally {
+ endOperation();
}
- } finally {
- // ensure locks are released in the same order: cvs then workspace
- lock.release();
- Platform.getJobManager().endRule();
}
}
+ /*
+ * Begin an access to the internal data structures of the synchronizer
+ */
+ private void beginOperation() {
+ lock.acquire();
+ }
+
+ /*
+ * End an access to the internal data structures of the synchronizer
+ */
+ private void endOperation() {
+ lock.release();
+ }
+
/**
- * Flushes unwritten sync information to disk.
+ * Flush the sync information from the in-memery cache to disk and purge
+ * the entries from the cache.
* <p>
- * Recursively commits unwritten sync information for all resources
- * below the root, and optionally purges the cached data from memory
+ * Recursively flushes the sync information for all resources
+ * below the root to disk and purges the entries from memory
* so that the next time it is accessed it will be retrieved from disk.
* May flush more sync information than strictly needed, but never less.
* </p>
- * <p>
- * Will throw a CVS Exception with a status with code = CVSStatus.DELETION_FAILED
- * if the flush could not perform CVS folder deletions. In this case, all other
- * aspects of the operation succeeded.
- * </p>
*
- * @param root the root of the subtree to flush
- * @param purgeCache if true, purges the cache from memory as well
+ * @param root the root of the subtree to purge
* @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 {
- // flush unwritten sync info to disk
+ public void flush(IContainer root, boolean deep, IProgressMonitor monitor) throws CVSException {
monitor = Policy.monitorFor(monitor);
monitor.beginTask(null, 10);
try {
- beginOperation(root, Policy.subMonitorFor(monitor, 1));
-
- IStatus status = commitCache(Policy.subMonitorFor(monitor, 7));
-
- // purge from memory too if we were asked to
- if (purgeCache) {
- sessionPropertyCache.purgeCache(root, deep);
- }
-
- // prepare for the operation again if we cut the last one short
- prepareCache(Policy.subMonitorFor(monitor, 1));
-
- if (!status.isOK()) {
- throw new CVSException(status);
+ beginBatching(root);
+ try {
+ beginOperation();
+ try {
+ // Flush changes to disk
+ resourceLock.flush(Policy.subMonitorFor(monitor, 7));
+ } finally {
+ // Purge the in-memory cache
+ sessionPropertyCache.purgeCache(root, deep);
+ }
+ } finally {
+ endOperation();
}
} finally {
- endOperation(Policy.subMonitorFor(monitor, 1));
+ endBatching(Policy.subMonitorFor(monitor, 3));
monitor.done();
}
}
public void deconfigure(final IProject project, IProgressMonitor monitor) throws CVSException {
- run(project, new ICVSRunnable() {
- public void run(IProgressMonitor monitor) throws CVSException {
- flush(project, true, true, monitor);
+ monitor = Policy.monitorFor(monitor);
+ monitor.beginTask(null, 100);
+ try {
+ beginBatching(project);
+ // Flush
+ flush(project, true /* deep */, monitor);
- // forget about pruned folders however the top level pruned folder will have resource sync (e.g.
- // a line in the Entry file). As a result the folder is managed but is not a CVS folder.
- synchronizerCache.purgeCache(project, true);
- }
- }, monitor);
+ // forget about pruned folders however the top level pruned folder will have resource sync (e.g.
+ // a line in the Entry file). As a result the folder is managed but is not a CVS folder.
+ synchronizerCache.purgeCache(project, true);
+ } finally {
+ endBatching(Policy.subMonitorFor(monitor, 20));
+ monitor.done();
+ }
}
private void purgeCache(IResource resource, boolean deep) throws CVSException {
@@ -529,7 +589,7 @@ public class EclipseSynchronizer {
try {
for (int i = 0; i < roots.length; i++) {
IContainer root = roots[i];
- flush(root, true, false /*don't flush children*/, null);
+ flush(root, false /*don't flush children*/, null);
List changedPeers = new ArrayList();
changedPeers.add(root);
changedPeers.addAll(Arrays.asList(root.members()));
@@ -548,37 +608,42 @@ public class EclipseSynchronizer {
public void prepareForDeletion(IResource resource) throws CVSException {
if (!resource.exists()) return;
try {
- beginOperation(resource, null);
- // Flush the dirty info for the resource and it's ancestors.
- // Although we could be smarter, we need to do this because the
- // deletion may fail.
- adjustDirtyStateRecursively(resource, RECOMPUTE_INDICATOR);
- if (resource.getType() == IResource.FILE) {
- byte[] syncBytes = getSyncBytes(resource);
- if (syncBytes != null) {
- if (!ResourceSyncInfo.isAddition(syncBytes)) {
- syncBytes = convertToDeletion(syncBytes);
- synchronizerCache.setCachedSyncBytes(resource, syncBytes);
+ beginBatching(resource);
+ try {
+ beginOperation();
+ // Flush the dirty info for the resource and it's ancestors.
+ // Although we could be smarter, we need to do this because the
+ // deletion may fail.
+ adjustDirtyStateRecursively(resource, RECOMPUTE_INDICATOR);
+ if (resource.getType() == IResource.FILE) {
+ byte[] syncBytes = getSyncBytes(resource);
+ if (syncBytes != null) {
+ if (!ResourceSyncInfo.isAddition(syncBytes)) {
+ syncBytes = convertToDeletion(syncBytes);
+ synchronizerCache.setCachedSyncBytes(resource, syncBytes);
+ }
+ resourceChanged(resource);
}
- changedResources.add(resource);
- }
- } else {
- IContainer container = (IContainer)resource;
- if (container.getType() == IResource.PROJECT) {
- synchronizerCache.flush((IProject)container);
} else {
- // Move the folder sync info into phantom space
- FolderSyncInfo info = getFolderSync(container);
- if (info == null) return;
- synchronizerCache.setCachedFolderSync(container, info);
- changedFolders.add(container);
- // move the resource sync as well
- byte[] syncBytes = getSyncBytes(resource);
- synchronizerCache.setCachedSyncBytes(resource, syncBytes);
+ IContainer container = (IContainer)resource;
+ if (container.getType() == IResource.PROJECT) {
+ synchronizerCache.flush((IProject)container);
+ } else {
+ // Move the folder sync info into phantom space
+ FolderSyncInfo info = getFolderSync(container);
+ if (info == null) return;
+ synchronizerCache.setCachedFolderSync(container, info);
+ folderChanged(container);
+ // move the resource sync as well
+ byte[] syncBytes = getSyncBytes(resource);
+ synchronizerCache.setCachedSyncBytes(resource, syncBytes);
+ }
}
+ } finally {
+ endOperation();
}
} finally {
- endOperation(null);
+ endBatching(null);
}
}
@@ -593,10 +658,15 @@ public class EclipseSynchronizer {
protected void handleDeleted(IResource resource) throws CVSException {
if (resource.exists()) return;
try {
- beginOperation(resource, null);
- adjustDirtyStateRecursively(resource, RECOMPUTE_INDICATOR);
+ beginBatching(resource);
+ try {
+ beginOperation();
+ adjustDirtyStateRecursively(resource, RECOMPUTE_INDICATOR);
+ } finally {
+ endOperation();
+ }
} finally {
- endOperation(null);
+ endBatching(null);
}
}
@@ -613,10 +683,11 @@ public class EclipseSynchronizer {
public void prepareForMoveDelete(IResource resource, IProgressMonitor monitor) throws CVSException {
// Move sync info to phantom space for the resource and all it's children
try {
+ monitor.beginTask(null, 100);
resource.accept(new IResourceVisitor() {
- public boolean visit(IResource resource) throws CoreException {
+ public boolean visit(IResource innerResource) throws CoreException {
try {
- prepareForDeletion(resource);
+ prepareForDeletion(innerResource);
} catch (CVSException e) {
CVSProviderPlugin.log(e);
throw new CoreException(e.getStatus());
@@ -626,6 +697,8 @@ public class EclipseSynchronizer {
});
} catch (CoreException e) {
throw CVSException.wrapException(e);
+ } finally {
+ monitor.done();
}
// purge the sync info to clear the session properties
purgeCache(resource, true);
@@ -639,11 +712,17 @@ public class EclipseSynchronizer {
* @throws CVSException
*/
public void created(IResource resource) throws CVSException {
- if (resource.getType() == IResource.FILE) {
- created((IFile)resource);
- } else if (resource.getType() == IResource.FOLDER) {
- created((IFolder)resource);
+ try {
+ beginBatching(resource);
+ if (resource.getType() == IResource.FILE) {
+ created((IFile)resource);
+ } else if (resource.getType() == IResource.FOLDER) {
+ created((IFolder)resource);
+ }
+ } finally {
+ endBatching(null);
}
+
}
/**
@@ -655,7 +734,7 @@ public class EclipseSynchronizer {
private void created(IFolder folder) throws CVSException {
try {
// set the dirty count using what was cached in the phantom it
- beginOperation(folder, null);
+ beginOperation();
FolderSyncInfo folderInfo = synchronizerCache.getCachedFolderSync(folder);
byte[] syncBytes = synchronizerCache.getCachedSyncBytes(folder);
if (folderInfo != null && syncBytes != null) {
@@ -691,7 +770,7 @@ public class EclipseSynchronizer {
}
} finally {
try {
- endOperation(null);
+ endOperation();
} finally {
synchronizerCache.flush(folder);
}
@@ -707,7 +786,7 @@ public class EclipseSynchronizer {
private void created(IFile file) throws CVSException {
try {
// set the dirty count using what was cached in the phantom it
- beginOperation(file, null);
+ beginOperation();
byte[] syncBytes = synchronizerCache.getCachedSyncBytes(file);
if (syncBytes == null) return;
byte[] newBytes = getSyncBytes(file);
@@ -717,7 +796,7 @@ public class EclipseSynchronizer {
}
} finally {
try {
- endOperation(null);
+ endOperation();
} finally {
synchronizerCache.setCachedSyncBytes(file, null);
}
@@ -801,14 +880,6 @@ public class EclipseSynchronizer {
}
/**
- * Prepares the cache for a series of operations.
- *
- * @param monitor the progress monitor, may be null
- */
- private void prepareCache(IProgressMonitor monitor) throws CVSException {
- }
-
- /**
* Commits the cache after a series of operations.
*
* Will return STATUS_OK unless there were problems writting sync
@@ -818,24 +889,26 @@ public class EclipseSynchronizer {
*
* @param monitor the progress monitor, may be null
*/
- private IStatus commitCache(IProgressMonitor monitor) {
- if (changedFolders.isEmpty() && changedResources.isEmpty()) {
+ /* internal use only */ IStatus commitCache(ThreadInfo threadInfo, IProgressMonitor monitor) {
+ if (threadInfo.isEmpty()) {
return SyncInfoCache.STATUS_OK;
}
List errors = new ArrayList();
try {
/*** prepare operation ***/
// find parents of changed resources
+ IResource[] changedResources = threadInfo.getChangedResources();
+ IContainer[] changedFolders = threadInfo.getChangedFolders();
Set dirtyParents = new HashSet();
- for(Iterator it = changedResources.iterator(); it.hasNext();) {
- IResource resource = (IResource) it.next();
+ for (int i = 0; i < changedResources.length; i++) {
+ IResource resource = changedResources[i];
IContainer folder = resource.getParent();
dirtyParents.add(folder);
}
monitor = Policy.monitorFor(monitor);
int numDirty = dirtyParents.size();
- int numResources = changedFolders.size() + numDirty;
+ int numResources = changedFolders.length + numDirty;
monitor.beginTask(null, numResources);
if(monitor.isCanceled()) {
monitor.subTask(Policy.bind("EclipseSynchronizer.UpdatingSyncEndOperationCancelled")); //$NON-NLS-1$
@@ -845,8 +918,8 @@ public class EclipseSynchronizer {
/*** write sync info to disk ***/
// folder sync info changes
- for(Iterator it = changedFolders.iterator(); it.hasNext();) {
- IContainer folder = (IContainer) it.next();
+ for (int i = 0; i < changedFolders.length; i++) {
+ IContainer folder = changedFolders[i];
if (folder.exists() && folder.getType() != IResource.ROOT) {
try {
FolderSyncInfo info = sessionPropertyCache.getCachedFolderSync(folder);
@@ -916,13 +989,13 @@ public class EclipseSynchronizer {
/*** broadcast events ***/
monitor.subTask(Policy.bind("EclipseSynchronizer.NotifyingListeners")); //$NON-NLS-1$
- changedResources.addAll(changedFolders);
- changedResources.addAll(dirtyParents);
- IResource[] resources = (IResource[]) changedResources.toArray(
- new IResource[changedResources.size()]);
+ Set allChanges = new HashSet();
+ allChanges.addAll(Arrays.asList(changedResources));
+ allChanges.addAll(Arrays.asList(changedFolders));
+ allChanges.addAll(dirtyParents);
+ IResource[] resources = (IResource[]) allChanges.toArray(
+ new IResource[allChanges.size()]);
broadcastResourceStateChanges(resources);
- changedResources.clear();
- changedFolders.clear();
if ( ! errors.isEmpty()) {
MultiStatus status = new MultiStatus(CVSProviderPlugin.ID,
CVSStatus.COMMITTING_SYNC_INFO_FAILED,
@@ -972,7 +1045,7 @@ public class EclipseSynchronizer {
*/
private void setCachedSyncBytes(IResource resource, byte[] syncBytes) throws CVSException {
getSyncInfoCacheFor(resource).setCachedSyncBytes(resource, syncBytes);
- changedResources.add(resource);
+ resourceChanged(resource);
}
/**
@@ -1067,7 +1140,7 @@ public class EclipseSynchronizer {
Map infoMap = new HashMap();
for (int i = 0; i < infos.length; i++) {
NotifyInfo notifyInfo = infos[i];
- infoMap.put(infos[i].getName(), infos[i]);
+ infoMap.put(notifyInfo.getName(), notifyInfo);
}
if (info == null) {
// if the info is null, remove the entry
@@ -1114,7 +1187,7 @@ public class EclipseSynchronizer {
Map infoMap = new HashMap();
for (int i = 0; i < infos.length; i++) {
NotifyInfo notifyInfo = infos[i];
- infoMap.put(infos[i].getName(), infos[i]);
+ infoMap.put(notifyInfo.getName(), notifyInfo);
}
infoMap.remove(resource.getName());
NotifyInfo[] newInfos = new NotifyInfo[infoMap.size()];
@@ -1194,29 +1267,37 @@ public class EclipseSynchronizer {
}
public void copyFileToBaseDirectory(final IFile file, IProgressMonitor monitor) throws CVSException {
- run(file, new ICVSRunnable() {
- public void run(IProgressMonitor monitor) throws CVSException {
- ResourceSyncInfo info = getResourceSync(file);
- // The file must exist remotely and locally
- if (info == null || info.isAdded() || info.isDeleted())
- return;
- SyncFileWriter.writeFileToBaseDirectory(file, monitor);
- changedResources.add(file);
- }
- }, monitor);
+ monitor = Policy.monitorFor(monitor);
+ monitor.beginTask(null, 100);
+ try {
+ beginBatching(file);
+ ResourceSyncInfo info = getResourceSync(file);
+ // The file must exist remotely and locally
+ if (info == null || info.isAdded() || info.isDeleted())
+ return;
+ SyncFileWriter.writeFileToBaseDirectory(file, monitor);
+ resourceChanged(file);
+ } finally {
+ endBatching(Policy.subMonitorFor(monitor, 20));
+ monitor.done();
+ }
}
public void restoreFileFromBaseDirectory(final IFile file, IProgressMonitor monitor) throws CVSException {
- run(file, new ICVSRunnable() {
- public void run(IProgressMonitor monitor) throws CVSException {
- ResourceSyncInfo info = getResourceSync(file);
- // The file must exist remotely
- if (info == null || info.isAdded())
- return;
- SyncFileWriter.restoreFileFromBaseDirectory(file, monitor);
- changedResources.add(file);
- }
- }, monitor);
+ monitor = Policy.monitorFor(monitor);
+ monitor.beginTask(null, 100);
+ try {
+ beginBatching(file);
+ ResourceSyncInfo info = getResourceSync(file);
+ // The file must exist remotely
+ if (info == null || info.isAdded())
+ return;
+ SyncFileWriter.restoreFileFromBaseDirectory(file, monitor);
+ resourceChanged(file);
+ } finally {
+ endBatching(Policy.subMonitorFor(monitor, 20));
+ monitor.done();
+ }
}
public void deleteFileFromBaseDirectory(final IFile file, IProgressMonitor monitor) throws CVSException {
@@ -1261,12 +1342,12 @@ public class EclipseSynchronizer {
for (int i = 0; i < folders.length; i++) {
IContainer parent = folders[i];
try {
- beginOperation(parent, null);
+ beginOperation();
cacheResourceSyncForChildren(parent);
cacheFolderSync(parent);
cacheFolderIgnores(parent);
} finally {
- endOperation(null);
+ endOperation();
}
}
}
@@ -1287,9 +1368,9 @@ public class EclipseSynchronizer {
if (depth != IResource.DEPTH_ZERO) {
try {
resource.accept(new IResourceVisitor() {
- public boolean visit(IResource resource) throws CoreException {
- if (resource.getType() == IResource.FOLDER)
- folders.add(resource);
+ public boolean visit(IResource innerResource) throws CoreException {
+ if (innerResource.getType() == IResource.FOLDER)
+ folders.add(innerResource);
// let the depth determine who we visit
return true;
}
@@ -1312,10 +1393,10 @@ public class EclipseSynchronizer {
monitor = Policy.monitorFor(monitor);
monitor.beginTask(null, 100);
try {
- beginOperation(rootResource, Policy.subMonitorFor(monitor, 5));
- job.run(Policy.subMonitorFor(monitor, 60));
+ beginBatching(rootResource);
+ job.run(Policy.subMonitorFor(monitor, 80));
} finally {
- endOperation(Policy.subMonitorFor(monitor, 35));
+ endBatching(Policy.subMonitorFor(monitor, 20));
monitor.done();
}
}
@@ -1338,7 +1419,7 @@ public class EclipseSynchronizer {
private void adjustDirtyStateRecursively(IResource resource, String indicator) throws CVSException {
if (resource.getType() == IResource.ROOT) return;
try {
- beginOperation(resource, null);
+ beginOperation();
if (indicator == getDirtyIndicator(resource)) {
return;
@@ -1363,16 +1444,16 @@ public class EclipseSynchronizer {
adjustDirtyStateRecursively(parent, indicator);
}
} finally {
- endOperation(null);
+ endOperation();
}
}
protected String getDirtyIndicator(IResource resource) throws CVSException {
try {
- beginOperation(resource, null);
+ beginOperation();
return getSyncInfoCacheFor(resource).getDirtyIndicator(resource);
} finally {
- endOperation(null);
+ endOperation();
}
}
@@ -1382,14 +1463,9 @@ public class EclipseSynchronizer {
* Return true if the modification state was changed.
*/
protected void setDirtyIndicator(IResource resource, boolean modified) throws CVSException {
- try {
- beginOperation(resource, null);
- String indicator = modified ? IS_DIRTY_INDICATOR : NOT_DIRTY_INDICATOR;
- // set the dirty indicator and adjust the parent accordingly
- adjustDirtyStateRecursively(resource, indicator);
- } finally {
- endOperation(null);
- }
+ String indicator = modified ? IS_DIRTY_INDICATOR : NOT_DIRTY_INDICATOR;
+ // set the dirty indicator and adjust the parent accordingly
+ adjustDirtyStateRecursively(resource, indicator);
}
/**
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
index acf453df3..1a43bfd86 100644
--- 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
@@ -10,75 +10,187 @@
*******************************************************************************/
package org.eclipse.team.internal.ccvs.core.syncinfo;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+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.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.
+ * lock on a specific resource by calling acquire(). Subsequently, acquire() can be called
+ * multiple times on the resource or any of its children from within the same thread
+ * without blocking. Other threads that try
+ * and acquire the lock on those same resources will be blocked until the first
+ * thread releases all it's nested locks.
+ * <p>
+ * The locking is managed by the platform via scheduling rules. This class simply
+ * provides the nesting mechnism in order to allow the client to determine when
+ * the lock for the thread has been released. Therefore, this lock will block if
+ * another thread already locks the same resource.</p>
*/
public class ReentrantLock {
private final static boolean DEBUG = Policy.DEBUG_THREADING;
- private Thread thread;
- private int nestingCount;
- private Set readOnlyThreads = new HashSet();
+ public class ThreadInfo {
+ private int nestingCount = 0;
+ private Set changedResources = new HashSet();
+ private Set changedFolders = new HashSet();
+ private IFlushOperation operation;
+ public ThreadInfo(IFlushOperation operation) {
+ this.operation = operation;
+ }
+ public void increment() {
+ nestingCount++;
+ }
+ public int decrement() {
+ nestingCount--;
+ return nestingCount;
+ }
+ public int getNestingCount() {
+ return nestingCount;
+ }
+ public void addChangedResource(IResource resource) {
+ changedResources.add(resource);
+ }
+ public void addChangedFolder(IContainer container) {
+ changedFolders.add(container);
+ }
+ public boolean isEmpty() {
+ return changedFolders.isEmpty() && changedResources.isEmpty();
+ }
+ public IResource[] getChangedResources() {
+ return (IResource[]) changedResources.toArray(new IResource[changedResources.size()]);
+ }
+ public IContainer[] getChangedFolders() {
+ return (IContainer[]) changedFolders.toArray(new IContainer[changedFolders.size()]);
+ }
+ public void flush(IProgressMonitor monitor) throws CVSException {
+ try {
+ operation.flush(this, monitor);
+ } catch (OutOfMemoryError e) {
+ throw e;
+ } catch (Error e) {
+ handleAbortedFlush(e);
+ throw e;
+ } catch (RuntimeException e) {
+ handleAbortedFlush(e);
+ throw e;
+ }
+ changedResources.clear();
+ changedFolders.clear();
+ }
+ private void handleAbortedFlush(Throwable t) {
+ CVSProviderPlugin.log(new CVSStatus(IStatus.ERROR, Policy.bind("ReentrantLock.9"), t)); //$NON-NLS-1$
+ }
+ }
+
+ public interface IFlushOperation {
+ public void flush(ThreadInfo info, IProgressMonitor monitor) throws CVSException;
+ }
+
+ private Map infos = new HashMap();
+
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
+ private ThreadInfo getThreadInfo() {
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"); //$NON-NLS-1$ //$NON-NLS-2$
- wait();
- } catch(InterruptedException e) {
- // keep waiting for the lock
- if(DEBUG) System.out.println("["+ thisThread.getName() + "] interrupted in CVS synchronizer lock"); //$NON-NLS-1$ //$NON-NLS-2$
- }
- }
- thread = thisThread;
- if(DEBUG) System.out.println("[" + thisThread.getName() + "] acquired CVS synchronizer lock"); //$NON-NLS-1$ //$NON-NLS-2$
- }
- nestingCount++;
+ ThreadInfo info = (ThreadInfo)infos.get(thisThread);
+ return info;
}
- public synchronized void release() {
- Thread thisThread = Thread.currentThread();
- Assert.isLegal(thread == thisThread,
- "Thread attempted to release a lock it did not own"); //$NON-NLS-1$
- if (--nestingCount == 0) {
- if(DEBUG) System.out.println("[" + thread.getName() + "] released CVS synchronizer lock"); //$NON-NLS-1$ //$NON-NLS-2$
- thread = null;
- notifyAll();
- }
+ public synchronized void acquire(IResource resource, IFlushOperation operation) {
+ lock(resource);
+ incrementNestingCount(resource, operation);
}
- public int getNestingCount() {
- Thread thisThread = Thread.currentThread();
- Assert.isLegal(thread == thisThread,
- "Thread attempted to read nesting count of a lock it did not own"); //$NON-NLS-1$
- return nestingCount;
+ private void incrementNestingCount(IResource resource, IFlushOperation operation) {
+ ThreadInfo info = getThreadInfo();
+ if (info == null) {
+ info = new ThreadInfo(operation);
+ Thread thisThread = Thread.currentThread();
+ infos.put(thisThread, info);
+ if(DEBUG) System.out.println("[" + thisThread.getName() + "] acquired CVS lock on " + resource.getFullPath()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ info.increment();
}
- public boolean isReadOnly() {
- return readOnlyThreads.contains(thread);
+ private void lock(IResource resource) {
+ // The scheduling rule is either the project or the resource's parent
+ ISchedulingRule rule;
+ if (resource.getType() == IResource.ROOT) {
+ // Never lock the whole workspace
+ rule = new ISchedulingRule() {
+ public boolean contains(ISchedulingRule innerRule) {
+ return false;
+ }
+ public boolean isConflicting(ISchedulingRule innerRule) {
+ return false;
+ }
+ };
+ } else if (resource.getType() == IResource.PROJECT) {
+ rule = resource;
+ } else {
+ rule = resource.getParent();
+ }
+ Platform.getJobManager().beginRule(rule);
+ }
+
+ private void unlock() {
+ Platform.getJobManager().endRule();
}
- public void addReadOnlyThread(Thread thread) {
- readOnlyThreads.add(thread);
+ /**
+ * Release the lock held on any resources by this thread. Execute the
+ * provided runnable if the lock is no longer held (i.e. nesting count is 0).
+ * On exit, the scheduling rule is held by the lock until after the runnable
+ * is run.
+ */
+ public synchronized void release(IProgressMonitor monitor) throws CVSException {
+ ThreadInfo info = getThreadInfo();
+ Assert.isNotNull(info, "Unmatched acquire/release."); //$NON-NLS-1$
+ Assert.isTrue(info.getNestingCount() > 0, "Unmatched acquire/release."); //$NON-NLS-1$
+ if (info.decrement() == 0) {
+ Thread thisThread = Thread.currentThread();
+ if(DEBUG) System.out.println("[" + thisThread.getName() + "] released CVS lock"); //$NON-NLS-1$ //$NON-NLS-2$
+ infos.remove(thisThread);
+ info.flush(monitor);
+ unlock();
+ }
+ }
+
+ public void folderChanged(IContainer folder) {
+ ThreadInfo info = getThreadInfo();
+ Assert.isNotNull(info, "Folder changed outside of resource lock"); //$NON-NLS-1$
+ info.addChangedFolder(folder);
+ }
+
+ public void resourceChanged(IResource resource) {
+ ThreadInfo info = getThreadInfo();
+ Assert.isNotNull(info, "Folder changed outside of resource lock"); //$NON-NLS-1$
+ info.addChangedResource(resource);
+ }
+
+ /**
+ * Flush any changes accumulated by the lock so far.
+ */
+ public void flush(IProgressMonitor monitor) throws CVSException {
+ ThreadInfo info = getThreadInfo();
+ Assert.isNotNull(info, "Flush requested outside of resource lock"); //$NON-NLS-1$
+ info.flush(monitor);
}
}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/CVSDateFormatter.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/CVSDateFormatter.java
index e952b9cb9..fb42459d5 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/CVSDateFormatter.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/CVSDateFormatter.java
@@ -38,18 +38,18 @@ public class CVSDateFormatter {
static {
entryLineFormat.setTimeZone(TimeZone.getTimeZone("GMT")); //$NON-NLS-1$
}
- static public Date serverStampToDate(String text) throws ParseException {
+ static synchronized public Date serverStampToDate(String text) throws ParseException {
serverFormat.setTimeZone(getTimeZone(text));
Date date = serverFormat.parse(text);
return date;
}
- static public String dateToServerStamp(Date date) {
+ static synchronized public String dateToServerStamp(Date date) {
serverFormat.setTimeZone(TimeZone.getTimeZone("GMT"));//$NON-NLS-1$
return serverFormat.format(date) + " -0000"; //$NON-NLS-1$
}
- static public Date entryLineToDate(String text) throws ParseException {
+ static synchronized public Date entryLineToDate(String text) throws ParseException {
try {
if (text.charAt(ENTRYLINE_TENS_DAY_OFFSET) == ' ') {
StringBuffer buf = new StringBuffer(text);
@@ -62,7 +62,7 @@ public class CVSDateFormatter {
return entryLineFormat.parse(text);
}
- static public String dateToEntryLine(Date date) {
+ static synchronized public String dateToEntryLine(Date date) {
if (date == null) return ""; //$NON-NLS-1$
String passOne = entryLineFormat.format(date);
if (passOne.charAt(ENTRYLINE_TENS_DAY_OFFSET) != '0') return passOne;
@@ -71,7 +71,7 @@ public class CVSDateFormatter {
return passTwo.toString();
}
- static public String dateToNotifyServer(Date date) {
+ static synchronized public String dateToNotifyServer(Date date) {
serverFormat.setTimeZone(TimeZone.getTimeZone("GMT"));//$NON-NLS-1$
return serverFormat.format(date) + " GMT"; //$NON-NLS-1$
}
diff --git a/tests/org.eclipse.team.tests.cvs.core/launchConfigurations/One CVS Test.launch b/tests/org.eclipse.team.tests.cvs.core/launchConfigurations/One CVS Test.launch
new file mode 100644
index 000000000..3d3f131ff
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/launchConfigurations/One CVS Test.launch
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<launchConfiguration type="org.eclipse.pde.ui.JunitLaunchConfig">
+ <stringAttribute key="vmargs" value="-Declipse.cvs.properties=c:\eclipse\repository.properties -Declipse.cvs.testName=testFileAdditions"/>
+ <booleanAttribute key="askclear" value="false"/>
+ <booleanAttribute key="default" value="true"/>
+ <booleanAttribute key="clearws" value="true"/>
+ <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.team.tests.cvs.core"/>
+ <stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.debug.ui.javaSourceLocator"/>
+ <stringAttribute key="location0" value="C:\Eclipse\Latest-Eclipse-Drop\eclipse\runtime-test-workspace"/>
+ <stringAttribute
+ key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
+ <booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+ <stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
+ <stringAttribute key="application" value="org.eclipse.pde.junit.runtime.coretestapplication"/>
+ <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.team.tests.ccvs.core.provider.IsModifiedTests"/>
+ <stringAttribute key="progargs" value="-os win32 -ws win32 -arch x86 -nl en_CA"/>
+</launchConfiguration>
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/cvsresources/BatchedTestSetup.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/cvsresources/BatchedTestSetup.java
index 1cf00240b..4060f25bc 100644
--- a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/cvsresources/BatchedTestSetup.java
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/cvsresources/BatchedTestSetup.java
@@ -24,10 +24,10 @@ public class BatchedTestSetup extends TestSetup {
}
public void setUp() throws CVSException {
- EclipseSynchronizer.getInstance().beginOperation(ResourcesPlugin.getWorkspace().getRoot(), null);
+ EclipseSynchronizer.getInstance().beginBatching(ResourcesPlugin.getWorkspace().getRoot());
}
public void tearDown() throws CVSException {
- EclipseSynchronizer.getInstance().endOperation(null);
+ EclipseSynchronizer.getInstance().endBatching(null);
}
}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/ConcurrencyTests.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/ConcurrencyTests.java
index 119ae7a2a..aa86e9641 100644
--- a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/ConcurrencyTests.java
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/ConcurrencyTests.java
@@ -24,6 +24,7 @@ import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.progress.IElementCollector;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.ccvs.core.ICVSRemoteFolder;
@@ -71,15 +72,10 @@ public class ConcurrencyTests extends EclipseTest {
}
};
- IJobChangeListener listener = new IJobChangeListener() {
- public void aboutToRun(IJobChangeEvent event) {}
- public void awake(IJobChangeEvent event) {}
+ IJobChangeListener listener = new JobChangeAdapter() {
public void done(IJobChangeEvent event) {
done[0] = true;
}
- public void running(IJobChangeEvent event) {}
- public void scheduled(IJobChangeEvent event) {}
- public void sleeping(IJobChangeEvent event) {}
};
FetchMembersOperation operation = new FetchMembersOperation(null, folder, collector);
operation.setCVSRunnableContext(new HeadlessCVSRunnableContext(listener));
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/LinkResourcesTest.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/LinkResourcesTest.java
index 6dfd62382..d7c80563c 100644
--- a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/LinkResourcesTest.java
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/LinkResourcesTest.java
@@ -71,7 +71,7 @@ public class LinkResourcesTest extends EclipseTest {
public void testLinkCVSFolder() throws CoreException, TeamException, IOException {
IProject source = createProject("testLinkSource", new String[] { "changed.txt", "deleted.txt", "folder1/", "folder1/a.txt" });
IProject sourceCopy = checkoutCopy(source, "copy");
- EclipseSynchronizer.getInstance().flush(source, true, true, DEFAULT_MONITOR);
+ EclipseSynchronizer.getInstance().flush(source, true, DEFAULT_MONITOR);
IProject target = createProject("testLinkTarget", new String[] { "changed.txt", "deleted.txt", "folder1/", "folder1/a.txt" });
IFolder folder = target.getFolder("link");
folder.createLink(source.getLocation(), 0, null);

Back to the top