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 9bf9d4e..e7a2b11 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
@@ -40,13 +40,16 @@
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.Plugin;
 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;
 import org.eclipse.debug.internal.core.BreakpointManager;
 import org.eclipse.debug.internal.core.DebugCoreMessages;
 import org.eclipse.debug.internal.core.ExpressionManager;
+import org.eclipse.debug.internal.core.IDebugRuleFactory;
 import org.eclipse.debug.internal.core.LaunchManager;
 import org.eclipse.debug.internal.core.ListenerList;
 import org.eclipse.debug.internal.core.LogicalStructureManager;
@@ -286,6 +289,52 @@
 	 */
 	private static final int NOTIFY_FILTERS = 0;
 	private static final int NOTIFY_EVENTS = 1;
+	
+	private List fEventQueue = new ArrayList();
+	private EventDispatchJob fEventDispatchJob = new EventDispatchJob();
+	
+	class EventDispatchJob extends Job {
+
+	    /**
+         * Creates a new event dispatch job.
+         */
+        public EventDispatchJob() {
+            super("Debug Event Dispatch");
+            setPriority(Job.INTERACTIVE);
+        }
+        /* (non-Javadoc)
+         * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
+         */
+        protected IStatus run(IProgressMonitor monitor) {
+            
+            while (!fEventQueue.isEmpty()) {
+                DebugEvent[] events = null;
+	            synchronized (fEventQueue) {
+	                if (!fEventQueue.isEmpty()) {
+	                    events = (DebugEvent[]) fEventQueue.remove(0);
+	                }
+	            }
+	            if (events != null) {
+	                getEventNotifier().dispatch(events);
+	            }
+            }
+            return Status.OK_STATUS;
+        }
+	    
+        /* (non-Javadoc)
+         * @see org.eclipse.core.runtime.jobs.Job#shouldRun()
+         */
+        public boolean shouldRun() {
+            return shouldSchedule();
+        }
+        /* (non-Javadoc)
+         * @see org.eclipse.core.internal.jobs.InternalJob#shouldSchedule()
+         */
+        public boolean shouldSchedule() {
+            return !(isShuttingDown() || fEventListeners == null);
+        }
+        
+	}
 
 	/**
 	 * Returns the singleton instance of the debug plug-in.
@@ -352,7 +401,10 @@
 	public void fireDebugEventSet(DebugEvent[] events) {
 		if (isShuttingDown() || events == null || fEventListeners == null)
 			return;
-		getEventNotifier().dispatch(events);
+		synchronized (fEventQueue) {
+			fEventQueue.add(events);
+		}
+		fEventDispatchJob.schedule();
 	}
 	
 	/**
@@ -1229,6 +1281,35 @@
 		
 		return res;
 	}	
+	
+	/**
+	 * 
+	 * @param element
+	 * @return
+	 * @since 3.1
+	 */
+	public static ISchedulingRule accessRule(IDebugElement element) {
+	    IDebugRuleFactory factory = (IDebugRuleFactory) element.getAdapter(IDebugRuleFactory.class);
+        if (factory != null) {
+            return factory.accessRule(element);
+        }
+        return null;   
+	}
+	
+	/**
+	 * 
+	 * @param element
+	 * @return
+	 * @since 3.1
+	 */
+	public static ISchedulingRule modificationRule(IDebugElement element) {
+	    IDebugRuleFactory factory = (IDebugRuleFactory) element.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/DebugElement.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugElement.java
new file mode 100644
index 0000000..d3a0f9c
--- /dev/null
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugElement.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 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.debug.internal.core;
+
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IDebugElement;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.IStepFilters;
+
+/**
+ * Implementation of common function for debug elements.
+ * <p>
+ * Clients may subclass this class.
+ * </p>
+ * @since 3.1
+ */
+public abstract class DebugElement extends PlatformObject implements IDebugElement {
+    
+    private IDebugTarget fTarget;
+
+    /**
+     * Constructs a debug element referring to an artifact in the given
+     * debug target.
+     * 
+     * @param target debug target containing this element
+     */
+    public DebugElement(IDebugTarget target) {
+        fTarget = target;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget()
+     */
+    public IDebugTarget getDebugTarget() {
+        return fTarget;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
+     */
+    public ILaunch getLaunch() {
+        return getDebugTarget().getLaunch();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+     */
+    public Object getAdapter(Class adapter) {
+		if (adapter == IDebugElement.class) {
+			return this;
+		}
+		if (adapter == IStepFilters.class) {
+			return getDebugTarget();
+		}
+		if (adapter == IDebugTarget.class) {
+			return getDebugTarget();
+		}
+		if (adapter == IDebugRuleFactory.class) {
+		    return DefaultDebugRuleFactory.getDefault(); 
+		}
+		if (adapter == ILaunch.class) {
+		    return getLaunch();
+		}
+		if (adapter == IProcess.class) {
+		    return getDebugTarget().getProcess();
+		}
+		return super.getAdapter(adapter);
+    }
+
+	/**
+	 * Fires a debug event.
+	 * 
+	 * @param event debug event to fire
+	 */
+	protected void fireEvent(DebugEvent event) {
+		DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] {event});
+	}    
+
+	/**
+	 * Fires a change event for this debug element
+	 * with the specified detail code.
+	 * 
+	 * @param detail detail code for the change event,
+	 *  such as <code>DebugEvent.STATE</code> or <code>DebugEvent.CONTENT</code>
+	 */
+	public void fireChangeEvent(int detail) {
+		fireEvent(new DebugEvent(this, DebugEvent.CHANGE, detail));
+	}
+	
+	/**
+	 * Fires a creation event for this debug element.
+	 */
+	protected void fireCreationEvent() {
+		fireEvent(new DebugEvent(this, DebugEvent.CREATE));
+	}	
+	
+	/**
+	 * Fires a resume for this debug element with
+	 * the specified detail code.
+	 * 
+	 * @param detail detail code for the resume event, such 
+	 *  as <code>DebugEvent.STEP_OVER</code>
+	 */
+	protected void fireResumeEvent(int detail) {
+		fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail));
+	}
+	
+	/**
+	 * Fires a suspend event for this debug element with
+	 * the specified detail code.
+	 * 
+	 * @param detail detail code for the suspend event, such
+	 *  as <code>DebugEvent.BREAKPOINT</code>
+	 */
+	protected void fireSuspendEvent(int detail) {
+		fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail));
+	}	
+	
+	/**
+	 * Fires a terminate event for this debug element.
+	 */
+	protected void fireTerminateEvent() {
+		fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
+	}	
+}
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
new file mode 100644
index 0000000..7f1a6a7
--- /dev/null
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DefaultDebugRuleFactory.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 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.debug.internal.core;
+
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.debug.core.model.IDebugElement;
+import org.eclipse.debug.core.model.IDebugTarget;
+
+/**
+ * Default rule factory returns rules that serialzie access and modification of
+ * debug elements.
+ * 
+ * @since 3.1
+ */
+public class DefaultDebugRuleFactory implements IDebugRuleFactory {
+    
+    private static IDebugRuleFactory fgDefault;
+    
+    class PessimisticRule implements ISchedulingRule {
+        
+        private IDebugElement fElement;
+        
+        PessimisticRule(IDebugElement element) {
+            fElement = element;
+        }
+
+        /* (non-Javadoc)
+         * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+         */
+        public boolean contains(ISchedulingRule rule) {
+            if (rule instanceof PessimisticRule) {
+                return isSameTarget((PessimisticRule) rule, this);
+            }
+            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 PessimisticRule) {
+                return isSameTarget((PessimisticRule) rule, this);
+            }
+            return false;
+        }
+        
+        private boolean isSameTarget(PessimisticRule a, PessimisticRule b) {
+            IDebugTarget t1 = a.fElement.getDebugTarget();
+            IDebugTarget t2 = b.fElement.getDebugTarget();
+            return t1 != null && t1.equals(t2);
+        }
+        
+    }
+    
+    public static IDebugRuleFactory getDefault() {
+        if (fgDefault == null) {
+            fgDefault = new DefaultDebugRuleFactory();
+        }
+        return fgDefault;
+    }
+
+    /**
+     * Constucts a rule factory.
+     */
+    DefaultDebugRuleFactory() {
+        super();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.debug.internal.core.IDebugRuleFactory#accessRule(org.eclipse.debug.core.model.IDebugElement)
+     */
+    public ISchedulingRule accessRule(IDebugElement debugElement) {
+        return new PessimisticRule(debugElement);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.debug.internal.core.IDebugRuleFactory#modificationRule(org.eclipse.debug.core.model.IDebugElement)
+     */
+    public ISchedulingRule modificationRule(IDebugElement debugElement) {
+        return new PessimisticRule(debugElement);
+    }
+
+}
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
new file mode 100644
index 0000000..83a8752
--- /dev/null
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/IDebugRuleFactory.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 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.debug.internal.core;
+
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.debug.core.model.IDebugElement;
+
+/**
+ * Creates scheduling rules for debug elements.
+ * 
+ * @since 3.1
+ */
+public interface IDebugRuleFactory {
+
+    /**
+     * Returns a scheduling rule used to schedule a job that accesses a debug element.
+     * 
+     * @param debugElement debug element to be accessed
+     * @return rule used to schedule a job that accesses a debug element.
+     */
+    public ISchedulingRule accessRule(IDebugElement debugElement);
+    
+    /**
+     * 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
+     */
+    public ISchedulingRule modificationRule(IDebugElement debugElement);
+}
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 b59c5e3..2be3c4c 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,10 +18,16 @@
 
 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;
@@ -99,17 +105,58 @@
 	 * @see IDebugModelPresentation#getImage(Object)
 	 */
 	public Image getImage(Object element) {
-		return getPresentation().getImage(element);
+	    Image image = null;
+	    // TODO: causes UI job blocking dialog to appear
+//	    ISchedulingRule rule = null;
+//	    try {
+//		    rule = beginRule(element);
+		    image = getPresentation().getImage(element);
+//	    } finally {
+//	        endRule(rule);
+//	    }
+	    return image;
 	}
 
 	/**
 	 * @see IDebugModelPresentation#getText(Object)
 	 */
 	public String getText(Object element) {
-		return getPresentation().getText(element);
+	    String text = null;
+	    ISchedulingRule rule = null;
+	    try {
+		    rule = beginRule(element); 
+			text = getPresentation().getText(element);
+	    } finally {
+	        endRule(rule);
+	    }
+		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)
 	 */
 	public void computeDetail(IValue value, IValueDetailListener listener) {
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/AbstractDebugActionDelegate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/AbstractDebugActionDelegate.java
index 77d8399..7326ebe 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/AbstractDebugActionDelegate.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/AbstractDebugActionDelegate.java
@@ -16,8 +16,12 @@
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.MultiStatus;
+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.DebugException;
+import org.eclipse.debug.core.model.IDebugElement;
+import org.eclipse.debug.internal.core.IDebugRuleFactory;
 import org.eclipse.debug.internal.ui.DebugUIPlugin;
 import org.eclipse.debug.ui.IDebugUIConstants;
 import org.eclipse.jface.action.IAction;
@@ -65,14 +69,9 @@
 	 */
 	protected IWorkbenchWindow fWindow;
 	
-	/**
-	 * Background job for this action, or <code>null</code> if none.
-	 */
-	private DebugRequestJob fBackgroundJob = null;
-	
 	class DebugRequestJob extends Job {
 	    
-	    private Object[] fElements = null;
+	    private Object fElement;
 
 	    /** 
 	     * Constructs a new job to perform a debug request (for example, step)
@@ -80,35 +79,22 @@
 	     * 
 	     * @param name job name
 	     */
-	    public DebugRequestJob(String name) {
+	    public DebugRequestJob(String name, Object target) {
 	        super(name);
 	        setPriority(Job.INTERACTIVE);
+	        fElement = target;
 	    }
 	    
         /* (non-Javadoc)
          * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
          */
         protected IStatus run(IProgressMonitor monitor) {
-		    MultiStatus status= 
-				new MultiStatus(DebugUIPlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, getStatusMessage(), null);
-		    for (int i = 0; i < fElements.length; i++) {
-				Object element= fElements[i];
-				try {
-					doAction(element);
-				} catch (DebugException e) {
-					status.merge(e.getStatus());
-				}
+			try {
+				doAction(fElement);
+			} catch (DebugException e) {
+				return e.getStatus();
 			}
-			return status;
-        }
-        
-        /**
-         * Sets the selection to operate on.
-         * 
-         * @param elements
-         */
-        public void setTargets(Object[] elements) {
-            fElements = elements;
+			return Status.OK_STATUS;
         }
 	    
 	}
@@ -128,7 +114,6 @@
 		if (getWindow() != null) {
 			getWindow().getSelectionService().removeSelectionListener(IDebugUIConstants.ID_DEBUG_VIEW, this);
 		}
-		fBackgroundJob = null;
 	}
 
 	/* (non-Javadoc)
@@ -163,11 +148,21 @@
 	private void runInBackground(IAction action, IStructuredSelection selection) {
 	    // disable the action
 	    action.setEnabled(false);
-	    if (fBackgroundJob == null) {
-			fBackgroundJob = new DebugRequestJob(action.getText());
+	    Iterator iterator = selection.iterator();
+	    while (iterator.hasNext()) {
+	        Object target = iterator.next();
+	        ISchedulingRule rule = null;
+	        if (target instanceof IDebugElement) {
+                IDebugElement element = (IDebugElement) target;
+                IDebugRuleFactory ruleFactory = (IDebugRuleFactory) element.getAdapter(IDebugRuleFactory.class);
+                if (ruleFactory != null) {
+                    rule = ruleFactory.modificationRule(element);
+                }
+            }
+	        DebugRequestJob job = new DebugRequestJob(action.getText(), target);
+	        job.setRule(rule);
+	        job.schedule();
 	    }
-	    fBackgroundJob.setTargets(selection.toArray());
-		fBackgroundJob.schedule();
 	}
 	
 	/**
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 769f3db..94f0bbf 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,10 +16,13 @@
 
 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;
@@ -31,6 +34,8 @@
 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.
  */
@@ -42,11 +47,16 @@
 	private AbstractDebugView fView;
 	
 	/**
-	 * Queued debug event sets (arrays of events) to process, or <code>null</code> if none.
+	 * Queued debug event sets (arrays of events) to process.
 	 */
 	private List fEventSetQueue = new ArrayList();
 	
 	/**
+	 * Queued data associated with event sets. Entries may be <code>null</code>.
+	 */
+	private List fDataQueue = new ArrayList();
+	
+	/**
 	 * Update job 
 	 */
 	private EventProcessingJob fUpdateJob = new EventProcessingJob();
@@ -56,6 +66,8 @@
 	 */
 	protected static final DebugEvent[] EMPTY_EVENT_SET = new DebugEvent[0];
 	
+	private Object NULL = new Object();
+	
 	/**
 	 * Job to dispatch debug event sets
 	 */
@@ -75,6 +87,7 @@
             // to avoid blocking the UI thread, process a max of 50 event sets at once
             while (more && (count < 50)) {
                 DebugEvent[] eventSet = null;
+                Object data = null;
 			    synchronized (fEventSetQueue) {
 			        if (fEventSetQueue.isEmpty()) {
 			            return Status.OK_STATUS;
@@ -82,11 +95,17 @@
 			        eventSet = (DebugEvent[]) fEventSetQueue.remove(0);
 			        more = !fEventSetQueue.isEmpty();
 			    }
+			    synchronized (fDataQueue) {
+			        data = fDataQueue.remove(0);
+			        if (data == NULL) {
+			            data = null;
+			        }
+			    }
 				if (isAvailable()) {
 					if (isViewVisible()) {
-						doHandleDebugEvents(eventSet);
+						doHandleDebugEvents(eventSet, data);
 					}
-					updateForDebugEvents(eventSet);
+					updateForDebugEvents(eventSet, data);
 				}
 				count++;
             }
@@ -132,13 +151,32 @@
 		if (events.length == 0) {
 		    return;
 		}
+		events = doPreprocessEvents(events);
+		if (events.length == 0) {
+		    return;
+		}
 		// add the event set to the queue and schedule update
 		synchronized (fEventSetQueue) {
 		    fEventSetQueue.add(events);
+		    synchronized (fDataQueue) {
+		        if (fDataQueue.size() < fEventSetQueue.size()) {
+		            fDataQueue.add(NULL);
+		        }
+		    }		    
 		}
 		fUpdateJob.schedule();
 	}
 	
+	protected void queueData(Object data) {
+	    synchronized (fDataQueue) {
+	        fDataQueue.add(data);
+        }
+	}
+	
+	protected DebugEvent[] doPreprocessEvents(DebugEvent[] events) {
+	    return events;
+	}
+	
 	/**
 	 * Filters the given events before processing.
 	 *  
@@ -156,14 +194,14 @@
 	 * updating that must always be performed, even when the view is not
 	 * visible.
 	 */
-	protected void updateForDebugEvents(DebugEvent[] events) {
+	protected void updateForDebugEvents(DebugEvent[] events, Object data) {
 	}
 	
 	/**
 	 * Implementation specific handling of debug events.
 	 * Subclasses should override.
 	 */
-	protected abstract void doHandleDebugEvents(DebugEvent[] events);	
+	protected abstract void doHandleDebugEvents(DebugEvent[] events, Object data);	
 		
 	/**
 	 * Helper method for inserting the given element - must be called in UI thread
@@ -316,5 +354,24 @@
 	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/DebugViewLabelDecorator.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/DebugViewLabelDecorator.java
index 71f2a67..6c6119e 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/DebugViewLabelDecorator.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/DebugViewLabelDecorator.java
@@ -258,7 +258,8 @@
 		public LabelJob(String name, IDebugModelPresentation presentation) {
 			super(name);
 			fJobPresentation= presentation;
-			setRule(this);
+			// TODO: why was this rule needed?
+			//setRule(this);
 			setSystem(true);
 		}
 		
@@ -284,17 +285,13 @@
 		 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
 		 */
 		public IStatus run(IProgressMonitor monitor) {
-			synchronized(DebugViewLabelDecorator.this) {
-				fNextJob= null;
-			}
-
 			int numElements= fElementQueue.size();
 			monitor.beginTask(MessageFormat.format(DebugUIViewsMessages.getString("DebugViewLabelDecorator.1"), new String[] { Integer.toString(numElements) }), numElements); //$NON-NLS-1$
 			while (!fElementQueue.isEmpty() && !monitor.isCanceled()) {
 				StringBuffer message= new StringBuffer(MessageFormat.format(DebugUIViewsMessages.getString("DebugViewLabelDecorator.1"), new String[] { Integer.toString(fElementQueue.size()) })); //$NON-NLS-1$
-				if (fNextJob != null) {
+				//if (fNextJob != null) {
 					message.append(MessageFormat.format(DebugUIViewsMessages.getString("DebugViewLabelDecorator.2"), new String[] { Integer.toString(fNextJob.fElementQueue.size()) })); //$NON-NLS-1$
-				}
+				//}
 				monitor.setTaskName(message.toString());
 				int blockSize= 10;
 				if (fElementQueue.size() < blockSize) {
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DeferredDebugElementWorkbenchAdapter.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DeferredDebugElementWorkbenchAdapter.java
new file mode 100644
index 0000000..30acb0c
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DeferredDebugElementWorkbenchAdapter.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 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.debug.internal.ui.views.launch;
+
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.debug.core.model.IDebugElement;
+import org.eclipse.debug.internal.core.IDebugRuleFactory;
+import org.eclipse.debug.ui.DebugElementWorkbenchAdapter;
+import org.eclipse.ui.progress.IDeferredWorkbenchAdapter;
+
+
+/**
+ * Default deferred content provider for a debug target 
+ */
+public abstract class DeferredDebugElementWorkbenchAdapter extends DebugElementWorkbenchAdapter implements IDeferredWorkbenchAdapter {
+    
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.progress.IDeferredWorkbenchAdapter#isContainer()
+     */
+    public boolean isContainer() {
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.progress.IDeferredWorkbenchAdapter#getRule(java.lang.Object)
+     */
+    public ISchedulingRule getRule(Object object) {
+        if (object instanceof IDebugElement) {
+            IDebugElement element = (IDebugElement)object;
+            IDebugRuleFactory factory = (IDebugRuleFactory) element.getAdapter(IDebugRuleFactory.class);
+            if (factory != null) {
+                return factory.accessRule(element);
+            }
+        }
+        return null;
+    }
+
+
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DeferredTarget.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DeferredTarget.java
index 42cc723..dcc0b8f 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DeferredTarget.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DeferredTarget.java
@@ -11,11 +11,9 @@
 package org.eclipse.debug.internal.ui.views.launch;
 
 import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.jobs.ISchedulingRule;
 import org.eclipse.debug.core.DebugException;
 import org.eclipse.debug.core.model.IDebugTarget;
 import org.eclipse.debug.core.model.IThread;
-import org.eclipse.debug.ui.DebugElementWorkbenchAdapter;
 import org.eclipse.ui.progress.IDeferredWorkbenchAdapter;
 import org.eclipse.ui.progress.IElementCollector;
 
@@ -23,7 +21,7 @@
 /**
  * Default deferred content provider for a debug target 
  */
-public class DeferredTarget extends DebugElementWorkbenchAdapter implements IDeferredWorkbenchAdapter {
+public class DeferredTarget extends DeferredDebugElementWorkbenchAdapter implements IDeferredWorkbenchAdapter {
     
     /* (non-Javadoc)
      * @see org.eclipse.ui.progress.IDeferredWorkbenchAdapter#fetchDeferredChildren(java.lang.Object, org.eclipse.ui.progress.IElementCollector, org.eclipse.core.runtime.IProgressMonitor)
@@ -37,20 +35,4 @@
         }
         collector.done();
     }
-
-    /* (non-Javadoc)
-     * @see org.eclipse.ui.progress.IDeferredWorkbenchAdapter#isContainer()
-     */
-    public boolean isContainer() {
-        return true;
-    }
-
-    /* (non-Javadoc)
-     * @see org.eclipse.ui.progress.IDeferredWorkbenchAdapter#getRule(java.lang.Object)
-     */
-    public ISchedulingRule getRule(Object object) {
-        return null;
-    }
-
-
 }
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DeferredThread.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DeferredThread.java
index 375d774..f7781c9 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DeferredThread.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DeferredThread.java
@@ -11,11 +11,9 @@
 package org.eclipse.debug.internal.ui.views.launch;
 
 import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.jobs.ISchedulingRule;
 import org.eclipse.debug.core.DebugException;
 import org.eclipse.debug.core.model.IStackFrame;
 import org.eclipse.debug.core.model.IThread;
-import org.eclipse.debug.ui.DebugElementWorkbenchAdapter;
 import org.eclipse.ui.progress.IDeferredWorkbenchAdapter;
 import org.eclipse.ui.progress.IElementCollector;
 
@@ -23,7 +21,7 @@
 /**
  * Default deferred content provider for a debug target 
  */
-public class DeferredThread extends DebugElementWorkbenchAdapter implements IDeferredWorkbenchAdapter {
+public class DeferredThread extends DeferredDebugElementWorkbenchAdapter implements IDeferredWorkbenchAdapter {
     
     /* (non-Javadoc)
      * @see org.eclipse.ui.progress.IDeferredWorkbenchAdapter#fetchDeferredChildren(java.lang.Object, org.eclipse.ui.progress.IElementCollector, org.eclipse.core.runtime.IProgressMonitor)
@@ -38,19 +36,4 @@
         collector.done();
     }
 
-    /* (non-Javadoc)
-     * @see org.eclipse.ui.progress.IDeferredWorkbenchAdapter#isContainer()
-     */
-    public boolean isContainer() {
-        return true;
-    }
-
-    /* (non-Javadoc)
-     * @see org.eclipse.ui.progress.IDeferredWorkbenchAdapter#getRule(java.lang.Object)
-     */
-    public ISchedulingRule getRule(Object object) {
-        return null;
-    }
-
-
 }
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 9c97c3d..657ba05 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,6 +16,7 @@
 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;
@@ -29,7 +30,6 @@
 import org.eclipse.debug.core.model.IThread;
 import org.eclipse.debug.internal.ui.DebugUIPlugin;
 import org.eclipse.debug.internal.ui.views.AbstractDebugEventHandler;
-import org.eclipse.jface.viewers.ITreeContentProvider;
 
 /**
  * Handles debug events, updating the launch view and viewer.
@@ -84,7 +84,7 @@
 	/**
 	 * @see AbstractDebugEventHandler#doHandleDebugEvents(DebugEvent[])
 	 */
-	protected void doHandleDebugEvents(DebugEvent[] events) {
+	protected void doHandleDebugEvents(DebugEvent[] events, Object data) {
 		fThreadTimer.handleDebugEvents(events);
 		Object suspendee = null;
 		for (int i = 0; i < events.length; i++) {
@@ -103,19 +103,24 @@
 						fThreadTimer.getTimedOutThreads().remove(source);
 						remove(source);
 					} else {
+					    Object parent = null;
 						if (source instanceof IDebugTarget) {
 							clearSourceSelection(source);
+							parent = ((IDebugTarget)source).getLaunch();
+						} else if (source instanceof IProcess) {
+						    parent = ((IProcess)source).getLaunch();
 						}
-						Object parent = ((ITreeContentProvider)getTreeViewer().getContentProvider()).getParent(source);
-						refresh(parent);
+						if (parent != null) {
+						    refresh(parent);
+						}
 					}
 					break;
 				case DebugEvent.RESUME :
-					doHandleResumeEvent(event, source);
+					doHandleResumeEvent(event, source, data);
 					break;
 				case DebugEvent.SUSPEND :
 					if (suspendee == null || !suspendee.equals(source)) {
-						doHandleSuspendEvent(source, event);
+						doHandleSuspendEvent(source, event, data);
 						suspendee = source;
 					}
 					break;
@@ -146,7 +151,7 @@
 	/**
 	 * Handles the given resume event with the given source.
 	 */
-	protected void doHandleResumeEvent(DebugEvent event, Object source) {
+	protected void doHandleResumeEvent(DebugEvent event, Object source, Object data) {
 		if (!event.isEvaluation()) {
 			clearSourceSelection(source);
 		}
@@ -161,18 +166,9 @@
 		}
 		refresh(source);
 		if (source instanceof IThread) {
-			// When a thread resumes, try to select another suspended thread
-			// in the same target.
-			try {
-				IThread[] threads= ((IThread) source).getDebugTarget().getThreads();
-				for (int i = 0; i < threads.length; i++) {
-					IStackFrame frame = threads[i].getTopStackFrame();
-					if (frame != null) {
-						selectAndReveal(frame);
-						return;
-					}
-				}
-			} catch (DebugException e) {
+		    if (data instanceof IStackFrame) {
+				selectAndReveal(data);
+				return;
 			}
 			selectAndReveal(source);
 			return;
@@ -191,7 +187,7 @@
 		clearSourceSelection(thread);
 	}
 
-	protected void doHandleSuspendEvent(Object element, DebugEvent event) {
+	protected void doHandleSuspendEvent(Object element, DebugEvent event, Object data) {
 		IThread thread= getThread(element);
 		if (thread != null) {
 			fThreadTimer.stopTimer(thread);
@@ -205,7 +201,7 @@
 			}
 		}
 		if (element instanceof IThread) {
-			doHandleSuspendThreadEvent((IThread)element, event, wasTimedOut);
+			doHandleSuspendThreadEvent((IThread)element, event, wasTimedOut, data);
 			return;
 		}
 		refresh(element);
@@ -214,7 +210,7 @@
 	/**
 	 * Updates the given thread for the given suspend event.
 	 */
-	protected void doHandleSuspendThreadEvent(IThread thread, DebugEvent event, boolean wasTimedOut) {
+	protected void doHandleSuspendThreadEvent(IThread thread, DebugEvent event, boolean wasTimedOut, Object data) {
 		// if the thread has already resumed, do nothing
 		if (!thread.isSuspended() || !isAvailable()) {
 			return;
@@ -223,48 +219,48 @@
 		// do not update source selection for evaluation events
 		boolean evaluationEvent = event.isEvaluation();
 		
+		// get the top frame
+		IStackFrame frame = null;
+		if (data instanceof IStackFrame) {
+		    frame = (IStackFrame) data;
+		}
+	    
 		// if the top frame is the same, only update labels and images, and re-select
 		// the frame to display source
-		try {
-			IStackFrame frame = thread.getTopStackFrame();
-			if (frame != null && frame.equals(fLastStackFrame)) {
-				if (wasTimedOut) {
-					getLaunchViewer().updateStackFrameImages(thread);
-				}
-				getLaunchViewer().update(new Object[] {thread, frame}, null);
-				if (!evaluationEvent) {
-				    getLaunchViewer().deferExpansion(thread);
-					getLaunchViewer().setDeferredSelection(frame);
-				} else if (wasTimedOut) {
-					getLaunchView().showEditorForCurrentSelection();
-				}
-				return;
+		if (frame != null && frame.equals(fLastStackFrame)) {
+			if (wasTimedOut) {
+				getLaunchViewer().updateStackFrameImages(thread);
 			}
-		} catch (DebugException e) {
+			getLaunchViewer().update(new Object[] {thread, frame}, null);
+			if (!evaluationEvent) {
+			    getLaunchViewer().deferExpansion(thread);
+				getLaunchViewer().setDeferredSelection(frame);
+			} else if (wasTimedOut) {
+				getLaunchView().showEditorForCurrentSelection();
+			}
+			return;
 		}
 		
-		try {
-			fLastStackFrame = thread.getTopStackFrame();
+		if (frame != null) {
+		    fLastStackFrame = frame;
 			// Auto-expand the thread. Only select the thread if this wasn't the end
 			// of an evaluation
 			getLaunchView().autoExpand(thread, false);
 			if (fLastStackFrame != null) {
 			    getLaunchView().autoExpand(fLastStackFrame, !evaluationEvent);
 			}
-		} catch (DebugException e) {
-			fLastStackFrame = null;
 		}
 	}
 	
 	/**
 	 * @see AbstractDebugEventHandler#updateForDebugEvents(DebugEvent[])
 	 */
-	protected void updateForDebugEvents(DebugEvent[] events) {
-		super.updateForDebugEvents(events);
+	protected void updateForDebugEvents(DebugEvent[] events, Object data) {
+		super.updateForDebugEvents(events, data);
 		if (isViewVisible()) {
 			return;
 		}
-		doHandleDebugEvents(events);
+		doHandleDebugEvents(events, null);
 	}
 	
 	/**
@@ -579,4 +575,52 @@
 		getView().asyncExec(r);
 	}
 
+    /* (non-Javadoc)
+     * @see org.eclipse.debug.internal.ui.views.AbstractDebugEventHandler#doPreprocessEvents(org.eclipse.debug.core.DebugEvent[])
+     */
+    protected DebugEvent[] doPreprocessEvents(DebugEvent[] events) {
+        for (int i = 0; i < events.length; i++) {
+            DebugEvent event = events[i];
+            Object source = event.getSource();
+            switch (event.getKind()) {
+            	case DebugEvent.SUSPEND:
+            	    if (source instanceof IThread) {
+            	        IThread thread = (IThread)source;
+            		    ISchedulingRule rule = null;
+            		    try {
+            		        rule = beginAccessRule(thread);
+            		        IStackFrame frame = thread.getTopStackFrame();
+            		        queueData(frame);
+            		    } catch (DebugException e) {
+            		    } finally {
+            		        endRule(rule);
+            		    }
+            	    }
+            	    break;
+            	case DebugEvent.RESUME:
+            		if (source instanceof IThread) {
+            			// When a thread resumes, try to select another suspended thread
+            			// in the same target.
+            		    ISchedulingRule rule = null;
+            			try {
+            			    IDebugTarget target = ((IThread) source).getDebugTarget();
+            			    rule = beginAccessRule(target);
+            				IThread[] threads= target.getThreads();
+            				for (int j = 0; j < threads.length; j++) {
+            					IStackFrame frame = threads[j].getTopStackFrame();
+            					if (frame != null) {
+            						queueData(frame);
+            						break;
+            					}
+            				}
+            			} catch (DebugException e) {
+            			} finally {
+            			    endRule(rule);
+            			}
+            		}
+            		break;
+            }
+        }
+        return events;
+    }
 }
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/VariablesViewEventHandler.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/VariablesViewEventHandler.java
index 5f6a831..57436cf 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/VariablesViewEventHandler.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/VariablesViewEventHandler.java
@@ -35,7 +35,7 @@
 	/**
 	 * @see AbstractDebugEventHandler#handleDebugEvents(DebugEvent[])
 	 */
-	protected void doHandleDebugEvents(DebugEvent[] events) {
+	protected void doHandleDebugEvents(DebugEvent[] events, Object data) {
 		for (int i = 0; i < events.length; i++) {	
 			DebugEvent event = events[i];
 			switch (event.getKind()) {
@@ -55,7 +55,7 @@
 	/**
 	 * @see AbstractDebugEventHandler#updateForDebugEvents(DebugEvent[])
 	 */
-	protected void updateForDebugEvents(DebugEvent[] events) {
+	protected void updateForDebugEvents(DebugEvent[] events, Object data) {
 		for (int i = 0; i < events.length; i++) {	
 			DebugEvent event = events[i];
 			switch (event.getKind()) {