diff options
author | Michael Valenta | 2003-09-19 19:50:33 +0000 |
---|---|---|
committer | Michael Valenta | 2003-09-19 19:50:33 +0000 |
commit | 14d9674e9df3f9ffbdef77af247d8930f5659685 (patch) | |
tree | 195601bbebe848b01b4477ec72316df6b31d4aee | |
parent | f3991bda215c1449fd538550cf5c1b0802322708 (diff) | |
download | eclipse.platform.team-14d9674e9df3f9ffbdef77af247d8930f5659685.tar.gz eclipse.platform.team-14d9674e9df3f9ffbdef77af247d8930f5659685.tar.xz eclipse.platform.team-14d9674e9df3f9ffbdef77af247d8930f5659685.zip |
Fix to properly handle intermitant deltas during a CVS operation
7 files changed, 195 insertions, 61 deletions
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java index a4d2615c3..4bebfc688 100644 --- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java +++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java @@ -1383,7 +1383,7 @@ public class CVSTeamProvider extends RepositoryProvider { } }; try { - ResourcesPlugin.getWorkspace().run(workspaceRunnable, Policy.monitorFor(monitor)); + ResourcesPlugin.getWorkspace().run(workspaceRunnable, getProject(), Policy.monitorFor(monitor)); } catch (CoreException e) { if (exception[0] == null) { throw CVSException.wrapException(e); diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSWorkspaceRoot.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSWorkspaceRoot.java index c67155253..0243cc603 100644 --- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSWorkspaceRoot.java +++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSWorkspaceRoot.java @@ -291,7 +291,7 @@ public class CVSWorkspaceRoot { exception[0] = e; } } - }, monitor); + }, project, monitor); if (exception[0] != null) throw exception[0]; } catch (CoreException e) { diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFolder.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFolder.java index 5453d7e3e..5ec323636 100644 --- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFolder.java +++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFolder.java @@ -300,7 +300,7 @@ class EclipseFolder extends EclipseResource implements ICVSFolder { error[0] = e; } } - }, monitor); + }, getIResource(), monitor); } catch(CoreException e) { throw CVSException.wrapException(e); } 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 93f0364cd..cb0a71e3c 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 @@ -516,7 +516,7 @@ public class EclipseSynchronizer implements IFlushOperation { } /** - * Flush the sync information from the in-memery cache to disk and purge + * Flush the sync information from the in-memory cache to disk and purge * the entries from the cache. * <p> * Recursively flushes the sync information for all resources @@ -586,7 +586,7 @@ public class EclipseSynchronizer implements IFlushOperation { try { for (int i = 0; i < roots.length; i++) { IContainer root = roots[i]; - flush(root, false /*don't flush children*/, null); + sessionPropertyCache.purgeCache(root, false /*don't flush children*/); List changedPeers = new ArrayList(); changedPeers.add(root); changedPeers.addAll(Arrays.asList(root.members())); @@ -891,6 +891,11 @@ public class EclipseSynchronizer implements IFlushOperation { allChanges.addAll(Arrays.asList(changedResources)); allChanges.addAll(Arrays.asList(changedFolders)); allChanges.addAll(dirtyParents); + try { + allChanges.addAll(Arrays.asList(getResourcesAffectedByChangedIgnoreFiles(threadInfo.getChangedIgnoreFiles()))); + } catch (CVSException e) { + errors.add(e.getStatus()); + } IResource[] resources = (IResource[]) allChanges.toArray( new IResource[allChanges.size()]); broadcastResourceStateChanges(resources); @@ -909,7 +914,7 @@ public class EclipseSynchronizer implements IFlushOperation { monitor.done(); } } - + /** * Broadcasts the resource state changes for the given resources to CVS Provider Plugin */ @@ -1437,4 +1442,42 @@ public class EclipseSynchronizer implements IFlushOperation { return ICVSFile.UNKNOWN; } } + + /** + * Return true if the given resource is contained by the scheduling rule + * that is being used by the current thread. + * @param resource + * @return + */ + public boolean isWithinOperationScope(IResource resource) { + return resourceLock.isWithinActiveThread(resource); + } + + /** + * Record the changed ignore file so it can be handled at the + * end of the operation associated with the current thread. + * @param resource + */ + public void handleIgnoreFileChange(IResource resource) { + Assert.isTrue(resource.getType() == IResource.FILE); + resourceLock.recordIgnoreFileChange((IFile)resource); + } + + private IResource[] getResourcesAffectedByChangedIgnoreFiles(IFile[] files) throws CVSException { + try { + Set changedPeers = new HashSet(); + for (int i = 0; i < files.length; i++) { + IContainer parent = files[i].getParent(); + if (parent.exists()) { + // Include the parent + changedPeers.add(parent); + // Include all siblings + changedPeers.addAll(Arrays.asList(parent.members(false))); + } + } + return (IResource[]) changedPeers.toArray(new IResource[changedPeers.size()]); + } catch (CoreException e) { + throw CVSException.wrapException(e); + } + } } 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 c14556e69..7ec287588 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 @@ -13,11 +13,13 @@ package org.eclipse.team.internal.ccvs.core.syncinfo; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; @@ -47,7 +49,7 @@ public class ReentrantLock { private final static boolean DEBUG = Policy.DEBUG_THREADING; // This is a placeholder rule used to indicate that no scheduling rule is needed - private static final ISchedulingRule NULL_SCHEDULING_RULE= new ISchedulingRule() { + /* internal use only */ static final ISchedulingRule NULL_SCHEDULING_RULE= new ISchedulingRule() { public boolean contains(ISchedulingRule rule) { return false; } @@ -57,23 +59,61 @@ public class ReentrantLock { }; public class ThreadInfo { - private int nestingCount = 0; private Set changedResources = new HashSet(); private Set changedFolders = new HashSet(); + private Set changedIgnoreFiles = new HashSet(); private IFlushOperation operation; private List rules = new ArrayList(); public ThreadInfo(IFlushOperation operation) { this.operation = operation; } - public void increment() { - nestingCount++; + /** + * Push a scheduling rule onto the stack for this thread and + * acquire the rule if it is not the workspace root. + * @param resource + */ + public void pushRule(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 = NULL_SCHEDULING_RULE; + } else if (resource.getType() == IResource.PROJECT) { + rule = resource; + } else { + rule = resource.getParent(); + } + if (rule != NULL_SCHEDULING_RULE) { + Platform.getJobManager().beginRule(rule); + } + addRule(rule); } - public int decrement() { - nestingCount--; - return nestingCount; + /** + * Pop the scheduling rule from the stack and release it if it + * is not the workspace root. Flush any changed sync info to + * disk if necessary. A flush is necessary if the stack is empty + * or if the top-most non-null scheduling rule was popped as a result + * of this operation. + * @param monitor + * @throws CVSException + */ + public void popRule(IProgressMonitor monitor) throws CVSException { + ISchedulingRule rule = removeRule(); + if (isFlushRequired()) { + flush(monitor); + } + if (rule != NULL_SCHEDULING_RULE) { + Platform.getJobManager().endRule(rule); + } } - public int getNestingCount() { - return nestingCount; + /** + * Return <code>true</code> if we are still nested in + * an acquire for this thread. + * + * @return + */ + public boolean isNested() { + return !rules.isEmpty(); } public void addChangedResource(IResource resource) { changedResources.add(resource); @@ -81,8 +121,11 @@ public class ReentrantLock { public void addChangedFolder(IContainer container) { changedFolders.add(container); } + public void addChangedIgnoreFile(IFile resource) { + changedIgnoreFiles.add(resource); + } public boolean isEmpty() { - return changedFolders.isEmpty() && changedResources.isEmpty(); + return changedFolders.isEmpty() && changedResources.isEmpty() && changedIgnoreFiles.isEmpty(); } public IResource[] getChangedResources() { return (IResource[]) changedResources.toArray(new IResource[changedResources.size()]); @@ -90,6 +133,9 @@ public class ReentrantLock { public IContainer[] getChangedFolders() { return (IContainer[]) changedFolders.toArray(new IContainer[changedFolders.size()]); } + public IFile[] getChangedIgnoreFiles() { + return (IFile[]) changedIgnoreFiles.toArray(new IFile[changedIgnoreFiles.size()]); + } public void flush(IProgressMonitor monitor) throws CVSException { try { operation.flush(this, monitor); @@ -105,15 +151,36 @@ public class ReentrantLock { changedResources.clear(); changedFolders.clear(); } + private boolean isFlushRequired() { + return !isNested() || !isNoneNullRules(); + } + private boolean isNoneNullRules() { + for (Iterator iter = rules.iterator(); iter.hasNext();) { + ISchedulingRule rule = (ISchedulingRule) iter.next(); + if (rule != NULL_SCHEDULING_RULE) { + return true; + } + } + return false; + } private void handleAbortedFlush(Throwable t) { CVSProviderPlugin.log(new CVSStatus(IStatus.ERROR, Policy.bind("ReentrantLock.9"), t)); //$NON-NLS-1$ } - public void addRule(ISchedulingRule rule) { + private void addRule(ISchedulingRule rule) { rules.add(rule); } - public ISchedulingRule removeRule() { + private ISchedulingRule removeRule() { return (ISchedulingRule)rules.remove(rules.size() - 1); } + public boolean ruleContains(IResource resource) { + for (Iterator iter = rules.iterator(); iter.hasNext();) { + ISchedulingRule rule = (ISchedulingRule) iter.next(); + if (rule != NULL_SCHEDULING_RULE) { + return rule.contains(resource); + } + } + return false; + } } public interface IFlushOperation { @@ -122,10 +189,6 @@ public class ReentrantLock { private Map infos = new HashMap(); - - public ReentrantLock() { - } - private ThreadInfo getThreadInfo() { Thread thisThread = Thread.currentThread(); ThreadInfo info = (ThreadInfo)infos.get(thisThread); @@ -133,11 +196,6 @@ public class ReentrantLock { } public synchronized void acquire(IResource resource, IFlushOperation operation) { - ISchedulingRule ruleUsed = lock(resource); - incrementNestingCount(resource, ruleUsed, operation); - } - - private void incrementNestingCount(IResource resource, ISchedulingRule rule, IFlushOperation operation) { ThreadInfo info = getThreadInfo(); if (info == null) { info = new ThreadInfo(operation); @@ -145,31 +203,7 @@ public class ReentrantLock { infos.put(thisThread, info); if(DEBUG) System.out.println("[" + thisThread.getName() + "] acquired CVS lock on " + resource.getFullPath()); //$NON-NLS-1$ //$NON-NLS-2$ } - info.addRule(rule); - info.increment(); - } - - private ISchedulingRule 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 = NULL_SCHEDULING_RULE; - } else if (resource.getType() == IResource.PROJECT) { - rule = resource; - } else { - rule = resource.getParent(); - } - if (rule != NULL_SCHEDULING_RULE) { - Platform.getJobManager().beginRule(rule); - } - return rule; - } - - private void unlock(ISchedulingRule rule) { - if (rule != NULL_SCHEDULING_RULE) { - Platform.getJobManager().endRule(rule); - } + info.pushRule(resource); } /** @@ -181,14 +215,13 @@ public class ReentrantLock { 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) { + Assert.isTrue(info.isNested(), "Unmatched acquire/release."); //$NON-NLS-1$ + info.popRule(monitor); + if (!info.isNested()) { 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(info.removeRule()); } public void folderChanged(IContainer folder) { @@ -211,4 +244,26 @@ public class ReentrantLock { Assert.isNotNull(info, "Flush requested outside of resource lock"); //$NON-NLS-1$ info.flush(monitor); } + + /** + * Return <code>true</code> if the current thread is part of a CVS operation + * and the given resource is contained the scheduling rule held by that operation. + * @param resource + * @return + */ + public synchronized boolean isWithinActiveThread(IResource resource) { + ThreadInfo info = getThreadInfo(); + if (info == null) return false; + return info.ruleContains(resource); + } + + /** + * Record the ignore file change as part of the current operation. + * @param resource + */ + public synchronized void recordIgnoreFileChange(IFile resource) { + ThreadInfo info = getThreadInfo(); + Assert.isNotNull(info); + info.addChangedIgnoreFile(resource); + } } diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileChangeListener.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileChangeListener.java index 5c13b73da..cc7bf8927 100644 --- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileChangeListener.java +++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileChangeListener.java @@ -120,10 +120,26 @@ public class SyncFileChangeListener implements IResourceChangeListener { // } // } } - - if(isMetaFile(resource)) { + + if(isChangedByActiveCVSOperation(resource)) { + // This resource is modified by an active CVS operation + // (i.e. this is an intermitant delta) + // Notification is not required (or possible) in this case + // as the operation will peform the necessary notifications + // when it completes + if (resource.isTeamPrivateMember()) { + // No need to look at meta-files in this case + return false; + } else if (isIgnoreFile(resource)) { + // Add the ignore file to the current operation + // so it is handled when the operation completes + addIgnoreFileChangeToCVSOperation(resource); + } + // Must visit children in case there is an ignore file changed + return true; + } if(isMetaFile(resource)) { toBeNotified = handleChangedMetaFile(resource, kind); - } else if(name.equals(SyncFileWriter.IGNORE_FILE)) { + } else if(isIgnoreFile(resource)) { toBeNotified = handleChangedIgnoreFile(resource, kind); } else if (isExternalDeletion(resource, kind)) { toBeNotified = handleExternalDeletion(resource); @@ -153,6 +169,21 @@ public class SyncFileChangeListener implements IResourceChangeListener { /** * @param resource + */ + protected void addIgnoreFileChangeToCVSOperation(IResource resource) { + EclipseSynchronizer.getInstance().handleIgnoreFileChange(resource); + } + + /** + * @param resource + * @return + */ + protected boolean isChangedByActiveCVSOperation(IResource resource) { + return EclipseSynchronizer.getInstance().isWithinOperationScope(resource); + } + + /** + * @param resource * @return */ protected IContainer[] handleExternalDeletion(IResource resource) { @@ -232,6 +263,11 @@ public class SyncFileChangeListener implements IResourceChangeListener { } } + protected boolean isIgnoreFile(IResource resource) { + return resource.getType() == IResource.FILE && + resource.getName().equals(SyncFileWriter.IGNORE_FILE); + } + /* * It's a meta file if it's parent is a team-private CVS folder. */ diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileWriter.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileWriter.java index b305a97a0..774c30761 100644 --- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileWriter.java +++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileWriter.java @@ -380,7 +380,7 @@ public class SyncFileWriter { cvsSubDir.create(false /*don't force*/, true /*make local*/, null); cvsSubDir.setTeamPrivateMember(true); } - }, null); + }, folder, null); } return cvsSubDir; } catch (CoreException e) { @@ -484,7 +484,7 @@ public class SyncFileWriter { throw new CoreException(e.getStatus()); } } - }, null); + }, file, null); } catch (CoreException e) { throw CVSException.wrapException(e); } |