Bug 79913 - Concurrency support
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java b/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java
index e7a2b11..d44ca2d 100644
--- a/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java
@@ -31,6 +31,7 @@
 
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IExtensionPoint;
 import org.eclipse.core.runtime.IProgressMonitor;
@@ -42,7 +43,6 @@
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.jobs.ISchedulingRule;
 import org.eclipse.core.runtime.jobs.Job;
-import org.eclipse.debug.core.model.IDebugElement;
 import org.eclipse.debug.core.model.IProcess;
 import org.eclipse.debug.core.model.IValue;
 import org.eclipse.debug.core.model.RuntimeProcess;
@@ -290,9 +290,28 @@
 	private static final int NOTIFY_FILTERS = 0;
 	private static final int NOTIFY_EVENTS = 1;
 	
+	/**
+	 * An lock object used to indicate no locking was required.
+	 * @since 3.1
+	 */
+	private static Object NULL_LOCK = new Object();
+	
+	/**
+	 * Queue of debug events to fire to listeners.
+	 * @since 3.1
+	 */
 	private List fEventQueue = new ArrayList();
+	
+	/**
+	 * Job to fire events to listeners.
+	 * @since 3.1
+	 */
 	private EventDispatchJob fEventDispatchJob = new EventDispatchJob();
 	
