From 9af018f65eceb6929641745f58132cd64bc3a924 Mon Sep 17 00:00:00 2001 From: Michael Valenta Date: Wed, 10 Sep 2003 17:58:06 +0000 Subject: Storing initial locking work on a branch so it is not lost --- .../eclipse/team/core/ReadOnlySchedulingRule.java | 41 ++++++++++++++ .../core/RepositoryProviderSchedulingRule.java | 65 ++++++++++++++++++++++ .../team/core/WriteResourcesSchedulingRule.java | 39 +++++++++++++ .../eclipse/team/core/WriteSyncSchedulingRule.java | 26 +++++++++ .../ccvs/core/syncinfo/RemoteTagSynchronizer.java | 58 +++++++++++++++---- .../ui/operations/CheckoutProjectOperation.java | 27 +++++++-- .../ui/operations/RepositoryProviderOperation.java | 46 ++++++++++----- .../internal/ccvs/ui/operations/TagOperation.java | 13 ++++- 8 files changed, 284 insertions(+), 31 deletions(-) create mode 100644 bundles/org.eclipse.team.core/src/org/eclipse/team/core/ReadOnlySchedulingRule.java create mode 100644 bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProviderSchedulingRule.java create mode 100644 bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteResourcesSchedulingRule.java create mode 100644 bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteSyncSchedulingRule.java 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 000000000..d4e6777c5 --- /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 000000000..b85594753 --- /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 000000000..72d07d96b --- /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 000000000..6bb278691 --- /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 9046b4f78..01c5f336a 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.List; 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 @@ public class RemoteTagSynchronizer extends CVSRemoteSynchronizer { 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 @@ public class RemoteTagSynchronizer extends CVSRemoteSynchronizer { 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 6a2f7c810..1443e9ca4 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.core.runtime.jobs.MultiRule; 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 @@ public abstract class CheckoutProjectOperation extends CheckoutOperation { } 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 @@ public abstract class CheckoutProjectOperation extends CheckoutOperation { } 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 5b8f0715b..38544329a 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.List; 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 @@ import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot; * Performs a cvs operation on multiple repository providers */ public abstract class RepositoryProviderOperation extends CVSOperation { - + private IResource[] resources; /** @@ -60,7 +63,12 @@ public abstract class RepositoryProviderOperation extends CVSOperation { 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 @@ public abstract class RepositoryProviderOperation extends CVSOperation { * 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 @@ public abstract class RepositoryProviderOperation extends CVSOperation { * @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 @@ public abstract class RepositoryProviderOperation extends CVSOperation { /* * 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