Storing initial locking work on a branch so it is not lost
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ReadOnlySchedulingRule.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ReadOnlySchedulingRule.java new file mode 100644 index 0000000..d4e6777 --- /dev/null +++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ReadOnlySchedulingRule.java
@@ -0,0 +1,41 @@ +/******************************************************************************* + * 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.core; + +import org.eclipse.core.runtime.jobs.ISchedulingRule; + +/** + * The purpose of ReadOnlySchedulingRule is to prevent Write*SchedulingRule threads + * from running concurrently with the read only thread. It does not prevent the read only + * thread from modifying resources or sync info. Also, it does not prevent other threads from + * modifying resources. It is the reponsibility of clients to + * choose the proper scheduling rule up front given the above mentioned characteristics. + */ +public class ReadOnlySchedulingRule extends RepositoryProviderSchedulingRule { + public ReadOnlySchedulingRule(RepositoryProvider provider) { + super(provider); + } + public boolean contains(ISchedulingRule rule) { + if (rule instanceof WriteSyncSchedulingRule) { + // Read rules cannot contain write rules + RepositoryProvider otherProvider = ((RepositoryProviderSchedulingRule)rule).getProvider(); + if (otherProvider == getProvider()) return false; + } + return super.contains(rule); + } + public boolean isConflicting(ISchedulingRule rule) { + if (rule instanceof ReadOnlySchedulingRule) { + // Read rules never conflict with other reads + return false; + } + return super.isConflicting(rule); + } +}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProviderSchedulingRule.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProviderSchedulingRule.java new file mode 100644 index 0000000..b855947 --- /dev/null +++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProviderSchedulingRule.java
@@ -0,0 +1,65 @@ +/******************************************************************************* + * 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.core; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.jobs.ISchedulingRule; + +/** + * This class provides a scheduling rule that can be used to ensure exclusive access + * to the resources of a project under the control of a repository provider. When a + * RepositoryProviderSchedulingRule is held, the holding thread may still modify resources + * within the project. However, resources in other projects are not contained by a + * RepositoryProviderSchedulingRule. + */ +public class RepositoryProviderSchedulingRule implements ISchedulingRule { + + private RepositoryProvider provider; + + public RepositoryProviderSchedulingRule(RepositoryProvider provider) { + this.provider = provider; + } + + public RepositoryProvider getProvider() { + return provider; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule) + */ + public boolean contains(ISchedulingRule rule) { + if (rule instanceof RepositoryProviderSchedulingRule) { + // In general, RepositoryProviderSchedulingRule can contain other RepositoryProviderSchedulingRules + RepositoryProvider otherProvider = ((RepositoryProviderSchedulingRule)rule).getProvider(); + if (otherProvider == getProvider()) return true; + } else if (rule instanceof IResource) { + // Rules can contain resource modifications in the provider's project + return getProvider().getProject().contains(rule); + } + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule) + */ + public boolean isConflicting(ISchedulingRule rule) { + if (rule instanceof RepositoryProviderSchedulingRule) { + // In general, RepositoryProviderSchedulingRules on the same provider conflict + if (((RepositoryProviderSchedulingRule)rule).getProvider() == getProvider()) { + // We can use identity for the check since a RepositoryProvider + // is only created once for each project + return true; + } + } + return false; + } + +}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteResourcesSchedulingRule.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteResourcesSchedulingRule.java new file mode 100644 index 0000000..72d07d9 --- /dev/null +++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteResourcesSchedulingRule.java
@@ -0,0 +1,39 @@ +/******************************************************************************* + * 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.core; + +import org.eclipse.core.runtime.jobs.ISchedulingRule; + +/** + * The purpose of the WriteResourceSchedulingRule is to prevent concurrent modification + * of the sync info of a repository provider as well as the resources within the provider's + * project. When a write resource rule is held, no other + * RepositoryProviderSchedulingRules will be allowed access until the write resource rule + * is released. Also, no threads requesting a rule on the provider's project or any of is + * children will be allwed to run until the write lock is released. + */ +public class WriteResourcesSchedulingRule extends WriteSyncSchedulingRule { + public WriteResourcesSchedulingRule(RepositoryProvider provider) { + super(provider); + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule) + */ + public boolean isConflicting(ISchedulingRule rule) { + if (getProvider().getProject().contains(rule)) { + // a write resource rule conflicts with modifications on the provider's project + return true; + } + return super.isConflicting(rule); + } + +}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteSyncSchedulingRule.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteSyncSchedulingRule.java new file mode 100644 index 0000000..6bb2786 --- /dev/null +++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteSyncSchedulingRule.java
@@ -0,0 +1,26 @@ +/******************************************************************************* + * 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.core; + + +/** + * The purpose of the WriteSyncSchedulingRule is to prevent concurrent modification + * of the sync info of a repository provider. When a write sync rule is held, no other + * RepositoryProviderSchedulingRules will be allowed access until the write sync rule + * is released. However, the write sync rule does not prevent the thread that holds the lock + * from modifying the workspace. This is important since the sync info may be contained in + * resources or in the ISynchronizer which noth require write access to the workspace tree. + */ +public class WriteSyncSchedulingRule extends RepositoryProviderSchedulingRule { + public WriteSyncSchedulingRule(RepositoryProvider provider) { + super(provider); + } +}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/RemoteTagSynchronizer.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/RemoteTagSynchronizer.java index 9046b4f..01c5f33 100644 --- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/RemoteTagSynchronizer.java +++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/RemoteTagSynchronizer.java
@@ -19,12 +19,15 @@ import java.util.Map; import java.util.Set; +import org.eclipse.core.internal.jobs.JobManager; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; +import org.eclipse.team.core.ReadOnlySchedulingRule; +import org.eclipse.team.core.RepositoryProvider; import org.eclipse.team.core.TeamException; import org.eclipse.team.core.sync.IRemoteResource; import org.eclipse.team.internal.ccvs.core.CVSException; @@ -232,19 +235,29 @@ for (int i = 0; i < resources.length; i++) { IResource resource = resources[i]; - monitor.setTaskName(Policy.bind("RemoteTagSynchronizer.0", resource.getFullPath().makeRelative().toString())); //$NON-NLS-1$ - - // build the remote tree only if an initial tree hasn't been provided - IRemoteResource tree = buildRemoteTree(resource, depth, cacheFileContentsHint, Policy.subMonitorFor(monitor, 70)); - - // update the known remote handles - IProgressMonitor sub = Policy.infiniteSubMonitorFor(monitor, 30); + boolean lockObtained = false; try { - sub.beginTask(null, 512); - //removeSyncBytes(resource, IResource.DEPTH_INFINITE); - collectChanges(resource, tree, depth, sub); + lockObtained = beginRefresh(resource); + if (lockObtained) { + monitor.setTaskName(Policy.bind("RemoteTagSynchronizer.0", resource.getFullPath().makeRelative().toString())); //$NON-NLS-1$ + + // build the remote tree only if an initial tree hasn't been provided + IRemoteResource tree = buildRemoteTree(resource, depth, cacheFileContentsHint, Policy.subMonitorFor(monitor, 70)); + + // update the known remote handles + IProgressMonitor sub = Policy.infiniteSubMonitorFor(monitor, 30); + try { + sub.beginTask(null, 512); + //removeSyncBytes(resource, IResource.DEPTH_INFINITE); + collectChanges(resource, tree, depth, sub); + } finally { + sub.done(); + } + } } finally { - sub.done(); + if (lockObtained) { + endRefresh(); + } } } } finally { @@ -255,6 +268,29 @@ return changes; } + /* + * Obtain a rea-only scheduling rule for the given resource. + * Return true if the rule was obtained and false otherwise. + * If false is returned, do not refresh the resource but continue + * with the others. + */ + private boolean beginRefresh(IResource resource) { + RepositoryProvider provider = RepositoryProvider.getProvider(resource.getProject()); + if (provider == null) return false; + JobManager.getInstance().beginRule(new ReadOnlySchedulingRule(provider)); + // It's possible that the provider was unmapped before we obtained the rule + RepositoryProvider newProvider = RepositoryProvider.getProvider(resource.getProject()); + if (newProvider != provider) { + JobManager.getInstance().endRule(); + return false; + } + return true; + } + + private void endRefresh() { + JobManager.getInstance().endRule(); + } + /** * Build a remote tree for the given parameters. */
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CheckoutProjectOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CheckoutProjectOperation.java index 6a2f7c8..1443e9c 100644 --- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CheckoutProjectOperation.java +++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CheckoutProjectOperation.java
@@ -32,6 +32,7 @@ import org.eclipse.swt.widgets.Shell; import org.eclipse.team.core.RepositoryProvider; import org.eclipse.team.core.TeamException; +import org.eclipse.team.core.WriteResourcesSchedulingRule; import org.eclipse.team.internal.ccvs.core.CVSException; import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin; import org.eclipse.team.internal.ccvs.core.CVSStatus; @@ -168,13 +169,32 @@ } private ISchedulingRule getSchedulingRule(IProject[] projects) { - if (projects.length == 1) { - return projects[0]; + ISchedulingRule[] rules = getSchedulingRules(projects); + if (rules.length == 1) { + return rules[0]; } else { - return new MultiRule(projects); + return new MultiRule(rules); } } + /* + * Use a provider scheduling rule for those projects that are mapped to + * a provider. + */ + private ISchedulingRule[] getSchedulingRules(IProject[] projects) { + ISchedulingRule[] rules = new ISchedulingRule[projects.length]; + for (int i = 0; i < projects.length; i++) { + IProject project = projects[i]; + RepositoryProvider provider = RepositoryProvider.getProvider(project); + if (provider != null) { + rules[i] = new WriteResourcesSchedulingRule(provider); + } else { + rules[i] = project; + } + } + return rules; + } + private IStatus performCheckout(Session session, ICVSRemoteFolder resource, IProject[] targetProjects, boolean sendModuleName, IProgressMonitor pm) throws CVSException { // Set the task name of the progress monitor to let the user know // which project we're on. Don't use subTask since that will be @@ -383,7 +403,6 @@ } protected String getOverwritePromptMessage(ICVSRemoteFolder remoteFolder, IProject project) { - File localLocation = getFileLocation(project); if(project.exists()) { return Policy.bind("CheckoutOperation.thisResourceExists", project.getName(), getRemoteModuleName(remoteFolder));//$NON-NLS-1$ } else {
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/RepositoryProviderOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/RepositoryProviderOperation.java index 5b8f071..3854432 100644 --- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/RepositoryProviderOperation.java +++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/RepositoryProviderOperation.java
@@ -17,12 +17,15 @@ import java.util.Map; import java.util.Set; +import org.eclipse.core.internal.jobs.JobManager; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.swt.widgets.Shell; import org.eclipse.team.core.RepositoryProvider; +import org.eclipse.team.core.WriteResourcesSchedulingRule; import org.eclipse.team.internal.ccvs.core.CVSException; import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin; import org.eclipse.team.internal.ccvs.core.CVSTeamProvider; @@ -36,7 +39,7 @@ * Performs a cvs operation on multiple repository providers */ public abstract class RepositoryProviderOperation extends CVSOperation { - + private IResource[] resources; /** @@ -60,7 +63,12 @@ CVSTeamProvider provider = (CVSTeamProvider)iterator.next(); List list = (List)table.get(provider); IResource[] providerResources = (IResource[])list.toArray(new IResource[list.size()]); - execute(provider, providerResources, subMonitor); + try { + JobManager.getInstance().beginRule(getSchedulingRule(provider)); + execute(provider, providerResources, subMonitor); + } finally { + JobManager.getInstance().endRule(); + } } } @@ -69,16 +77,16 @@ * Helper method. Return a Map mapping provider to a list of resources * shared with that provider. */ - private Map getProviderMapping(IResource[] resources) { + private Map getProviderMapping(IResource[] localResources) { Map result = new HashMap(); - for (int i = 0; i < resources.length; i++) { - RepositoryProvider provider = RepositoryProvider.getProvider(resources[i].getProject(), CVSProviderPlugin.getTypeId()); + for (int i = 0; i < localResources.length; i++) { + RepositoryProvider provider = RepositoryProvider.getProvider(localResources[i].getProject(), CVSProviderPlugin.getTypeId()); List list = (List)result.get(provider); if (list == null) { list = new ArrayList(); result.put(provider, list); } - list.add(resources[i]); + list.add(localResources[i]); } return result; } @@ -107,12 +115,12 @@ * @throws CVSException * @throws InterruptedException */ - protected abstract void execute(CVSTeamProvider provider, IResource[] resources, IProgressMonitor monitor) throws CVSException, InterruptedException; + protected abstract void execute(CVSTeamProvider provider, IResource[] localResources, IProgressMonitor monitor) throws CVSException, InterruptedException; - protected ICVSResource[] getCVSArguments(IResource[] resources) { - ICVSResource[] cvsResources = new ICVSResource[resources.length]; + protected ICVSResource[] getCVSArguments(IResource[] localResources) { + ICVSResource[] cvsResources = new ICVSResource[localResources.length]; for (int i = 0; i < cvsResources.length; i++) { - cvsResources[i] = CVSWorkspaceRoot.getCVSResourceFor(resources[i]); + cvsResources[i] = CVSWorkspaceRoot.getCVSResourceFor(localResources[i]); } return cvsResources; } @@ -120,10 +128,10 @@ /* * Get the arguments to be passed to a commit or update */ - protected String[] getStringArguments(IResource[] resources) throws CVSException { - List arguments = new ArrayList(resources.length); - for (int i=0;i<resources.length;i++) { - IPath cvsPath = resources[i].getFullPath().removeFirstSegments(1); + protected String[] getStringArguments(IResource[] localResources) throws CVSException { + List arguments = new ArrayList(localResources.length); + for (int i=0;i<localResources.length;i++) { + IPath cvsPath = localResources[i].getFullPath().removeFirstSegments(1); if (cvsPath.segmentCount() == 0) { arguments.add(Session.CURRENT_LOCAL_FOLDER); } else { @@ -151,4 +159,14 @@ return workspaceRoot.getLocalRoot(); } + /** + * Return the scheduling rule to be used for the given provider. The default is + * write access to both sync info and resources. Subclasses may override. + * @param provider the repository provider + * @return the scheduling rule to be used for this operation on the provider + */ + protected ISchedulingRule getSchedulingRule(RepositoryProvider provider) { + return new WriteResourcesSchedulingRule(provider); + } + }
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/TagOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/TagOperation.java index 87e22bf..19ae11a 100644 --- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/TagOperation.java +++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/TagOperation.java
@@ -16,8 +16,10 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.swt.widgets.Shell; +import org.eclipse.team.core.RepositoryProvider; +import org.eclipse.team.core.WriteSyncSchedulingRule; import org.eclipse.team.internal.ccvs.core.CVSException; import org.eclipse.team.internal.ccvs.core.CVSTag; import org.eclipse.team.internal.ccvs.core.CVSTeamProvider; @@ -57,7 +59,6 @@ */ protected String getErrorMessage(IStatus[] problems, int operationCount) { // We accumulated 1 status per resource above. - MultiStatus combinedStatus; if(operationCount == 1) { return Policy.bind("TagAction.tagProblemsMessage"); //$NON-NLS-1$ } else { @@ -124,4 +125,12 @@ protected String getTaskName() { return Policy.bind("TagFromWorkspace.taskName"); //$NON-NLS-1$ } + /* (non-Javadoc) + * @see org.eclipse.team.internal.ccvs.ui.operations.RepositoryProviderOperation#getSchedulingRule(org.eclipse.team.core.RepositoryProvider) + */ + protected ISchedulingRule getSchedulingRule(RepositoryProvider provider) { + // The tag operation writes sync info + return new WriteSyncSchedulingRule(provider); + } + }