+	/**
+	 * Event dispatch job
+	 * @since 3.1
+	 */
 	class EventDispatchJob extends Job {
 
 	    /**
@@ -1283,30 +1302,101 @@
 	}	
 	
 	/**
+	 * Returns a scheduling rule that can be used to schedule a job
+	 * that performs an access operation on the given debug artifact,
+	 * or <code>null</code> if none.
 	 * 
-	 * @param element
-	 * @return
+	 * @param element debug artifact 
+	 * @return a scheduling rule for an access job, or <code>null</code>
 	 * @since 3.1
 	 */
-	public static ISchedulingRule accessRule(IDebugElement element) {
-	    IDebugRuleFactory factory = (IDebugRuleFactory) element.getAdapter(IDebugRuleFactory.class);
-        if (factory != null) {
-            return factory.accessRule(element);
-        }
+	public static ISchedulingRule accessRule(Object element) {
+		if (element instanceof IAdaptable) {
+			IAdaptable adaptable = (IAdaptable) element;
+		    IDebugRuleFactory factory = (IDebugRuleFactory) adaptable.getAdapter(IDebugRuleFactory.class);
+	        if (factory != null) {
+	            return factory.accessRule(element);
+	        }			
+		}
+
         return null;   
 	}
 	
 	/**
-	 * 
-	 * @param element
-	 * @return
+	 * Obtains and returns a lock on the given debug artifact for an
+	 * access operation. This method blocks until the lock is available.
+	 * The lock must be released by the caller via <code>releaseLock(Object)</code>.
+	 *   
+	 * @param element debug artifact for which an access lock is required
+	 * @return access lock that must be subsequently released
 	 * @since 3.1
 	 */
-	public static ISchedulingRule modificationRule(IDebugElement element) {
-	    IDebugRuleFactory factory = (IDebugRuleFactory) element.getAdapter(IDebugRuleFactory.class);
-        if (factory != null) {
-            return factory.modificationRule(element);
-        }
+	public static Object getAccessLock(Object element) {
+		return getLock(accessRule(element));
+	}
+	
+	/**
+	 * Obtains and returns a lock on the given debug artifact for an
+	 * modify operation. This method blocks until the lock is available.
+	 * The lock must be released by the caller via <code>releaseLock(Object)</code>.
+	 *   
+	 * @param element debug artifact for which a modification lock is required
+	 * @return access lock that must be subsequently released
+	 * @since 3.1
+	 */
+	public static Object getModificationLock(Object element) {
+		return getLock(modificationRule(element));
+	}
+	
+	/**
+	 * Obtains a lock based on the given scheduling rule, in the
+	 * job manager, and returns an object representing a lock 
+	 * that must be subsequently released. If the lock is
+	 * <code>null</code>, no lock is obtained, but a dummy
+	 * lock object is still returned.
+	 *  
+	 * @param rule scheduling rule or <code>null</code>
+	 * @return returns a lock that must be subsequently released
+	 * @since 3.1
+	 */
+	private static Object getLock(ISchedulingRule rule) {
+		if (rule == null) {
+			return NULL_LOCK;
+		}
+		Platform.getJobManager().beginRule(rule, null);
+		return rule;
+	}
+	
+	/**
+	 * Releases the given lock. Must be called pairwise with
+	 * <code>getAccessLock(Object)</code> or <code>getModificationLock(Object)</code>.
+	 * 
+	 * @param lock lock to release
+	 * @since 3.1
+	 */
+	public static void releaseLock(Object lock) {
+		if (lock instanceof ISchedulingRule) {
+			Platform.getJobManager().endRule((ISchedulingRule)lock);
+		}
+	}
+	
+	/**
+	 * Returns a scheduling rule that can be used to schedule a job
+	 * that performs a modification operation on the given debug artifact,
+	 * or <code>null</code> if none.
+	 * 
+	 * @param element debug artifact 
+	 * @return a scheduling rule for a modification job, or <code>null</code>
+	 * @since 3.1
+	 */
+	public static ISchedulingRule modificationRule(Object element) {
+		if (element instanceof IAdaptable) {
+			IAdaptable adaptable = (IAdaptable) element;
+		    IDebugRuleFactory factory = (IDebugRuleFactory) adaptable.getAdapter(IDebugRuleFactory.class);
+	        if (factory != null) {
+	            return factory.modificationRule(element);
+	        }
+		}
         return null;   	    
 	}
 	
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DefaultDebugRuleFactory.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DefaultDebugRuleFactory.java
index 7f1a6a7..080d791 100644
--- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DefaultDebugRuleFactory.java
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DefaultDebugRuleFactory.java
@@ -75,17 +75,23 @@
     }
 
     /* (non-Javadoc)
-     * @see org.eclipse.debug.internal.core.IDebugRuleFactory#accessRule(org.eclipse.debug.core.model.IDebugElement)
+     * @see org.eclipse.debug.internal.core.IDebugRuleFactory#accessRule(java.lang.Object)
      */
-    public ISchedulingRule accessRule(IDebugElement debugElement) {
-        return new PessimisticRule(debugElement);
+    public ISchedulingRule accessRule(Object artifact) {
+    	if (artifact instanceof IDebugElement) {
+        	return new PessimisticRule((IDebugElement)artifact);
+    	}
+    	return null;
     }
 
     /* (non-Javadoc)
-     * @see org.eclipse.debug.internal.core.IDebugRuleFactory#modificationRule(org.eclipse.debug.core.model.IDebugElement)
+     * @see org.eclipse.debug.internal.core.IDebugRuleFactory#modificationRule(java.lang.Object)
      */
-    public ISchedulingRule modificationRule(IDebugElement debugElement) {
-        return new PessimisticRule(debugElement);
+    public ISchedulingRule modificationRule(Object artifact) {
+    	if (artifact instanceof IDebugElement) {
+        	return new PessimisticRule((IDebugElement)artifact);
+    	}
+    	return null;
     }
 
 }
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/IDebugRuleFactory.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/IDebugRuleFactory.java
index 83a8752..de0c95a 100644
--- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/IDebugRuleFactory.java
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/IDebugRuleFactory.java
@@ -11,7 +11,6 @@
 package org.eclipse.debug.internal.core;
 
 import org.eclipse.core.runtime.jobs.ISchedulingRule;
-import org.eclipse.debug.core.model.IDebugElement;
 
 /**
  * Creates scheduling rules for debug elements.
@@ -21,18 +20,20 @@
 public interface IDebugRuleFactory {
 
     /**
-     * Returns a scheduling rule used to schedule a job that accesses a debug element.
+     * Returns a scheduling rule used to schedule a job that accesses a debug artifact,
+     * or <code>null</code> if none.
      * 
-     * @param debugElement debug element to be accessed
-     * @return rule used to schedule a job that accesses a debug element.
+     * @param artifact debug artifact to be accessed
+     * @return rule used to schedule a job that accesses an artifact, or <code>null</code>
      */
-    public ISchedulingRule accessRule(IDebugElement debugElement);
+    public ISchedulingRule accessRule(Object artifact);
     
     /**
      * Returns a scheduling rule used to schedule a job that modifies the state of
-     * a debug element
-     * @param debugElement debug element to be modified
-     * @return rule used to schedule a job that modifies a debug element
+     * a debug artifact, or <code>null</code> if none.
+     * 
+     * @param artifact debug artifact to be modified
+     * @return rule used to schedule a job that modifies an artifact, or <code>null</code>
      */
-    public ISchedulingRule modificationRule(IDebugElement debugElement);
+    public ISchedulingRule modificationRule(Object artifact);
 }
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/LazyModelPresentation.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/LazyModelPresentation.java
index 2be3c4c..aaa5f1d 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/LazyModelPresentation.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/LazyModelPresentation.java
@@ -18,16 +18,11 @@
 
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IConfigurationElement;
-import org.eclipse.core.runtime.Platform;
-import org.eclipse.core.runtime.jobs.ISchedulingRule;
 import org.eclipse.debug.core.DebugPlugin;
-import org.eclipse.debug.core.model.IDebugElement;
-import org.eclipse.debug.core.model.IDebugTarget;
 import org.eclipse.debug.core.model.IMemoryBlock;
 import org.eclipse.debug.core.model.IStackFrame;
 import org.eclipse.debug.core.model.IThread;
 import org.eclipse.debug.core.model.IValue;
-import org.eclipse.debug.internal.core.IDebugRuleFactory;
 import org.eclipse.debug.internal.core.ListenerList;
 import org.eclipse.debug.internal.ui.views.memory.IMemoryBlockModelPresentation;
 import org.eclipse.debug.internal.ui.views.memory.IMemoryRenderingType;
@@ -122,40 +117,16 @@
 	 */
 	public String getText(Object element) {
 	    String text = null;
-	    ISchedulingRule rule = null;
+	    Object lock = null;
 	    try {
-		    rule = beginRule(element); 
+		    lock = DebugPlugin.getAccessLock(element); 
 			text = getPresentation().getText(element);
 	    } finally {
-	        endRule(rule);
+	        DebugPlugin.releaseLock(lock);
 	    }
 		return text;
 	}
 	
-	/**
-     * @param rule
-     */
-    private void endRule(ISchedulingRule rule) {
-        if (rule != null) {
-            Platform.getJobManager().endRule(rule);
-        }
-    }
-
-    /**
-     * @param element
-     * @return
-     */
-    private ISchedulingRule beginRule(Object object) {
-        ISchedulingRule rule = null;
-        if (object instanceof IDebugElement) {
-            rule = DebugPlugin.accessRule((IDebugElement)object);
-            if (rule != null) {
-                Platform.getJobManager().beginRule(rule, null);
-            }
-        }
-        return rule;
-    }
-
     /**
 	 * @see IDebugModelPresentation#computeDetail(IValue, IValueDetailListener)
 	 */
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ResumeActionDelegate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ResumeActionDelegate.java
index cc8864d..7730ff6 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ResumeActionDelegate.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ResumeActionDelegate.java
@@ -14,6 +14,7 @@
 import java.util.Iterator;
 
 import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
 import org.eclipse.debug.core.model.IDebugElement;
 import org.eclipse.debug.core.model.ISuspendResume;
 import org.eclipse.debug.core.model.IThread;
@@ -88,7 +89,9 @@
 	protected boolean isEnabledForAllThreads(Object element) {
 		if (element instanceof IDebugElement) {
             IDebugElement debugElement = (IDebugElement) element;
+            Object lock = null;
             try {
+            	lock = DebugPlugin.getAccessLock(debugElement);
                 IThread[] threads = debugElement.getDebugTarget().getThreads();
                 for (int i = 0; i < threads.length; i++) {
                     if (threads[i].canResume()) {
@@ -96,6 +99,8 @@
                     }
                 }
             } catch (DebugException e) {
+            } finally {
+            	DebugPlugin.releaseLock(lock);
             }
         }
 		return false;
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/AbstractDebugEventHandler.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/AbstractDebugEventHandler.java
index 94f0bbf..3b7d5fb 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/AbstractDebugEventHandler.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/AbstractDebugEventHandler.java
@@ -16,13 +16,10 @@
 
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.Status;
-import org.eclipse.core.runtime.jobs.ISchedulingRule;
 import org.eclipse.debug.core.DebugEvent;
 import org.eclipse.debug.core.DebugPlugin;
 import org.eclipse.debug.core.IDebugEventSetListener;
-import org.eclipse.debug.core.model.IDebugElement;
 import org.eclipse.debug.ui.AbstractDebugView;
 import org.eclipse.jface.viewers.IBasicPropertyConstants;
 import org.eclipse.jface.viewers.ITreeContentProvider;
@@ -34,8 +31,6 @@
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.progress.UIJob;
 
-import com.ibm.xslt4j.bcel.generic.FADD;
-
 /**
  * Handles debug events, updating a view and viewer.
  */
@@ -353,25 +348,5 @@
 	 */
 	protected void viewBecomesHidden() {
 	}
-
-	/**
-	 * 
-	 * @param element
-	 * @return
-	 * @since 3.1
-	 */
-	protected ISchedulingRule beginAccessRule(IDebugElement element) {
-	    ISchedulingRule rule = DebugPlugin.accessRule(element);
-	    if (rule != null) {
-	        Platform.getJobManager().beginRule(rule, null);
-	    }
-	    return rule;
-	}
-	
-	protected void endRule(ISchedulingRule rule) {
-	    if (rule != null) {
-	        Platform.getJobManager().endRule(rule);
-	    }
-	}
 }
 
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewEventHandler.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewEventHandler.java
index 657ba05..e8a3744 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewEventHandler.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewEventHandler.java
@@ -16,7 +16,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.eclipse.core.runtime.jobs.ISchedulingRule;
 import org.eclipse.debug.core.DebugEvent;
 import org.eclipse.debug.core.DebugException;
 import org.eclipse.debug.core.DebugPlugin;
@@ -586,14 +585,14 @@
             	case DebugEvent.SUSPEND:
             	    if (source instanceof IThread) {
             	        IThread thread = (IThread)source;
-            		    ISchedulingRule rule = null;
+            		    Object lock = null;
             		    try {
-            		        rule = beginAccessRule(thread);
+            		        lock = DebugPlugin.getAccessLock(thread);
             		        IStackFrame frame = thread.getTopStackFrame();
             		        queueData(frame);
             		    } catch (DebugException e) {
             		    } finally {
-            		        endRule(rule);
+            		        DebugPlugin.releaseLock(lock);
             		    }
             	    }
             	    break;
@@ -601,10 +600,10 @@
             		if (source instanceof IThread) {
             			// When a thread resumes, try to select another suspended thread
             			// in the same target.
-            		    ISchedulingRule rule = null;
+            		    Object lock = null;
             			try {
             			    IDebugTarget target = ((IThread) source).getDebugTarget();
-            			    rule = beginAccessRule(target);
+            			    lock = DebugPlugin.getAccessLock(target);
             				IThread[] threads= target.getThreads();
             				for (int j = 0; j < threads.length; j++) {
             					IStackFrame frame = threads[j].getTopStackFrame();
@@ -615,7 +614,7 @@
             				}
             			} catch (DebugException e) {
             			} finally {
-            			    endRule(rule);
+            			    DebugPlugin.releaseLock(lock);
             			}
             		}
             		break;