1 /*******************************************************************************
2 * Copyright (c) 2014 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Alexandre Montplaisir - Initial API and implementation
11 * Patrick Tasse - Update queue handling
12 *******************************************************************************/
14 package org
.eclipse
.tracecompass
.tmf
.ui
;
16 import java
.util
.LinkedHashMap
;
19 import org
.eclipse
.swt
.widgets
.Display
;
22 * This handler offers "coalescing" of UI updates.
24 * When displaying live experiments containing a high number of traces, every
25 * trace will want to regularly update views with their new available data. This
26 * can cause a high number of threads calling {@link Display#asyncExec}
27 * repeatedly, which can really impede UI responsiveness.
29 * Instead of calling {@link Display#asyncExec} directly, threads that want to
30 * queue updates to the UI can instead call
31 * {@link TmfUiRefreshHandler#queueUpdate}. If the handler is not currently
32 * executing another update it will be scheduled immediately. Otherwise the
33 * update will be queued.
35 * The handler will only execute one update at a time. While it is busy, new
36 * requests received from a source that is already in the queue will replace the
37 * previous one (as we assume the latest UI update request is the most
38 * up-to-date and interesting one), preserving the previous request order. New
39 * requests received from other sources will be added to the end of the queue
40 * (keeping only the latest request from each source).
42 * Once the current update is completed, the oldest request in the queue will be
43 * sent to the UI thread via one single call to {@link Display#syncExec}.
45 * @author Alexandre Montplaisir
47 public class TmfUiRefreshHandler
{
49 /** Singleton instance */
50 private static TmfUiRefreshHandler fInstance
= null;
52 private final Map
<Object
, Runnable
> fUpdates
= new LinkedHashMap
<>();
53 private Thread fCurrentTask
;
57 * Internal constructor
59 private TmfUiRefreshHandler() {
64 * Get the handler's instance
66 * @return The singleton instance
68 public static synchronized TmfUiRefreshHandler
getInstance() {
69 if (fInstance
== null) {
70 fInstance
= new TmfUiRefreshHandler();
76 * Cancel all current requests and dispose the handler.
78 public synchronized void dispose() {
84 * Queue a UI update. Threads that want to benefit from "UI coalescing"
85 * should send their {@link Runnable} to this method, instead of
86 * {@link Display#asyncExec(Runnable)}.
89 * The source sending the request. Typically callers should use
90 * "this". When multiple requests are queued before being
91 * executed, only the latest request per source is actually sent.
93 * The {@link Runnable} to execute in the UI thread.
95 public synchronized void queueUpdate(Object source
, Runnable task
) {
96 fUpdates
.put(source
, task
);
97 if (fCurrentTask
== null) {
98 fCurrentTask
= new RunAllUpdates();
104 * Task to empty the update queue, and send each task to the UI thread.
106 private class RunAllUpdates
extends Thread
{
110 Runnable nextTask
= null;
111 synchronized (TmfUiRefreshHandler
.this) {
112 if (!fUpdates
.isEmpty()) {
113 Object firstKey
= fUpdates
.keySet().iterator().next();
114 nextTask
= fUpdates
.get(firstKey
);
115 fUpdates
.remove(firstKey
);
117 if (nextTask
== null) {
119 * No updates remaining in the queue, put fCurrentTask
120 * back to null so that a new task can be scheduled.
126 Display
.getDefault().syncExec(nextTask
);