Bug 535686 - use one queue for scheduling async tasks

Queue async tasks coming from ChildrenUpdate's jobs via
scheduleViewerUpdate() in the same queue with async tasks coming from
modelChanged(), so that they all run in more predictable order. This
allows us to run async UI updates triggered by ElementContentProvider
jobs before we run next batch of model change events.

Additionally this should further reduce the number of
Display.asyncExec() tasks coming from debugger.

Change-Id: I4a16e9cc79ab453b35d064c85440b726a4772442
Signed-off-by: Andrey Loskutov <loskutov@gmx.de>
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java
index e6b5e82..f37bfef 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java
@@ -399,7 +399,7 @@
 	private class DelayedDoModelChangedJob extends WorkbenchJob {
 
 		// queue of submitted deltas to process
-		private final List<DelayedDoModelChange> fQueue = new ArrayList<>();
+		private final List<Object> fQueue = new ArrayList<>();
 		private boolean shutdown;
 
 		public DelayedDoModelChangedJob() {
@@ -410,7 +410,7 @@
 
 		@Override
 		public IStatus runInUIThread(IProgressMonitor monitor) {
-			List<DelayedDoModelChange> currentBatch = new ArrayList<>();
+			List<Object> currentBatch = new ArrayList<>();
 			synchronized (fQueue) {
 				if (shutdown || fQueue.isEmpty()) {
 					return Status.OK_STATUS;
@@ -421,21 +421,26 @@
 			if (DebugUIPlugin.DEBUG_CONTENT_PROVIDER) {
 				DebugUIPlugin.trace("Delayed batch size: " + currentBatch.size()); //$NON-NLS-1$
 			}
-			for (Iterator<DelayedDoModelChange> iterator = currentBatch.iterator(); iterator.hasNext();) {
-				DelayedDoModelChange change = iterator.next();
+			for (Iterator<?> iterator = currentBatch.iterator(); iterator.hasNext();) {
+				Object task = iterator.next();
 				if (monitor.isCanceled()) {
 					restoreQueue(currentBatch);
 					return Status.CANCEL_STATUS;
 				}
-				if (!change.proxy.isDisposed()) {
-					doModelChanged(change.delta, change.proxy);
+				if (task instanceof DelayedDoModelChange) {
+					DelayedDoModelChange change = (DelayedDoModelChange) task;
+					if (!change.proxy.isDisposed()) {
+						doModelChanged(change.delta, change.proxy);
+					}
+				} else {
+					((Runnable) task).run();
 				}
 				iterator.remove();
 			}
 			return Status.OK_STATUS;
 		}
 
-		private void restoreQueue(List<DelayedDoModelChange> currentBatch) {
+		private void restoreQueue(List<Object> currentBatch) {
 			synchronized (fQueue) {
 				currentBatch.addAll(fQueue);
 				fQueue.clear();
@@ -444,11 +449,19 @@
 		}
 
 		public void runDelayed(final IModelDelta delta, final IModelProxy proxy) {
+			runDelayed(new DelayedDoModelChange(delta, proxy));
+		}
+
+		public void runDelayed(final Runnable uiTask) {
+			runDelayed((Object) uiTask);
+		}
+
+		private void runDelayed(final Object task) {
 			synchronized (fQueue) {
 				if (shutdown) {
 					return;
 				}
-				fQueue.add(new DelayedDoModelChange(delta, proxy));
+				fQueue.add(task);
 				if (DebugUIPlugin.DEBUG_CONTENT_PROVIDER) {
 					DebugUIPlugin.trace("Delayed queue size: " + fQueue.size()); //$NON-NLS-1$
 				}
@@ -1779,7 +1792,7 @@
             if (Thread.currentThread() == display.getThread()) {
             	performUpdates();
             } else {
-            	display.asyncExec(updateJob);
+				fDelayedDoModelChangeJob.runDelayed(updateJob);
             }
 	    }
 	}