mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
[220446] Updated the "dataviewer" example and excercises for EclipseCon tutorial.
This commit is contained in:
parent
f3cd935a6c
commit
642ea06516
18 changed files with 1151 additions and 1957 deletions
|
@ -6,12 +6,12 @@
|
||||||
point="org.eclipse.ui.views">
|
point="org.eclipse.ui.views">
|
||||||
<category
|
<category
|
||||||
name="DSF Examples"
|
name="DSF Examples"
|
||||||
id="org.eclipse.dd.dsf.examples.timers">
|
id="org.eclipse.dd.examples.dsf">
|
||||||
</category>
|
</category>
|
||||||
<view
|
<view
|
||||||
name="Timers View"
|
name="Timers View"
|
||||||
icon="icons/timer.gif"
|
icon="icons/timer.gif"
|
||||||
category="org.eclipse.dd.dsf.examples.timers"
|
category="org.eclipse.dd.examples.dsf"
|
||||||
class="org.eclipse.dd.examples.dsf.timers.TimersView"
|
class="org.eclipse.dd.examples.dsf.timers.TimersView"
|
||||||
id="org.eclipse.dd.dsf.examples.model.TimersAlarmsView">
|
id="org.eclipse.dd.dsf.examples.model.TimersAlarmsView">
|
||||||
</view>
|
</view>
|
||||||
|
@ -22,34 +22,16 @@
|
||||||
id="org.eclipse.dd.dsf.test.actionSet"
|
id="org.eclipse.dd.dsf.test.actionSet"
|
||||||
label="DSF Examples">
|
label="DSF Examples">
|
||||||
<menu
|
<menu
|
||||||
id="org.eclipse.dd.dsf.examples.timers"
|
id="org.eclipse.dd.examples.dsf"
|
||||||
label="DSF Examples"
|
label="DSF Examples"
|
||||||
path="additions">
|
path="additions">
|
||||||
<groupMarker name="concurrent"/>
|
<groupMarker name="concurrent"/>
|
||||||
</menu>
|
</menu>
|
||||||
<action
|
|
||||||
class="org.eclipse.dd.examples.dsf.dataviewer.CancellableInputCoalescingSlowDataProviderAction"
|
|
||||||
id="org.eclipse.dd.dsf.test.cancellableInputCoalescingSlowDataProvider"
|
|
||||||
label="Open Dialog with Cancellable Input-Coalescing Slow Data Provider"
|
|
||||||
menubarPath="org.eclipse.dd.dsf.examples.timers/concurrent"
|
|
||||||
style="push"/>
|
|
||||||
<action
|
|
||||||
class="org.eclipse.dd.examples.dsf.dataviewer.InputCoalescingSlowDataProviderAction"
|
|
||||||
id="org.eclipse.dd.dsf.test.inputCoalescingSlowDataProvider"
|
|
||||||
label="Open Dialog with Input-Coalescing Slow Data Provider"
|
|
||||||
menubarPath="org.eclipse.dd.dsf.examples.timers/concurrent"
|
|
||||||
style="push"/>
|
|
||||||
<action
|
|
||||||
class="org.eclipse.dd.examples.dsf.dataviewer.SlowDataProviderAction"
|
|
||||||
id="org.eclipse.dd.dsf.test.slowDataProvider"
|
|
||||||
label="Open Dialog with Slow Data Provider"
|
|
||||||
menubarPath="org.eclipse.dd.dsf.examples.timers/concurrent"
|
|
||||||
style="push"/>
|
|
||||||
<action
|
<action
|
||||||
class="org.eclipse.dd.examples.dsf.filebrowser.FileBrowserAction"
|
class="org.eclipse.dd.examples.dsf.filebrowser.FileBrowserAction"
|
||||||
id="org.eclipse.dd.dsf.test.fileBrowser"
|
id="org.eclipse.dd.dsf.test.fileBrowser"
|
||||||
label="Open File Browser Dialog"
|
label="Open File Browser Dialog"
|
||||||
menubarPath="org.eclipse.dd.dsf.examples.timers/concurrent"
|
menubarPath="org.eclipse.dd.examples.dsf/concurrent"
|
||||||
style="push"/>
|
style="push"/>
|
||||||
</actionSet>
|
</actionSet>
|
||||||
</extension>
|
</extension>
|
||||||
|
|
|
@ -1,440 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2006 Wind River Systems and others.
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* Contributors:
|
|
||||||
* Wind River Systems - initial API and implementation
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.eclipse.dd.examples.dsf.concurrent;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.DelayQueue;
|
|
||||||
import java.util.concurrent.Delayed;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import org.eclipse.core.runtime.IStatus;
|
|
||||||
import org.eclipse.core.runtime.Status;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
|
|
||||||
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example data provider which has a built-in delay when fetching data. This
|
|
||||||
* data provider simulates a service which retrieves data from an external
|
|
||||||
* source such as a networked target, which incurs a considerable delay when
|
|
||||||
* retrieving data. The data items are simulated values which consist of the
|
|
||||||
* time when data is being retrieved followed by the item's index.
|
|
||||||
* <p>
|
|
||||||
* This version of the data provider builds on the input-coalescing feature,
|
|
||||||
* it adds a test to check if the requests were cancelled by the user, before
|
|
||||||
* they are coalesced and queued to be processed by the provider thread.
|
|
||||||
*/
|
|
||||||
public class CancellableInputCoalescingSlowDataProvider implements DataProvider {
|
|
||||||
|
|
||||||
/** Minimum count of data items */
|
|
||||||
private final static int MIN_COUNT = 1000;
|
|
||||||
|
|
||||||
/** Maximum count of data items */
|
|
||||||
private final static int MAX_COUNT = 2000;
|
|
||||||
|
|
||||||
/** Time interval how often random changes occur. */
|
|
||||||
private final static int RANDOM_CHANGE_MILIS = 10000;
|
|
||||||
|
|
||||||
/** Number of times random changes are made, before count is changed. */
|
|
||||||
private final static int RANDOM_COUNT_CHANGE_INTERVALS = 3;
|
|
||||||
|
|
||||||
/** Percentage of values that is changed upon random change (0-100). */
|
|
||||||
private final static int RANDOM_CHANGE_SET_PERCENTAGE = 10;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Amount of time (in miliseconds) how long the requests to provider, and
|
|
||||||
* events from provider are delayed by.
|
|
||||||
*/
|
|
||||||
private final static int TRANSMISSION_DELAY_TIME = 500;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Amount of time (in milliseconds) how long the provider takes to process
|
|
||||||
* a request.
|
|
||||||
*/
|
|
||||||
private final static int PROCESSING_TIME = 100;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of item requests that can be coalesced into a single
|
|
||||||
* request.
|
|
||||||
*/
|
|
||||||
private final static int COALESCING_COUNT_LIMIT = 10;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delay in processing the buffer of getItem() calls. This delay helps
|
|
||||||
* to ensure that a meaningful number of items is present in the buffer
|
|
||||||
* before the buffer data is coalesced into a request.
|
|
||||||
*/
|
|
||||||
private final static int COALESCING_DELAY_TIME = 100;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum allowed number of requests in transmission to provider thread.
|
|
||||||
* This limit causes most of the client calls to be buffered at the input
|
|
||||||
* rather than in the request queue, which in truns allows the stale
|
|
||||||
* requests to be cancelled, before they are sent to the provider thread
|
|
||||||
* for processing.
|
|
||||||
*/
|
|
||||||
private final static int REQUEST_QUEUE_SIZE_LIMIT = 100;
|
|
||||||
|
|
||||||
/** Delay before processing calls buffer, if the request queue is full */
|
|
||||||
private final static int REQUEST_BUFFER_FULL_RETRY_DELAY = PROCESSING_TIME;
|
|
||||||
|
|
||||||
/** Dispatch-thread executor that this provider uses. */
|
|
||||||
private DsfExecutor fExecutor;
|
|
||||||
|
|
||||||
/** List of listeners registered for events from provider. */
|
|
||||||
private List<Listener> fListeners = new LinkedList<Listener>();
|
|
||||||
|
|
||||||
/** Thread that handles data requests. */
|
|
||||||
private ProviderThread fProviderThread;
|
|
||||||
|
|
||||||
/** Queue of currently pending data requests. */
|
|
||||||
private final BlockingQueue<Request> fQueue = new DelayQueue<Request>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runnable to be submitted when the data provider thread is shut down.
|
|
||||||
* This variable acts like a flag: when client want to shut down the
|
|
||||||
* provider, it sets this runnable, and when the backgroun thread sees
|
|
||||||
* that it's set, it shuts itself down, and posts this runnable with
|
|
||||||
* the executor.
|
|
||||||
*/
|
|
||||||
private RequestMonitor fShutdownRequestMonitor = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Buffers for coalescing getItem() calls into a single request.
|
|
||||||
*/
|
|
||||||
private List<Integer> fGetItemIndexesBuffer = new LinkedList<Integer>();
|
|
||||||
private List<DataRequestMonitor<String>> fGetItemRequestMonitorsBuffer = new LinkedList<DataRequestMonitor<String>>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for requests that are queued by the data provider. It
|
|
||||||
* implements java.util.concurrent.Delayed to allow for use of DelayedQueue.
|
|
||||||
* Every request into the queue is delayed by the simulated transmission
|
|
||||||
* time.
|
|
||||||
*/
|
|
||||||
private static abstract class Request implements Delayed {
|
|
||||||
/** Sequence counter and number are used to ensure FIFO order **/
|
|
||||||
private static int fSequenceCounter = 0;
|
|
||||||
private int fSequenceNumber = fSequenceCounter++;
|
|
||||||
|
|
||||||
/** Time delay tracks how items will be delayed. **/
|
|
||||||
private long fTime = System.currentTimeMillis() + TRANSMISSION_DELAY_TIME;
|
|
||||||
|
|
||||||
// @see java.util.concurrent.Delayed
|
|
||||||
public long getDelay(TimeUnit unit) {
|
|
||||||
return unit.convert(fTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @see java.lang.Comparable
|
|
||||||
public int compareTo(Delayed other) {
|
|
||||||
if (other == this) // compare zero ONLY if same object
|
|
||||||
return 0;
|
|
||||||
Request x = (Request)other;
|
|
||||||
long diff = fTime - x.fTime;
|
|
||||||
|
|
||||||
if (diff < 0) return -1;
|
|
||||||
else if (diff > 0) return 1;
|
|
||||||
else if (fSequenceNumber < x.fSequenceNumber) return -1;
|
|
||||||
else return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** All requests have an associated array of RequestMonitor tokens **/
|
|
||||||
abstract RequestMonitor[] getRequestMonitors();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object used to encapsulate the "getItemCount" requests. Instances of it
|
|
||||||
* are queued till processed.
|
|
||||||
*/
|
|
||||||
private static class CountRequest extends Request
|
|
||||||
{
|
|
||||||
DataRequestMonitor<Integer> fRequestMonitor;
|
|
||||||
CountRequest(DataRequestMonitor<Integer> rm) { fRequestMonitor = rm; }
|
|
||||||
@Override
|
|
||||||
DataRequestMonitor<?>[] getRequestMonitors() { return new DataRequestMonitor[] { fRequestMonitor }; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object used to encapsulate the "getItem" requests. Instances of it
|
|
||||||
* are queued till processed.
|
|
||||||
*/
|
|
||||||
private static class ItemRequest extends Request
|
|
||||||
{
|
|
||||||
DataRequestMonitor<String>[] fRequestMonitors;
|
|
||||||
Integer[] fIndexes;
|
|
||||||
ItemRequest(Integer[] indexes, DataRequestMonitor<String>[] rms) { fIndexes = indexes; fRequestMonitors = rms; }
|
|
||||||
@Override
|
|
||||||
DataRequestMonitor<?>[] getRequestMonitors() { return fRequestMonitors; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The background thread of data provider. This thread retrieves the
|
|
||||||
* requests from the provider's queue and processes them. It also
|
|
||||||
* initiates random changes in the data set and issues corresponding
|
|
||||||
* events.
|
|
||||||
*/
|
|
||||||
private class ProviderThread extends Thread
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Current count of items in the data set. It is changed
|
|
||||||
* periodically for simulation purposes.
|
|
||||||
*/
|
|
||||||
private int fCount = MIN_COUNT;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Incremented with every data change, it causes the count to be reset
|
|
||||||
* every four random changes.
|
|
||||||
*/
|
|
||||||
private int fCountTrigger = 0;
|
|
||||||
|
|
||||||
/** Time when the last change was performed. */
|
|
||||||
private long fLastChangeTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
/** Random number generator */
|
|
||||||
private Random fRandom = new java.util.Random();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
// Initialize the count.
|
|
||||||
randomCount();
|
|
||||||
|
|
||||||
// Perform the loop until the shutdown runnable is set.
|
|
||||||
while(fShutdownRequestMonitor == null) {
|
|
||||||
// Get the next request from the queue. The time-out
|
|
||||||
// ensures that that we get to process the random changes.
|
|
||||||
final Request request = fQueue.poll(RANDOM_CHANGE_MILIS / 10, TimeUnit.MILLISECONDS);
|
|
||||||
|
|
||||||
// If a request was dequeued, process it.
|
|
||||||
if (request != null) {
|
|
||||||
// Simulate a processing delay.
|
|
||||||
Thread.sleep(PROCESSING_TIME);
|
|
||||||
|
|
||||||
if (request instanceof CountRequest) {
|
|
||||||
processCountRequest((CountRequest)request);
|
|
||||||
} else if (request instanceof ItemRequest) {
|
|
||||||
processItemRequest((ItemRequest)request);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Whatever the results, post it to dispatch thread
|
|
||||||
// executor (with transmission delay).
|
|
||||||
fExecutor.schedule(
|
|
||||||
new DsfRunnable() {
|
|
||||||
public void run() {
|
|
||||||
for (RequestMonitor requestMonitor : request.getRequestMonitors()) {
|
|
||||||
requestMonitor.done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate data changes.
|
|
||||||
randomChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (InterruptedException x) {
|
|
||||||
DsfExamplesPlugin.getDefault().getLog().log( new Status(
|
|
||||||
IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, 0, "Interrupted exception in slow data provider thread.", x )); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify the client that requested shutdown, that shutdown is complete.
|
|
||||||
fShutdownRequestMonitor.done();
|
|
||||||
fShutdownRequestMonitor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processCountRequest(CountRequest request) {
|
|
||||||
// Calculate the simulated values.
|
|
||||||
request.fRequestMonitor.setData(fCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processItemRequest(ItemRequest request) {
|
|
||||||
// Check to make sure that the number of indexes matches the number
|
|
||||||
// of return tokens.
|
|
||||||
assert request.fRequestMonitors.length == request.fIndexes.length;
|
|
||||||
|
|
||||||
// Calculate the simulated values for each index in request.
|
|
||||||
for (int i = 0; i < request.fIndexes.length; i++) {
|
|
||||||
request.fRequestMonitors[i].setData(Long.toHexString(fLastChangeTime) + "." + request.fIndexes[i]); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method simulates changes in provider's data set.
|
|
||||||
*/
|
|
||||||
private void randomChanges()
|
|
||||||
{
|
|
||||||
if (System.currentTimeMillis() > fLastChangeTime + RANDOM_CHANGE_MILIS) {
|
|
||||||
fLastChangeTime = System.currentTimeMillis();
|
|
||||||
// once in every 30 seconds broadcast item count change
|
|
||||||
if (++fCountTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0) randomCount();
|
|
||||||
else randomDataChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates new size for provider's data set.
|
|
||||||
*/
|
|
||||||
private void randomCount()
|
|
||||||
{
|
|
||||||
fCount = MIN_COUNT + Math.abs(fRandom.nextInt()) % (MAX_COUNT - MIN_COUNT);
|
|
||||||
|
|
||||||
// Generate the event that the count has changed, and post it to
|
|
||||||
// dispatch thread with transmission delay.
|
|
||||||
fExecutor.schedule(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
for (Listener listener : fListeners) {
|
|
||||||
listener.countChanged();
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidates a random range of indexes.
|
|
||||||
*/
|
|
||||||
private void randomDataChange()
|
|
||||||
{
|
|
||||||
final Set<Integer> set = new HashSet<Integer>();
|
|
||||||
// Change one in ten values.
|
|
||||||
for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) {
|
|
||||||
set.add( new Integer(Math.abs(fRandom.nextInt()) % fCount) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the event that the data has changed.
|
|
||||||
// Post dispatch thread with transmission delay.
|
|
||||||
fExecutor.schedule(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
for (Listener listener : fListeners) {
|
|
||||||
listener.dataChanged(set);
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public CancellableInputCoalescingSlowDataProvider(DsfExecutor executor) {
|
|
||||||
fExecutor = executor;
|
|
||||||
fProviderThread = new ProviderThread();
|
|
||||||
fProviderThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requests shutdown of this data provider.
|
|
||||||
* @param requestMonitor Monitor to call when shutdown is complete.
|
|
||||||
*/
|
|
||||||
public void shutdown(RequestMonitor requestMonitor) {
|
|
||||||
fShutdownRequestMonitor = requestMonitor;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// DataProvider
|
|
||||||
public DsfExecutor getDsfExecutor() {
|
|
||||||
return fExecutor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void getItemCount(final DataRequestMonitor<Integer> rm) {
|
|
||||||
fQueue.add(new CountRequest(rm));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void getItem(final int index, final DataRequestMonitor<String> rm) {
|
|
||||||
// Schedule a buffer-servicing call, if one is needed.
|
|
||||||
if (fGetItemIndexesBuffer.isEmpty()) {
|
|
||||||
fExecutor.schedule(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
fileBufferedRequests();
|
|
||||||
}},
|
|
||||||
COALESCING_DELAY_TIME,
|
|
||||||
TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the call data to the buffer.
|
|
||||||
// Note: it doesn't matter that the items were added to the buffer
|
|
||||||
// after the buffer-servicing request was scheduled. This is because
|
|
||||||
// the buffers are guaranteed not to be modified until this dispatch
|
|
||||||
// cycle is over.
|
|
||||||
fGetItemIndexesBuffer.add(index);
|
|
||||||
fGetItemRequestMonitorsBuffer.add(rm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void fileBufferedRequests() {
|
|
||||||
// Check if there is room in the request queue. If not, re-schedule the
|
|
||||||
// buffer-servicing for later.
|
|
||||||
if (fQueue.size() >= REQUEST_QUEUE_SIZE_LIMIT) {
|
|
||||||
if (fGetItemIndexesBuffer.isEmpty()) {
|
|
||||||
fExecutor.schedule(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
fileBufferedRequests();
|
|
||||||
}},
|
|
||||||
REQUEST_BUFFER_FULL_RETRY_DELAY,
|
|
||||||
TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove a number of getItem() calls from the buffer, and combine them
|
|
||||||
// into a request.
|
|
||||||
List<Integer> indexes = new LinkedList<Integer>();
|
|
||||||
List<DataRequestMonitor> rms = new LinkedList<DataRequestMonitor>();
|
|
||||||
int numToCoalesce = 0;
|
|
||||||
while (!fGetItemIndexesBuffer.isEmpty() && numToCoalesce < COALESCING_COUNT_LIMIT) {
|
|
||||||
// Get the next call from buffer.
|
|
||||||
Integer index = fGetItemIndexesBuffer.remove(0);
|
|
||||||
DataRequestMonitor rm = fGetItemRequestMonitorsBuffer.remove(0);
|
|
||||||
|
|
||||||
// If call is already cancelled, ignore it.
|
|
||||||
if (rm.getStatus().getSeverity() == IStatus.CANCEL) continue;
|
|
||||||
|
|
||||||
// Otherwise add it to the request.
|
|
||||||
indexes.add(index);
|
|
||||||
rms.add(rm);
|
|
||||||
numToCoalesce++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Queue the coalesced request.
|
|
||||||
fQueue.add( new ItemRequest(
|
|
||||||
indexes.toArray(new Integer[indexes.size()]),
|
|
||||||
rms.toArray(new DataRequestMonitor[rms.size()]))
|
|
||||||
);
|
|
||||||
|
|
||||||
// If there are still calls left in the buffer, execute another
|
|
||||||
// buffer-servicing call, but without any delay.
|
|
||||||
if (!fGetItemIndexesBuffer.isEmpty()) {
|
|
||||||
fExecutor.execute(new Runnable() { public void run() {
|
|
||||||
fileBufferedRequests();
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addListener(Listener listener) {
|
|
||||||
assert fExecutor.isInExecutorThread();
|
|
||||||
fListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeListener(Listener listener) {
|
|
||||||
assert fExecutor.isInExecutorThread();
|
|
||||||
fListeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2006 Wind River Systems and others.
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* Contributors:
|
|
||||||
* Wind River Systems - initial API and implementation
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.eclipse.dd.examples.dsf.concurrent;
|
|
||||||
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DefaultDsfExecutor;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
|
|
||||||
import org.eclipse.jface.action.IAction;
|
|
||||||
import org.eclipse.jface.dialogs.Dialog;
|
|
||||||
import org.eclipse.ui.IWorkbenchWindow;
|
|
||||||
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
|
|
||||||
import org.eclipse.ui.actions.ActionDelegate;
|
|
||||||
|
|
||||||
public class CancellableInputCoalescingSlowDataProviderAction extends ActionDelegate
|
|
||||||
implements IWorkbenchWindowActionDelegate
|
|
||||||
{
|
|
||||||
private IWorkbenchWindow fWindow;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run(IAction action) {
|
|
||||||
if (fWindow != null) {
|
|
||||||
// Create the standard data provider.
|
|
||||||
final CancellableInputCoalescingSlowDataProvider dataProvider =
|
|
||||||
new CancellableInputCoalescingSlowDataProvider(new DefaultDsfExecutor());
|
|
||||||
|
|
||||||
// Create the dialog and open it.
|
|
||||||
Dialog dialog = new SlowDataProviderDialog(
|
|
||||||
fWindow.getShell(), new CancellingSlowDataProviderContentProvider(), dataProvider);
|
|
||||||
dialog.open();
|
|
||||||
|
|
||||||
// Shut down the data provider thread and the DSF executor thread.
|
|
||||||
// Note, since data provider is running in background thread, we have to
|
|
||||||
// wait until this background thread has completed shutdown before
|
|
||||||
// killing the executor thread itself.
|
|
||||||
dataProvider.shutdown(new RequestMonitor(dataProvider.getDsfExecutor(), null) {
|
|
||||||
@Override
|
|
||||||
public void handleCompleted() {
|
|
||||||
dataProvider.getDsfExecutor().shutdown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(IWorkbenchWindow window) {
|
|
||||||
fWindow = window;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,292 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2006 Wind River Systems and others.
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* Contributors:
|
|
||||||
* Wind River Systems - initial API and implementation
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.eclipse.dd.examples.dsf.concurrent;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import org.eclipse.core.runtime.IStatus;
|
|
||||||
import org.eclipse.core.runtime.Status;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
|
|
||||||
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
|
|
||||||
import org.eclipse.jface.viewers.ILazyContentProvider;
|
|
||||||
import org.eclipse.jface.viewers.TableViewer;
|
|
||||||
import org.eclipse.jface.viewers.Viewer;
|
|
||||||
import org.eclipse.swt.widgets.Display;
|
|
||||||
import org.eclipse.swt.widgets.Table;
|
|
||||||
|
|
||||||
public class CancellingSlowDataProviderContentProvider
|
|
||||||
implements ILazyContentProvider, DataProvider.Listener
|
|
||||||
{
|
|
||||||
TableViewer fTableViewer;
|
|
||||||
DataProvider fDataProvider;
|
|
||||||
Display fDisplay;
|
|
||||||
List<ItemGetDataRequestMonitor> fItemDataRequestMonitors = new LinkedList<ItemGetDataRequestMonitor>();
|
|
||||||
Set<Integer> fCancelledIdxs = new HashSet<Integer>();
|
|
||||||
AtomicInteger fCancelCallsPending = new AtomicInteger();
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// ILazyContentProvider
|
|
||||||
public void dispose() {
|
|
||||||
if (fDataProvider != null) {
|
|
||||||
final DataProvider dataProvider = fDataProvider;
|
|
||||||
dataProvider.getDsfExecutor().execute(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
dataProvider.removeListener(CancellingSlowDataProviderContentProvider.this);
|
|
||||||
fTableViewer = null;
|
|
||||||
fDisplay = null;
|
|
||||||
fDataProvider = null;
|
|
||||||
}});
|
|
||||||
} else {
|
|
||||||
fTableViewer = null;
|
|
||||||
fDisplay = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void inputChanged(final Viewer viewer, Object oldInput, final Object newInput) {
|
|
||||||
// If old data provider is not-null, unregister from it as listener.
|
|
||||||
if (fDataProvider != null) {
|
|
||||||
final DataProvider dataProvider = fDataProvider;
|
|
||||||
dataProvider.getDsfExecutor().execute(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
dataProvider.removeListener(CancellingSlowDataProviderContentProvider.this);
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Register as listener with new data provider.
|
|
||||||
// Note: if old data provider and new data provider use different executors,
|
|
||||||
// there is a chance of a race condition here.
|
|
||||||
if (newInput != null) {
|
|
||||||
((DataProvider)newInput).getDsfExecutor().execute(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
if ( ((TableViewer)viewer).getTable().isDisposed() ) return;
|
|
||||||
fTableViewer = (TableViewer)viewer;
|
|
||||||
fDisplay = fTableViewer.getTable().getDisplay();
|
|
||||||
fDataProvider = (DataProvider)newInput;
|
|
||||||
fDataProvider.addListener(CancellingSlowDataProviderContentProvider.this);
|
|
||||||
queryItemCount();
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateElement(final int index) {
|
|
||||||
assert fTableViewer != null;
|
|
||||||
if (fDataProvider == null) return;
|
|
||||||
|
|
||||||
// Calculate the visible index range.
|
|
||||||
final int topIdx = fTableViewer.getTable().getTopIndex();
|
|
||||||
final int botIdx = topIdx + getVisibleItemCount(topIdx);
|
|
||||||
|
|
||||||
fCancelCallsPending.incrementAndGet();
|
|
||||||
fDataProvider.getDsfExecutor().execute(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
// Must check again, in case disposed while re-dispatching.
|
|
||||||
if (fDataProvider == null || fTableViewer.getTable().isDisposed()) return;
|
|
||||||
if (index >= topIdx && index <= botIdx) {
|
|
||||||
queryItemData(index);
|
|
||||||
}
|
|
||||||
cancelStaleRequests(topIdx, botIdx);
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getVisibleItemCount(int top) {
|
|
||||||
Table table = fTableViewer.getTable();
|
|
||||||
int itemCount = table.getItemCount();
|
|
||||||
return Math.min((table.getBounds().height / table.getItemHeight()) + 2, itemCount - top);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// DataProvider.Listener
|
|
||||||
public void countChanged() {
|
|
||||||
// Check for dispose.
|
|
||||||
if (fDataProvider == null) return;
|
|
||||||
|
|
||||||
// Request new count.
|
|
||||||
queryItemCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dataChanged(final Set<Integer> indexes) {
|
|
||||||
// Check for dispose.
|
|
||||||
if (fDataProvider == null) return;
|
|
||||||
|
|
||||||
// Clear changed items in table viewer.
|
|
||||||
final TableViewer tableViewer = fTableViewer;
|
|
||||||
fDisplay.asyncExec(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
// Check again if table wasn't disposed when
|
|
||||||
// switching to the display thread.
|
|
||||||
if (fTableViewer == null || fTableViewer.getTable().isDisposed()) return;
|
|
||||||
for (Integer index : indexes) {
|
|
||||||
tableViewer.clear(index);
|
|
||||||
}
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
//
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience extension to standard data return runnable. This extension
|
|
||||||
* automatically checks for errors and asynchronous dispose.
|
|
||||||
* @param <V>
|
|
||||||
*/
|
|
||||||
private abstract class CPGetDataRequestMonitor<V> extends DataRequestMonitor<V> {
|
|
||||||
public CPGetDataRequestMonitor(DsfExecutor executor) { super(executor, null); }
|
|
||||||
abstract protected void doRun();
|
|
||||||
@Override
|
|
||||||
public void handleCompleted() {
|
|
||||||
// If there is an error processing request, return.
|
|
||||||
if (!getStatus().isOK()) return;
|
|
||||||
|
|
||||||
// If content provider was disposed, return.
|
|
||||||
if (fTableViewer == null || fTableViewer.getTable().isDisposed()) return;
|
|
||||||
|
|
||||||
// Otherwise execute runnable.
|
|
||||||
doRun();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the item count query with DataProvider. Must be called on
|
|
||||||
* data provider's dispatch thread.
|
|
||||||
*/
|
|
||||||
private void queryItemCount() {
|
|
||||||
assert fDataProvider.getDsfExecutor().isInExecutorThread();
|
|
||||||
|
|
||||||
// Request coumt from data provider. When the count is returned, we
|
|
||||||
// have to re-dispatch into the display thread to avoid calling
|
|
||||||
// the table widget on the DSF dispatch thread.
|
|
||||||
fCancelledIdxs.clear();
|
|
||||||
fDataProvider.getItemCount(
|
|
||||||
new CPGetDataRequestMonitor<Integer>(fDataProvider.getDsfExecutor()) {
|
|
||||||
@Override
|
|
||||||
protected void doRun() {
|
|
||||||
final TableViewer tableViewer = fTableViewer;
|
|
||||||
tableViewer.getTable().getDisplay().asyncExec(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
// Check again if table wasn't disposed when
|
|
||||||
// switching to the display thread.
|
|
||||||
if (tableViewer.getTable().isDisposed()) return; // disposed
|
|
||||||
tableViewer.setItemCount(getData());
|
|
||||||
tableViewer.getTable().clearAll();
|
|
||||||
}});
|
|
||||||
}});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dedicated class for data item requests. This class holds the index
|
|
||||||
* argument so it can be examined when cancelling stale requests.
|
|
||||||
*/
|
|
||||||
// Request data from data provider. Likewise, when the data is
|
|
||||||
// returned, we have to re-dispatch into the display thread to
|
|
||||||
// call the table widget.
|
|
||||||
class ItemGetDataRequestMonitor extends CPGetDataRequestMonitor<String> {
|
|
||||||
|
|
||||||
/** Index is used when cancelling stale requests. */
|
|
||||||
int fIndex;
|
|
||||||
|
|
||||||
ItemGetDataRequestMonitor(DsfExecutor executor, int index) {
|
|
||||||
super(executor);
|
|
||||||
fIndex = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the request from list of outstanding requests. This has
|
|
||||||
// to be done in run() because doRun() is not always called.
|
|
||||||
@Override
|
|
||||||
public void handleCompleted() {
|
|
||||||
fItemDataRequestMonitors.remove(this);
|
|
||||||
super.handleCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the result as usual.
|
|
||||||
@Override
|
|
||||||
protected void doRun() {
|
|
||||||
final TableViewer tableViewer = fTableViewer;
|
|
||||||
tableViewer.getTable().getDisplay().asyncExec(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
// Check again if table wasn't disposed when
|
|
||||||
// switching to the display thread.
|
|
||||||
if (tableViewer.getTable().isDisposed()) return; // disposed
|
|
||||||
tableViewer.replace(getData(), fIndex);
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the data query with DataProvider. Must be called on dispatch
|
|
||||||
* thread.
|
|
||||||
* @param index Index of item to fetch.
|
|
||||||
*/
|
|
||||||
private void queryItemData(final int index) {
|
|
||||||
assert fDataProvider.getDsfExecutor().isInExecutorThread();
|
|
||||||
|
|
||||||
ItemGetDataRequestMonitor rm = new ItemGetDataRequestMonitor(fDataProvider.getDsfExecutor(), index);
|
|
||||||
fItemDataRequestMonitors.add(rm);
|
|
||||||
fDataProvider.getItem(index, rm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterates through the outstanding requests to data provider and
|
|
||||||
* cancells any that are nto visible any more.
|
|
||||||
* @param topIdx Top index of the visible items
|
|
||||||
* @param botIdx Bottom index of the visible items
|
|
||||||
*/
|
|
||||||
private void cancelStaleRequests(int topIdx, int botIdx) {
|
|
||||||
// Go through the outstanding requests and cencel any that
|
|
||||||
// are not visible anymore.
|
|
||||||
for (Iterator<ItemGetDataRequestMonitor> itr = fItemDataRequestMonitors.iterator(); itr.hasNext();) {
|
|
||||||
ItemGetDataRequestMonitor item = itr.next();
|
|
||||||
if (item.fIndex < topIdx || item.fIndex > botIdx) {
|
|
||||||
// Set the item to cancelled status, so that the data provider
|
|
||||||
// will ignore it.
|
|
||||||
item.setStatus(new Status(IStatus.CANCEL, DsfExamplesPlugin.PLUGIN_ID, 0, "Cancelled", null)); //$NON-NLS-1$
|
|
||||||
|
|
||||||
// Add the item index to list of indexes that were cancelled,
|
|
||||||
// which will be sent to the table widget.
|
|
||||||
fCancelledIdxs.add(item.fIndex);
|
|
||||||
|
|
||||||
// Remove the item from the outstanding cancel requests.
|
|
||||||
itr.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int cancelRequestsPending = fCancelCallsPending.decrementAndGet();
|
|
||||||
if (!fCancelledIdxs.isEmpty() && cancelRequestsPending == 0) {
|
|
||||||
final Set<Integer> cancelledIdxs = fCancelledIdxs;
|
|
||||||
fCancelledIdxs = new HashSet<Integer>();
|
|
||||||
final TableViewer tableViewer = fTableViewer;
|
|
||||||
tableViewer.getTable().getDisplay().asyncExec(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
// Check again if table wasn't disposed when
|
|
||||||
// switching to the display thread.
|
|
||||||
if (tableViewer.getTable().isDisposed()) return;
|
|
||||||
|
|
||||||
// Clear the indexes of the cancelled request, so that the
|
|
||||||
// viewer knows to request them again when needed.
|
|
||||||
// Note: clearing using TableViewer.clear(int) seems very
|
|
||||||
// inefficient, it's better to use Table.clear(int[]).
|
|
||||||
int[] cancelledIdxsArray = new int[cancelledIdxs.size()];
|
|
||||||
int i = 0;
|
|
||||||
for (Integer index : cancelledIdxs) {
|
|
||||||
cancelledIdxsArray[i++] = index;
|
|
||||||
}
|
|
||||||
tableViewer.getTable().clear(cancelledIdxsArray);
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2006 Wind River Systems and others.
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* Contributors:
|
|
||||||
* Wind River Systems - initial API and implementation
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.eclipse.dd.examples.dsf.concurrent;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
|
||||||
|
|
||||||
public interface DataProvider {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for listeners for changes in Provider's data.
|
|
||||||
*/
|
|
||||||
public interface Listener {
|
|
||||||
/**
|
|
||||||
* Indicates that the count of data items has changed.
|
|
||||||
*/
|
|
||||||
void countChanged();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates that some of the data values have changed.
|
|
||||||
* @param indexes Indexes of the changed items.
|
|
||||||
*/
|
|
||||||
void dataChanged(Set<Integer> indexes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the DSF executor that has to be used to call this data
|
|
||||||
* provider.
|
|
||||||
*/
|
|
||||||
DsfExecutor getDsfExecutor();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the current item count.
|
|
||||||
* @param rm Request monitor, to be filled in with the Integer value.
|
|
||||||
*/
|
|
||||||
void getItemCount(DataRequestMonitor<Integer> rm);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves data value for given index.
|
|
||||||
* @param index Index of the item to retrieve
|
|
||||||
* @param rm Return data token, to be filled in with a String value
|
|
||||||
*/
|
|
||||||
void getItem(int index, DataRequestMonitor<String> rm);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers given listener with data provider.
|
|
||||||
*/
|
|
||||||
void addListener(Listener listener);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes given listener from data provider.
|
|
||||||
*/
|
|
||||||
void removeListener(Listener listener);
|
|
||||||
}
|
|
|
@ -1,408 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2006 Wind River Systems and others.
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* Contributors:
|
|
||||||
* Wind River Systems - initial API and implementation
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.eclipse.dd.examples.dsf.concurrent;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.DelayQueue;
|
|
||||||
import java.util.concurrent.Delayed;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import org.eclipse.core.runtime.IStatus;
|
|
||||||
import org.eclipse.core.runtime.Status;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
|
||||||
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example data provider which has a built-in delay when fetching data. This
|
|
||||||
* data provider simulates a service which retrieves data from an external
|
|
||||||
* source such as a networked target, which incurs a considerable delay when
|
|
||||||
* retrieving data. The data items are simulated values which consist of the
|
|
||||||
* time when data is being retrieved followed by the item's index.
|
|
||||||
* <p>
|
|
||||||
* This version of the data provider features an optimization which causes
|
|
||||||
* item requests to be grouped together even before they are filed into the
|
|
||||||
* processing queue. This example demonstrates how the service can implement
|
|
||||||
* coalescing impelemntation in a situation where the provider has an
|
|
||||||
* interface which only accepts aggregate requests, so the requests have to be
|
|
||||||
* coalesed before they are sent to the provider.
|
|
||||||
*/
|
|
||||||
public class InputCoalescingSlowDataProvider implements DataProvider {
|
|
||||||
|
|
||||||
/** Minimum count of data items */
|
|
||||||
private final static int MIN_COUNT = 1000;
|
|
||||||
|
|
||||||
/** Maximum count of data items */
|
|
||||||
private final static int MAX_COUNT = 2000;
|
|
||||||
|
|
||||||
/** Time interval how often random changes occur. */
|
|
||||||
private final static int RANDOM_CHANGE_MILIS = 10000;
|
|
||||||
|
|
||||||
/** Number of times random changes are made, before count is changed. */
|
|
||||||
private final static int RANDOM_COUNT_CHANGE_INTERVALS = 3;
|
|
||||||
|
|
||||||
/** Percentage of values that is changed upon random change (0-100). */
|
|
||||||
private final static int RANDOM_CHANGE_SET_PERCENTAGE = 10;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Amount of time (in miliseconds) how long the requests to provider, and
|
|
||||||
* events from provider are delayed by.
|
|
||||||
*/
|
|
||||||
private final static int TRANSMISSION_DELAY_TIME = 500;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Amount of time (in milliseconds) how long the provider takes to process
|
|
||||||
* a request.
|
|
||||||
*/
|
|
||||||
private final static int PROCESSING_TIME = 100;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of item requests that can be coalesced into a single
|
|
||||||
* request.
|
|
||||||
*/
|
|
||||||
private final static int COALESCING_COUNT_LIMIT = 10;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delay in processing the buffer of getItem() calls. This delay helps
|
|
||||||
* to ensure that a meaningful number of items is present in the buffer
|
|
||||||
* before the buffer data is coalesced into a request.
|
|
||||||
*/
|
|
||||||
private final static int COALESCING_DELAY_TIME = 10;
|
|
||||||
|
|
||||||
/** Dispatch-thread executor that this provider uses. */
|
|
||||||
private DsfExecutor fExecutor;
|
|
||||||
|
|
||||||
/** List of listeners registered for events from provider. */
|
|
||||||
private List<Listener> fListeners = new LinkedList<Listener>();
|
|
||||||
|
|
||||||
/** Thread that handles data requests. */
|
|
||||||
private ProviderThread fProviderThread;
|
|
||||||
|
|
||||||
/** Queue of currently pending data requests. */
|
|
||||||
private final BlockingQueue<Request> fQueue = new DelayQueue<Request>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runnable to be submitted when the data provider thread is shut down.
|
|
||||||
* This variable acts like a flag: when client want to shut down the
|
|
||||||
* provider, it sets this runnable, and when the backgroun thread sees
|
|
||||||
* that it's set, it shuts itself down, and posts this runnable with
|
|
||||||
* the executor.
|
|
||||||
*/
|
|
||||||
private RequestMonitor fShutdownRequestMonitor = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Buffers for coalescing getItem() calls into a single request.
|
|
||||||
*/
|
|
||||||
private List<Integer> fGetItemIndexesBuffer = new LinkedList<Integer>();
|
|
||||||
private List<DataRequestMonitor<String>> fGetItemRequestMonitorsBuffer = new LinkedList<DataRequestMonitor<String>>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for requests that are queued by the data provider. It
|
|
||||||
* implements java.util.concurrent.Delayed to allow for use of DelayedQueue.
|
|
||||||
* Every request into the queue is delayed by the simulated transmission
|
|
||||||
* time.
|
|
||||||
*/
|
|
||||||
private static abstract class Request implements Delayed {
|
|
||||||
/** Sequence counter and number are used to ensure FIFO order **/
|
|
||||||
private static int fSequenceCounter = 0;
|
|
||||||
private int fSequenceNumber = fSequenceCounter++;
|
|
||||||
|
|
||||||
/** Time delay tracks how items will be delayed. **/
|
|
||||||
private long fTime = System.currentTimeMillis() + TRANSMISSION_DELAY_TIME;
|
|
||||||
|
|
||||||
// @see java.util.concurrent.Delayed
|
|
||||||
public long getDelay(TimeUnit unit) {
|
|
||||||
return unit.convert(fTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @see java.lang.Comparable
|
|
||||||
public int compareTo(Delayed other) {
|
|
||||||
if (other == this) // compare zero ONLY if same object
|
|
||||||
return 0;
|
|
||||||
Request x = (Request)other;
|
|
||||||
long diff = fTime - x.fTime;
|
|
||||||
|
|
||||||
if (diff < 0) return -1;
|
|
||||||
else if (diff > 0) return 1;
|
|
||||||
else if (fSequenceNumber < x.fSequenceNumber) return -1;
|
|
||||||
else return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** All requests have an associated array of RequestMonitor tokens **/
|
|
||||||
abstract RequestMonitor[] getRequestMonitors();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object used to encapsulate the "getItemCount" requests. Instances of it
|
|
||||||
* are queued till processed.
|
|
||||||
*/
|
|
||||||
private static class CountRequest extends Request
|
|
||||||
{
|
|
||||||
DataRequestMonitor<Integer> fRequestMonitors;
|
|
||||||
CountRequest(DataRequestMonitor<Integer> rms) { fRequestMonitors = rms; }
|
|
||||||
@Override
|
|
||||||
DataRequestMonitor<?>[] getRequestMonitors() { return new DataRequestMonitor[] { fRequestMonitors }; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object used to encapsulate the "getItem" requests. Instances of it
|
|
||||||
* are queued till processed.
|
|
||||||
*/
|
|
||||||
private static class ItemRequest extends Request
|
|
||||||
{
|
|
||||||
DataRequestMonitor<String>[] fRequestMonitors;
|
|
||||||
Integer[] fIndexes;
|
|
||||||
ItemRequest(Integer[] indexes, DataRequestMonitor<String>[] rms) { fIndexes = indexes; fRequestMonitors = rms; }
|
|
||||||
@Override
|
|
||||||
DataRequestMonitor<?>[] getRequestMonitors() { return fRequestMonitors; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The background thread of data provider. This thread retrieves the
|
|
||||||
* requests from the provider's queue and processes them. It also
|
|
||||||
* initiates random changes in the data set and issues corresponding
|
|
||||||
* events.
|
|
||||||
*/
|
|
||||||
private class ProviderThread extends Thread
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Current count of items in the data set. It is changed
|
|
||||||
* periodically for simulation purposes.
|
|
||||||
*/
|
|
||||||
private int fCount = MIN_COUNT;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Incremented with every data change, it causes the count to be reset
|
|
||||||
* every four random changes.
|
|
||||||
*/
|
|
||||||
private int fCountTrigger = 0;
|
|
||||||
|
|
||||||
/** Time when the last change was performed. */
|
|
||||||
private long fLastChangeTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
/** Random number generator */
|
|
||||||
private Random fRandom = new java.util.Random();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
// Initialize the count.
|
|
||||||
randomCount();
|
|
||||||
|
|
||||||
// Perform the loop until the shutdown runnable is set.
|
|
||||||
while(fShutdownRequestMonitor == null) {
|
|
||||||
// Get the next request from the queue. The time-out
|
|
||||||
// ensures that that we get to process the random changes.
|
|
||||||
final Request request = fQueue.poll(RANDOM_CHANGE_MILIS / 10, TimeUnit.MILLISECONDS);
|
|
||||||
|
|
||||||
// If a request was dequeued, process it.
|
|
||||||
if (request != null) {
|
|
||||||
// Simulate a processing delay.
|
|
||||||
Thread.sleep(PROCESSING_TIME);
|
|
||||||
|
|
||||||
if (request instanceof CountRequest) {
|
|
||||||
processCountRequest((CountRequest)request);
|
|
||||||
} else if (request instanceof ItemRequest) {
|
|
||||||
processItemRequest((ItemRequest)request);
|
|
||||||
}
|
|
||||||
// Whatever the results, post it to dispatch thread
|
|
||||||
// executor (with transmission delay).
|
|
||||||
fExecutor.schedule(
|
|
||||||
new DsfRunnable() {
|
|
||||||
public void run() {
|
|
||||||
for (RequestMonitor requestMonitor : request.getRequestMonitors()) {
|
|
||||||
requestMonitor.done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate data changes.
|
|
||||||
randomChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (InterruptedException x) {
|
|
||||||
DsfExamplesPlugin.getDefault().getLog().log( new Status(
|
|
||||||
IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, 0, "Interrupted exception in slow data provider thread.", x )); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify the client that requested shutdown, that shutdown is complete.
|
|
||||||
fShutdownRequestMonitor.done();
|
|
||||||
fShutdownRequestMonitor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processCountRequest(CountRequest request) {
|
|
||||||
// Calculate the simulated values.
|
|
||||||
request.fRequestMonitors.setData(fCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processItemRequest(ItemRequest request) {
|
|
||||||
// Check to make sure that the number of indexes matches the number
|
|
||||||
// of return tokens.
|
|
||||||
assert request.fRequestMonitors.length == request.fIndexes.length;
|
|
||||||
|
|
||||||
// Calculate the simulated values for each index in request.
|
|
||||||
for (int i = 0; i < request.fIndexes.length; i++) {
|
|
||||||
request.fRequestMonitors[i].setData(Long.toHexString(fLastChangeTime) + "." + request.fIndexes[i]); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method simulates changes in provider's data set.
|
|
||||||
*/
|
|
||||||
private void randomChanges()
|
|
||||||
{
|
|
||||||
if (System.currentTimeMillis() > fLastChangeTime + RANDOM_CHANGE_MILIS) {
|
|
||||||
fLastChangeTime = System.currentTimeMillis();
|
|
||||||
// once in every 30 seconds broadcast item count change
|
|
||||||
if (++fCountTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0) randomCount();
|
|
||||||
else randomDataChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates new size for provider's data set.
|
|
||||||
*/
|
|
||||||
private void randomCount()
|
|
||||||
{
|
|
||||||
fCount = MIN_COUNT + Math.abs(fRandom.nextInt()) % (MAX_COUNT - MIN_COUNT);
|
|
||||||
|
|
||||||
// Generate the event that the count has changed, and post it to
|
|
||||||
// dispatch thread with transmission delay.
|
|
||||||
fExecutor.schedule(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
for (Listener listener : fListeners) {
|
|
||||||
listener.countChanged();
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidates a random range of indexes.
|
|
||||||
*/
|
|
||||||
private void randomDataChange()
|
|
||||||
{
|
|
||||||
final Set<Integer> set = new HashSet<Integer>();
|
|
||||||
// Change one in ten values.
|
|
||||||
for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) {
|
|
||||||
set.add( new Integer(Math.abs(fRandom.nextInt()) % fCount) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the event that the data has changed.
|
|
||||||
// Post dispatch thread with transmission delay.
|
|
||||||
fExecutor.schedule(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
for (Listener listener : fListeners) {
|
|
||||||
listener.dataChanged(set);
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public InputCoalescingSlowDataProvider(DsfExecutor executor) {
|
|
||||||
fExecutor = executor;
|
|
||||||
fProviderThread = new ProviderThread();
|
|
||||||
fProviderThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requests shutdown of this data provider.
|
|
||||||
* @param requestMonitor Monitor to call when shutdown is complete.
|
|
||||||
*/
|
|
||||||
public void shutdown(RequestMonitor requestMonitor) {
|
|
||||||
fShutdownRequestMonitor = requestMonitor;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// DataProvider
|
|
||||||
public DsfExecutor getDsfExecutor() {
|
|
||||||
return fExecutor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void getItemCount(final DataRequestMonitor<Integer> rm) {
|
|
||||||
fExecutor.schedule(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
fQueue.add(new CountRequest(rm));
|
|
||||||
}},
|
|
||||||
TRANSMISSION_DELAY_TIME,
|
|
||||||
TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void getItem(final int index, final DataRequestMonitor<String> rm) {
|
|
||||||
// Schedule a buffer-servicing call, if one is needed.
|
|
||||||
if (fGetItemIndexesBuffer.isEmpty()) {
|
|
||||||
fExecutor.schedule(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
fileBufferedRequests();
|
|
||||||
}},
|
|
||||||
COALESCING_DELAY_TIME,
|
|
||||||
TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the call data to the buffer.
|
|
||||||
// Note: it doesn't matter that the items were added to the buffer
|
|
||||||
// after the buffer-servicing request was scheduled. This is because
|
|
||||||
// the buffers are guaranteed not to be modified until this dispatch
|
|
||||||
// cycle is over.
|
|
||||||
fGetItemIndexesBuffer.add(index);
|
|
||||||
fGetItemRequestMonitorsBuffer.add(rm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void fileBufferedRequests() {
|
|
||||||
// Remove a number of getItem() calls from the buffer, and combine them
|
|
||||||
// into a request.
|
|
||||||
int numToCoalesce = Math.min(fGetItemIndexesBuffer.size(), COALESCING_COUNT_LIMIT);
|
|
||||||
final ItemRequest request = new ItemRequest(new Integer[numToCoalesce], new DataRequestMonitor[numToCoalesce]);
|
|
||||||
for (int i = 0; i < numToCoalesce; i++) {
|
|
||||||
request.fIndexes[i] = fGetItemIndexesBuffer.remove(0);
|
|
||||||
request.fRequestMonitors[i] = fGetItemRequestMonitorsBuffer.remove(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Queue the coalesced request, with the appropriate transmission delay.
|
|
||||||
fQueue.add(request);
|
|
||||||
|
|
||||||
// If there are still calls left in the buffer, execute another
|
|
||||||
// buffer-servicing call, but without any delay.
|
|
||||||
if (!fGetItemIndexesBuffer.isEmpty()) {
|
|
||||||
fExecutor.execute(new Runnable() { public void run() {
|
|
||||||
fileBufferedRequests();
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addListener(Listener listener) {
|
|
||||||
assert fExecutor.isInExecutorThread();
|
|
||||||
fListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeListener(Listener listener) {
|
|
||||||
assert fExecutor.isInExecutorThread();
|
|
||||||
fListeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2006 Wind River Systems and others.
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* Contributors:
|
|
||||||
* Wind River Systems - initial API and implementation
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.eclipse.dd.examples.dsf.concurrent;
|
|
||||||
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DefaultDsfExecutor;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
|
|
||||||
import org.eclipse.jface.action.IAction;
|
|
||||||
import org.eclipse.jface.dialogs.Dialog;
|
|
||||||
import org.eclipse.ui.IWorkbenchWindow;
|
|
||||||
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
|
|
||||||
import org.eclipse.ui.actions.ActionDelegate;
|
|
||||||
|
|
||||||
public class InputCoalescingSlowDataProviderAction extends ActionDelegate
|
|
||||||
implements IWorkbenchWindowActionDelegate
|
|
||||||
{
|
|
||||||
private IWorkbenchWindow fWindow;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run(IAction action) {
|
|
||||||
if (fWindow != null) {
|
|
||||||
// Create the standard data provider.
|
|
||||||
final InputCoalescingSlowDataProvider dataProvider =
|
|
||||||
new InputCoalescingSlowDataProvider(new DefaultDsfExecutor());
|
|
||||||
|
|
||||||
// Create the dialog and open it.
|
|
||||||
Dialog dialog = new SlowDataProviderDialog(
|
|
||||||
fWindow.getShell(), new SlowDataProviderContentProvider(), dataProvider);
|
|
||||||
dialog.open();
|
|
||||||
|
|
||||||
// Shut down the data provider thread and the DSF executor thread.
|
|
||||||
// Note, since data provider is running in background thread, we have to
|
|
||||||
// wait until this background thread has completed shutdown before
|
|
||||||
// killing the executor thread itself.
|
|
||||||
dataProvider.shutdown(new RequestMonitor(dataProvider.getDsfExecutor(), null) {
|
|
||||||
@Override
|
|
||||||
public void handleCompleted() {
|
|
||||||
dataProvider.getDsfExecutor().shutdown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(IWorkbenchWindow window) {
|
|
||||||
fWindow = window;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,330 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2006 Wind River Systems and others.
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* Contributors:
|
|
||||||
* Wind River Systems - initial API and implementation
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.eclipse.dd.examples.dsf.concurrent;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.DelayQueue;
|
|
||||||
import java.util.concurrent.Delayed;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import org.eclipse.core.runtime.IStatus;
|
|
||||||
import org.eclipse.core.runtime.Status;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
|
||||||
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example data provider which has a built-in delay when fetching data. This
|
|
||||||
* data provider simulates a service which retrieves data from an external
|
|
||||||
* source such as a networked target, which incurs a considerable delay when
|
|
||||||
* retrieving data. The data items are simulated values which consist of the
|
|
||||||
* time when data is being retrieved followed by the item's index.
|
|
||||||
*/
|
|
||||||
public class SlowDataProvider implements DataProvider {
|
|
||||||
|
|
||||||
/** Minimum count of data items */
|
|
||||||
private final static int MIN_COUNT = 1000;
|
|
||||||
|
|
||||||
/** Maximum count of data items */
|
|
||||||
private final static int MAX_COUNT = 2000;
|
|
||||||
|
|
||||||
/** Time interval how often random changes occur. */
|
|
||||||
private final static int RANDOM_CHANGE_MILIS = 10000;
|
|
||||||
|
|
||||||
/** Number of times random changes are made, before count is changed. */
|
|
||||||
private final static int RANDOM_COUNT_CHANGE_INTERVALS = 3;
|
|
||||||
|
|
||||||
/** Percentage of values that is changed upon random change (0-100). */
|
|
||||||
private final static int RANDOM_CHANGE_SET_PERCENTAGE = 10;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Amount of time (in miliseconds) how long the requests to provider, and
|
|
||||||
* events from provider are delayed by.
|
|
||||||
*/
|
|
||||||
private final static int TRANSMISSION_DELAY_TIME = 500;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Amount of time (in milliseconds) how long the provider takes to process
|
|
||||||
* a request.
|
|
||||||
*/
|
|
||||||
private final static int PROCESSING_TIME = 100;
|
|
||||||
|
|
||||||
/** Dispatch-thread executor that this provider uses. */
|
|
||||||
private DsfExecutor fExecutor;
|
|
||||||
|
|
||||||
/** List of listeners registered for events from provider. */
|
|
||||||
private List<Listener> fListeners = new LinkedList<Listener>();
|
|
||||||
|
|
||||||
/** Thread that handles data requests. */
|
|
||||||
private ProviderThread fProviderThread;
|
|
||||||
|
|
||||||
/** Queue of currently pending data requests. */
|
|
||||||
private final BlockingQueue<Request> fQueue = new DelayQueue<Request>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runnable to be submitted when the data provider thread is shut down.
|
|
||||||
* This variable acts like a flag: when client want to shut down the
|
|
||||||
* provider, it sets this runnable, and when the backgroun thread sees
|
|
||||||
* that it's set, it shuts itself down, and posts this runnable with
|
|
||||||
* the executor.
|
|
||||||
*/
|
|
||||||
private RequestMonitor fShutdownRequestMonitor = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for requests that are queued by the data provider. It
|
|
||||||
* implements java.util.concurrent.Delayed to allow for use of DelayedQueue.
|
|
||||||
* Every request into the queue is delayed by the simulated transmission
|
|
||||||
* time.
|
|
||||||
*/
|
|
||||||
private static abstract class Request implements Delayed {
|
|
||||||
/** Sequence counter and number are used to ensure FIFO order **/
|
|
||||||
private static int fSequenceCounter = 0;
|
|
||||||
private int fSequenceNumber = fSequenceCounter++;
|
|
||||||
|
|
||||||
/** Time delay tracks how items will be delayed. **/
|
|
||||||
private long fTime = System.currentTimeMillis() + TRANSMISSION_DELAY_TIME;
|
|
||||||
|
|
||||||
// @see java.util.concurrent.Delayed
|
|
||||||
public long getDelay(TimeUnit unit) {
|
|
||||||
return unit.convert(fTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @see java.lang.Comparable
|
|
||||||
public int compareTo(Delayed other) {
|
|
||||||
if (other == this) // compare zero ONLY if same object
|
|
||||||
return 0;
|
|
||||||
Request x = (Request)other;
|
|
||||||
long diff = fTime - x.fTime;
|
|
||||||
|
|
||||||
if (diff < 0) return -1;
|
|
||||||
else if (diff > 0) return 1;
|
|
||||||
else if (fSequenceNumber < x.fSequenceNumber) return -1;
|
|
||||||
else return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** All requests have an associated RequestMonitor token **/
|
|
||||||
abstract RequestMonitor getRequestMonitor();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object used to encapsulate the "getItemCount" requests. Instances of it
|
|
||||||
* are queued till processed.
|
|
||||||
*/
|
|
||||||
private static class CountRequest extends Request
|
|
||||||
{
|
|
||||||
DataRequestMonitor<Integer> fRequestMonitor;
|
|
||||||
CountRequest(DataRequestMonitor<Integer> rm) { fRequestMonitor = rm; }
|
|
||||||
@Override
|
|
||||||
DataRequestMonitor<?> getRequestMonitor() { return fRequestMonitor; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object used to encapsulate the "getItem" requests. Instances of it
|
|
||||||
* are queued till processed.
|
|
||||||
*/
|
|
||||||
private static class ItemRequest extends Request
|
|
||||||
{
|
|
||||||
DataRequestMonitor<String> fRequestMonitor;
|
|
||||||
int fIndex;
|
|
||||||
ItemRequest(int index, DataRequestMonitor<String> rm) { fIndex = index; fRequestMonitor = rm; }
|
|
||||||
@Override
|
|
||||||
DataRequestMonitor<?> getRequestMonitor() { return fRequestMonitor; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The background thread of data provider. This thread retrieves the
|
|
||||||
* requests from the provider's queue and processes them. It also
|
|
||||||
* initiates random changes in the data set and issues corresponding
|
|
||||||
* events.
|
|
||||||
*/
|
|
||||||
private class ProviderThread extends Thread
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Current count of items in the data set. It is changed
|
|
||||||
* periodically for simulation purposes.
|
|
||||||
*/
|
|
||||||
private int fCount = MIN_COUNT;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Incremented with every data change, it causes the count to be reset
|
|
||||||
* every four random changes.
|
|
||||||
*/
|
|
||||||
private int fCountTrigger = 0;
|
|
||||||
|
|
||||||
/** Time when the last change was performed. */
|
|
||||||
private long fLastChangeTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
/** Random number generator */
|
|
||||||
private Random fRandom = new java.util.Random();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
// Initialize the count.
|
|
||||||
randomCount();
|
|
||||||
|
|
||||||
// Perform the loop until the shutdown runnable is set.
|
|
||||||
while(fShutdownRequestMonitor == null) {
|
|
||||||
// Get the next request from the queue. The time-out
|
|
||||||
// ensures that that we get to process the random changes.
|
|
||||||
final Request request = fQueue.poll(RANDOM_CHANGE_MILIS / 10, TimeUnit.MILLISECONDS);
|
|
||||||
|
|
||||||
// If a request was dequeued, process it.
|
|
||||||
if (request != null) {
|
|
||||||
// Simulate a processing delay.
|
|
||||||
Thread.sleep(PROCESSING_TIME);
|
|
||||||
|
|
||||||
if (request instanceof CountRequest) {
|
|
||||||
processCountRequest((CountRequest)request);
|
|
||||||
} else if (request instanceof ItemRequest) {
|
|
||||||
processItemRequest((ItemRequest)request);
|
|
||||||
}
|
|
||||||
// Whatever the result, post it to dispatch thread
|
|
||||||
// executor (with transmission delay).
|
|
||||||
fExecutor.schedule(
|
|
||||||
new DsfRunnable() {
|
|
||||||
public void run() {
|
|
||||||
request.getRequestMonitor().done();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate data changes.
|
|
||||||
randomChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (InterruptedException x) {
|
|
||||||
DsfExamplesPlugin.getDefault().getLog().log( new Status(
|
|
||||||
IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, 0, "Interrupted exception in slow data provider thread.", x )); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify the client that requested shutdown, that shutdown is complete.
|
|
||||||
fShutdownRequestMonitor.done();
|
|
||||||
fShutdownRequestMonitor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processCountRequest(CountRequest request) {
|
|
||||||
// Calculate the simulated values.
|
|
||||||
request.fRequestMonitor.setData(fCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processItemRequest(ItemRequest request) {
|
|
||||||
// Calculate the simulated values.
|
|
||||||
request.fRequestMonitor.setData(Long.toHexString(fLastChangeTime) + "." + request.fIndex); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method simulates changes in provider's data set.
|
|
||||||
*/
|
|
||||||
private void randomChanges()
|
|
||||||
{
|
|
||||||
if (System.currentTimeMillis() > fLastChangeTime + RANDOM_CHANGE_MILIS) {
|
|
||||||
fLastChangeTime = System.currentTimeMillis();
|
|
||||||
// once in every 30 seconds broadcast item count change
|
|
||||||
if (++fCountTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0) randomCount();
|
|
||||||
else randomDataChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates new size for provider's data set.
|
|
||||||
*/
|
|
||||||
private void randomCount()
|
|
||||||
{
|
|
||||||
fCount = MIN_COUNT + Math.abs(fRandom.nextInt()) % (MAX_COUNT - MIN_COUNT);
|
|
||||||
|
|
||||||
// Generate the event that the count has changed, and post it to
|
|
||||||
// dispatch thread with transmission delay.
|
|
||||||
fExecutor.schedule(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
for (Listener listener : fListeners) {
|
|
||||||
listener.countChanged();
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidates a random range of indexes.
|
|
||||||
*/
|
|
||||||
private void randomDataChange()
|
|
||||||
{
|
|
||||||
final Set<Integer> set = new HashSet<Integer>();
|
|
||||||
// Change one in ten values.
|
|
||||||
for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) {
|
|
||||||
set.add( new Integer(Math.abs(fRandom.nextInt()) % fCount) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the event that the data has changed.
|
|
||||||
// Post dispatch thread with transmission delay.
|
|
||||||
fExecutor.schedule(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
for (Listener listener : fListeners) {
|
|
||||||
listener.dataChanged(set);
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public SlowDataProvider(DsfExecutor executor) {
|
|
||||||
fExecutor = executor;
|
|
||||||
fProviderThread = new ProviderThread();
|
|
||||||
fProviderThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requests shutdown of this data provider.
|
|
||||||
* @param requestMonitor Request completion monitor.
|
|
||||||
*/
|
|
||||||
public void shutdown(RequestMonitor requestMonitor) {
|
|
||||||
fShutdownRequestMonitor = requestMonitor;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// DataProvider
|
|
||||||
public DsfExecutor getDsfExecutor() {
|
|
||||||
return fExecutor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void getItemCount(final DataRequestMonitor<Integer> rm) {
|
|
||||||
fQueue.add(new CountRequest(rm));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void getItem(final int index, final DataRequestMonitor<String> rm) {
|
|
||||||
fQueue.add(new ItemRequest(index, rm));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addListener(Listener listener) {
|
|
||||||
assert fExecutor.isInExecutorThread();
|
|
||||||
fListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeListener(Listener listener) {
|
|
||||||
assert fExecutor.isInExecutorThread();
|
|
||||||
fListeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2006 Wind River Systems and others.
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* Contributors:
|
|
||||||
* Wind River Systems - initial API and implementation
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.eclipse.dd.examples.dsf.concurrent;
|
|
||||||
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DefaultDsfExecutor;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
|
|
||||||
import org.eclipse.jface.action.IAction;
|
|
||||||
import org.eclipse.jface.dialogs.Dialog;
|
|
||||||
import org.eclipse.ui.IWorkbenchWindow;
|
|
||||||
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
|
|
||||||
import org.eclipse.ui.actions.ActionDelegate;
|
|
||||||
|
|
||||||
public class SlowDataProviderAction extends ActionDelegate
|
|
||||||
implements IWorkbenchWindowActionDelegate
|
|
||||||
{
|
|
||||||
private IWorkbenchWindow fWindow;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run(IAction action) {
|
|
||||||
if (fWindow != null) {
|
|
||||||
// Create the standard data provider.
|
|
||||||
final SlowDataProvider dataProvider = new SlowDataProvider(new DefaultDsfExecutor());
|
|
||||||
|
|
||||||
// Create the dialog and open it.
|
|
||||||
Dialog dialog = new SlowDataProviderDialog(
|
|
||||||
fWindow.getShell(), new SlowDataProviderContentProvider(), dataProvider);
|
|
||||||
dialog.open();
|
|
||||||
|
|
||||||
// Shut down the data provider thread and the DSF executor thread.
|
|
||||||
// Note, since data provider is running in background thread, we have to
|
|
||||||
// wait until this background thread has completed shutdown before
|
|
||||||
// killing the executor thread itself.
|
|
||||||
dataProvider.shutdown(new RequestMonitor(dataProvider.getDsfExecutor(), null) {
|
|
||||||
@Override
|
|
||||||
public void handleCompleted() {
|
|
||||||
dataProvider.getDsfExecutor().shutdown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(IWorkbenchWindow window) {
|
|
||||||
fWindow = window;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,190 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2006 Wind River Systems and others.
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* Contributors:
|
|
||||||
* Wind River Systems - initial API and implementation
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.eclipse.dd.examples.dsf.concurrent;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
|
|
||||||
import org.eclipse.jface.viewers.ILazyContentProvider;
|
|
||||||
import org.eclipse.jface.viewers.TableViewer;
|
|
||||||
import org.eclipse.jface.viewers.Viewer;
|
|
||||||
|
|
||||||
public class SlowDataProviderContentProvider
|
|
||||||
implements ILazyContentProvider, DataProvider.Listener
|
|
||||||
{
|
|
||||||
|
|
||||||
TableViewer fTableViewer;
|
|
||||||
DataProvider fDataProvider;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// ILazyContentProvider
|
|
||||||
public void dispose() {
|
|
||||||
if (fDataProvider != null) {
|
|
||||||
final DataProvider dataProvider = fDataProvider;
|
|
||||||
dataProvider.getDsfExecutor().execute(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
dataProvider.removeListener(SlowDataProviderContentProvider.this);
|
|
||||||
fTableViewer = null;
|
|
||||||
fDataProvider = null;
|
|
||||||
}});
|
|
||||||
} else {
|
|
||||||
fTableViewer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void inputChanged(final Viewer viewer, Object oldInput, final Object newInput) {
|
|
||||||
// If old data provider is not-null, unregister from it as listener.
|
|
||||||
if (fDataProvider != null) {
|
|
||||||
final DataProvider dataProvider = fDataProvider;
|
|
||||||
dataProvider.getDsfExecutor().execute(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
dataProvider.removeListener(SlowDataProviderContentProvider.this);
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Register as listener with new data provider.
|
|
||||||
// Note: if old data provider and new data provider use different executors,
|
|
||||||
// there is a chance of a race condition here.
|
|
||||||
if (newInput != null) {
|
|
||||||
((DataProvider)newInput).getDsfExecutor().execute(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
fTableViewer = (TableViewer)viewer;
|
|
||||||
fDataProvider = (DataProvider)newInput;
|
|
||||||
fDataProvider.addListener(SlowDataProviderContentProvider.this);
|
|
||||||
queryItemCount();
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateElement(final int index) {
|
|
||||||
assert fTableViewer != null;
|
|
||||||
if (fDataProvider == null) return;
|
|
||||||
|
|
||||||
fDataProvider.getDsfExecutor().execute(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
// Must check again, in case disposed while re-dispatching.
|
|
||||||
if (fDataProvider == null) return;
|
|
||||||
|
|
||||||
queryItemData(index);
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// DataProvider.Listener
|
|
||||||
public void countChanged() {
|
|
||||||
// Check for dispose.
|
|
||||||
if (fDataProvider == null) return;
|
|
||||||
|
|
||||||
// Request new count.
|
|
||||||
queryItemCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dataChanged(final Set<Integer> indexes) {
|
|
||||||
// Check for dispose.
|
|
||||||
if (fDataProvider == null) return;
|
|
||||||
|
|
||||||
// Clear changed items in table viewer.
|
|
||||||
if (fTableViewer != null) {
|
|
||||||
final TableViewer tableViewer = fTableViewer;
|
|
||||||
tableViewer.getTable().getDisplay().asyncExec(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
// Check again if table wasn't disposed when
|
|
||||||
// switching to the display thread.
|
|
||||||
if (tableViewer.getTable().isDisposed()) return; // disposed
|
|
||||||
for (Integer index : indexes) {
|
|
||||||
tableViewer.clear(index);
|
|
||||||
}
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience extension to standard data return runnable. This extension
|
|
||||||
* automatically checks for errors and asynchronous dipose.
|
|
||||||
* @param <V>
|
|
||||||
*/
|
|
||||||
private abstract class CPGetDataRequestMonitor<V> extends DataRequestMonitor<V> {
|
|
||||||
CPGetDataRequestMonitor(DsfExecutor executor) { super(executor, null); }
|
|
||||||
abstract protected void doRun();
|
|
||||||
@Override
|
|
||||||
final public void handleCompleted() {
|
|
||||||
// If there is an error processing request, return.
|
|
||||||
if (!getStatus().isOK()) return;
|
|
||||||
|
|
||||||
// If content provider was disposed, return.
|
|
||||||
if (fTableViewer == null) return;
|
|
||||||
|
|
||||||
// Otherwise execute runnable.
|
|
||||||
doRun();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the item count query with DataProvider. Must be called on
|
|
||||||
* data provider's dispatch thread.
|
|
||||||
*/
|
|
||||||
private void queryItemCount() {
|
|
||||||
assert fDataProvider.getDsfExecutor().isInExecutorThread();
|
|
||||||
|
|
||||||
// Request coumt from data provider. When the count is returned, we
|
|
||||||
// have to re-dispatch into the display thread to avoid calling
|
|
||||||
// the table widget on the DSF dispatch thread.
|
|
||||||
fDataProvider.getItemCount(
|
|
||||||
new CPGetDataRequestMonitor<Integer>(fDataProvider.getDsfExecutor()) {
|
|
||||||
@Override
|
|
||||||
protected void doRun() {
|
|
||||||
final TableViewer tableViewer = fTableViewer;
|
|
||||||
tableViewer.getTable().getDisplay().asyncExec(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
// Check again if table wasn't disposed when
|
|
||||||
// switching to the display thread.
|
|
||||||
if (tableViewer.getTable().isDisposed()) return; // disposed
|
|
||||||
tableViewer.setItemCount(getData());
|
|
||||||
tableViewer.getTable().clearAll();
|
|
||||||
}});
|
|
||||||
}});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the data query with DataProvider. Must be called on dispatch
|
|
||||||
* thread.
|
|
||||||
* @param index Index of item to fetch.
|
|
||||||
*/
|
|
||||||
private void queryItemData(final int index) {
|
|
||||||
assert fDataProvider.getDsfExecutor().isInExecutorThread();
|
|
||||||
|
|
||||||
// Request data from data provider. Likewise, when the data is
|
|
||||||
// returned, we have to re-dispatch into the display thread to
|
|
||||||
// call the table widget.
|
|
||||||
fDataProvider.getItem(
|
|
||||||
index,
|
|
||||||
new CPGetDataRequestMonitor<String>(fDataProvider.getDsfExecutor()) {
|
|
||||||
@Override
|
|
||||||
protected void doRun() {
|
|
||||||
final TableViewer tableViewer = fTableViewer;
|
|
||||||
tableViewer.getTable().getDisplay().asyncExec(
|
|
||||||
new Runnable() { public void run() {
|
|
||||||
// Check again if table wasn't disposed when
|
|
||||||
// switching to the display thread.
|
|
||||||
if (tableViewer.getTable().isDisposed()) return; // disposed
|
|
||||||
tableViewer.replace(getData(), index);
|
|
||||||
}});
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2006 Wind River Systems and others.
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* Contributors:
|
|
||||||
* Wind River Systems - initial API and implementation
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.eclipse.dd.examples.dsf.concurrent;
|
|
||||||
|
|
||||||
import org.eclipse.jface.dialogs.Dialog;
|
|
||||||
import org.eclipse.jface.viewers.IContentProvider;
|
|
||||||
import org.eclipse.jface.viewers.TableViewer;
|
|
||||||
import org.eclipse.swt.SWT;
|
|
||||||
import org.eclipse.swt.layout.GridData;
|
|
||||||
import org.eclipse.swt.widgets.Composite;
|
|
||||||
import org.eclipse.swt.widgets.Control;
|
|
||||||
import org.eclipse.swt.widgets.Shell;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dialog shared by all slow data provider examples. It accepts the data
|
|
||||||
* provider and the content provider as arguments to the constructor. So the
|
|
||||||
* only thing that the dialog does is to create the table viewer and
|
|
||||||
* initialize it with the providers.
|
|
||||||
*/
|
|
||||||
public class SlowDataProviderDialog extends Dialog {
|
|
||||||
|
|
||||||
private TableViewer fDataViewer;
|
|
||||||
private DataProvider fDataProvider;
|
|
||||||
private IContentProvider fContentProvider;
|
|
||||||
|
|
||||||
public SlowDataProviderDialog(Shell parent, IContentProvider contentProvider, DataProvider dataProvider) {
|
|
||||||
super(parent);
|
|
||||||
setShellStyle(getShellStyle() | SWT.RESIZE);
|
|
||||||
fContentProvider = contentProvider;
|
|
||||||
fDataProvider = dataProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Control createDialogArea(Composite parent) {
|
|
||||||
Composite area = (Composite) super.createDialogArea(parent);
|
|
||||||
fDataViewer = new TableViewer(area, SWT.VIRTUAL);
|
|
||||||
fDataViewer.getTable().setLayoutData(new GridData(GridData.FILL_BOTH));
|
|
||||||
fDataViewer.setContentProvider(fContentProvider);
|
|
||||||
fDataViewer.setInput(fDataProvider);
|
|
||||||
return area;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,266 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2006, 2008 Wind River Systems and others.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Wind River Systems - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.dd.examples.dsf.dataviewer;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.ImmediateExecutor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.Query;
|
||||||
|
import org.eclipse.dd.dsf.ui.concurrent.DisplayDsfExecutor;
|
||||||
|
import org.eclipse.jface.viewers.ILazyContentProvider;
|
||||||
|
import org.eclipse.jface.viewers.TableViewer;
|
||||||
|
import org.eclipse.jface.viewers.Viewer;
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.graphics.Font;
|
||||||
|
import org.eclipse.swt.layout.GridData;
|
||||||
|
import org.eclipse.swt.layout.GridLayout;
|
||||||
|
import org.eclipse.swt.widgets.Display;
|
||||||
|
import org.eclipse.swt.widgets.Shell;
|
||||||
|
import org.eclipse.swt.widgets.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data viewer based on a table, which reads data using asynchronous methods.
|
||||||
|
* <p>
|
||||||
|
* This viewer implements the {@link ILazyContentProvider} interface
|
||||||
|
* which is used by the JFace TableViewer class to populate a Table. This
|
||||||
|
* interface contains separate asynchronous methods for requesting the count
|
||||||
|
* and values for individual indexes, which neatly correspond to the methods
|
||||||
|
* in {@link IDataGenerator}. As an added optimization, this viewer
|
||||||
|
* implementation checks for the range of visible items in the view upon each
|
||||||
|
* request, and it cancels old requests which scroll out of view but have not
|
||||||
|
* been completed yet. However, it is up to the data generator implementation
|
||||||
|
* to check the canceled state of the requests and ignore them.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class AsyncDataViewer
|
||||||
|
implements ILazyContentProvider, IDataGenerator.Listener
|
||||||
|
{
|
||||||
|
// Executor to use instead of Display.asyncExec().
|
||||||
|
final private DsfExecutor fDisplayExecutor;
|
||||||
|
|
||||||
|
// The viewer and generator that this content provider using.
|
||||||
|
final private TableViewer fViewer;
|
||||||
|
final private IDataGenerator fDataGenerator;
|
||||||
|
|
||||||
|
// Fields used in request cancellation logic.
|
||||||
|
private List<ValueDataRequestMonitor> fItemDataRequestMonitors = new LinkedList<ValueDataRequestMonitor>();
|
||||||
|
private Set<Integer> fIndexesToCancel = new HashSet<Integer>();
|
||||||
|
private int fCancelCallsPending = 0;
|
||||||
|
|
||||||
|
public AsyncDataViewer(TableViewer viewer, IDataGenerator generator) {
|
||||||
|
fViewer = viewer;
|
||||||
|
fDisplayExecutor = DisplayDsfExecutor.getDisplayDsfExecutor(fViewer.getTable().getDisplay());
|
||||||
|
fDataGenerator = generator;
|
||||||
|
fDataGenerator.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
fDataGenerator.removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
|
||||||
|
// Set the initial count to the viewer after the input is set.
|
||||||
|
queryItemCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateElement(final int index) {
|
||||||
|
// Calculate the visible index range.
|
||||||
|
final int topIdx = fViewer.getTable().getTopIndex();
|
||||||
|
final int botIdx = topIdx + getVisibleItemCount(topIdx);
|
||||||
|
|
||||||
|
// Request the item for the given index.
|
||||||
|
queryValue(index);
|
||||||
|
|
||||||
|
// Invoke a cancel task with a delay. The delay allows multiple cancel
|
||||||
|
// calls to be combined together improving performance of the viewer.
|
||||||
|
fCancelCallsPending++;
|
||||||
|
fDisplayExecutor.schedule(
|
||||||
|
new Runnable() { public void run() {
|
||||||
|
cancelStaleRequests(topIdx, botIdx);
|
||||||
|
}},
|
||||||
|
1, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getVisibleItemCount(int top) {
|
||||||
|
Table table = fViewer.getTable();
|
||||||
|
int itemCount = table.getItemCount();
|
||||||
|
return Math.min((table.getBounds().height / table.getItemHeight()) + 2, itemCount - top);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void countChanged() {
|
||||||
|
queryItemCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void valuesChanged(final Set<Integer> indexes) {
|
||||||
|
// Mark the changed items in table viewer as dirty, this will
|
||||||
|
// trigger update requests for these indexes if they are
|
||||||
|
// visible in the viewer.
|
||||||
|
final TableViewer tableViewer = fViewer;
|
||||||
|
fDisplayExecutor.execute( new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
if (!fViewer.getTable().isDisposed()) {
|
||||||
|
for (Integer index : indexes) {
|
||||||
|
tableViewer.clear(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void queryItemCount() {
|
||||||
|
// Request count from data provider. When the count is returned, we
|
||||||
|
// have to re-dispatch into the display thread to avoid calling
|
||||||
|
// the table widget on the DSF dispatch thread.
|
||||||
|
fIndexesToCancel.clear();
|
||||||
|
fDataGenerator.getCount(
|
||||||
|
// Use the display executor to construct the request monitor, this
|
||||||
|
// will cause the handleCompleted() method to be automatically
|
||||||
|
// called on the display thread.
|
||||||
|
new DataRequestMonitor<Integer>(fDisplayExecutor, null) {
|
||||||
|
@Override
|
||||||
|
protected void handleCompleted() {
|
||||||
|
if (!fViewer.getTable().isDisposed()) {
|
||||||
|
fViewer.setItemCount(getData());
|
||||||
|
fViewer.getTable().clearAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Dedicated class for data item requests. This class holds the index
|
||||||
|
// argument so it can be examined when canceling stale requests.
|
||||||
|
private class ValueDataRequestMonitor extends DataRequestMonitor<String> {
|
||||||
|
|
||||||
|
/** Index is used when canceling stale requests. */
|
||||||
|
int fIndex;
|
||||||
|
|
||||||
|
ValueDataRequestMonitor(int index) {
|
||||||
|
super(fDisplayExecutor, null);
|
||||||
|
fIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleCompleted() {
|
||||||
|
fItemDataRequestMonitors.remove(this);
|
||||||
|
|
||||||
|
// Check if the request completed successfully, otherwise ignore it.
|
||||||
|
if (getStatus().isOK()) {
|
||||||
|
if (!fViewer.getTable().isDisposed()) {
|
||||||
|
fViewer.replace(getData(), fIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void queryValue(final int index) {
|
||||||
|
ValueDataRequestMonitor rm = new ValueDataRequestMonitor(index);
|
||||||
|
fItemDataRequestMonitors.add(rm);
|
||||||
|
fDataGenerator.getValue(index, rm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelStaleRequests(int topIdx, int botIdx) {
|
||||||
|
// Decrement the count of outstanding cancel calls.
|
||||||
|
fCancelCallsPending--;
|
||||||
|
|
||||||
|
// Must check again, in case disposed while re-dispatching.
|
||||||
|
if (fDataGenerator == null || fViewer.getTable().isDisposed()) return;
|
||||||
|
|
||||||
|
// Go through the outstanding requests and cancel any that
|
||||||
|
// are not visible anymore.
|
||||||
|
for (Iterator<ValueDataRequestMonitor> itr = fItemDataRequestMonitors.iterator(); itr.hasNext();) {
|
||||||
|
ValueDataRequestMonitor item = itr.next();
|
||||||
|
if (item.fIndex < topIdx || item.fIndex > botIdx) {
|
||||||
|
// Set the item to canceled status, so that the data provider
|
||||||
|
// will ignore it.
|
||||||
|
item.setCanceled(true);
|
||||||
|
|
||||||
|
// Add the item index to list of indexes that were canceled,
|
||||||
|
// which will be sent to the table widget.
|
||||||
|
fIndexesToCancel.add(item.fIndex);
|
||||||
|
|
||||||
|
// Remove the item from the outstanding cancel requests.
|
||||||
|
itr.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!fIndexesToCancel.isEmpty() && fCancelCallsPending == 0) {
|
||||||
|
Set<Integer> canceledIdxs = fIndexesToCancel;
|
||||||
|
fIndexesToCancel = new HashSet<Integer>();
|
||||||
|
|
||||||
|
// Clear the indexes of the canceled request, so that the
|
||||||
|
// viewer knows to request them again when needed.
|
||||||
|
// Note: clearing using TableViewer.clear(int) seems very
|
||||||
|
// inefficient, it's better to use Table.clear(int[]).
|
||||||
|
int[] canceledIdxsArray = new int[canceledIdxs.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (Integer index : canceledIdxs) {
|
||||||
|
canceledIdxsArray[i++] = index;
|
||||||
|
}
|
||||||
|
fViewer.getTable().clear(canceledIdxsArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Create the shell to hold the viewer.
|
||||||
|
Display display = new Display();
|
||||||
|
Shell shell = new Shell(display, SWT.SHELL_TRIM);
|
||||||
|
shell.setLayout(new GridLayout());
|
||||||
|
GridData data = new GridData(GridData.FILL_BOTH);
|
||||||
|
shell.setLayoutData(data);
|
||||||
|
Font font = new Font(display, "Courier", 10, SWT.NORMAL);
|
||||||
|
|
||||||
|
// Create the table viewer.
|
||||||
|
TableViewer tableViewer = new TableViewer(shell, SWT.BORDER | SWT.VIRTUAL);
|
||||||
|
tableViewer.getControl().setLayoutData(data);
|
||||||
|
|
||||||
|
// Create the data generator.
|
||||||
|
final IDataGenerator generator = new DataGeneratorWithExecutor();
|
||||||
|
|
||||||
|
// Create the content provider which will populate the viewer.
|
||||||
|
AsyncDataViewer contentProvider = new AsyncDataViewer(tableViewer, generator);
|
||||||
|
tableViewer.setContentProvider(contentProvider);
|
||||||
|
tableViewer.setInput(new Object());
|
||||||
|
|
||||||
|
// Open the shell and service the display dispatch loop until user
|
||||||
|
// closes the shell.
|
||||||
|
shell.open();
|
||||||
|
while (!shell.isDisposed()) {
|
||||||
|
if (!display.readAndDispatch())
|
||||||
|
display.sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The IDataGenerator.shutdown() method is asynchronous, this requires
|
||||||
|
// using a query again in order to wait for its completion.
|
||||||
|
Query<Object> shutdownQuery = new Query<Object>() {
|
||||||
|
@Override
|
||||||
|
protected void execute(DataRequestMonitor<Object> rm) {
|
||||||
|
generator.shutdown(rm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ImmediateExecutor.getInstance().execute(shutdownQuery);
|
||||||
|
try {
|
||||||
|
shutdownQuery.get();
|
||||||
|
} catch (Exception e) {}
|
||||||
|
|
||||||
|
// Shut down the display.
|
||||||
|
font.dispose();
|
||||||
|
display.dispose();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,404 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2006 Wind River Systems and others.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Wind River Systems - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
//#ifdef excercises
|
||||||
|
package org.eclipse.dd.examples.dsf.dataviewer;
|
||||||
|
//#else
|
||||||
|
//#package org.eclipse.dd.examples.dsf.dataviewer.answers;
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.eclipse.core.runtime.IStatus;
|
||||||
|
import org.eclipse.core.runtime.Status;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.ConfinedToDsfExecutor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.DefaultDsfExecutor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.Immutable;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.ThreadSafe;
|
||||||
|
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DSF Executor-based implementation of the data generator.
|
||||||
|
* <p>
|
||||||
|
* This generator uses a queue of client requests and processes these
|
||||||
|
* requests periodically using a DSF executor. The main feature of this
|
||||||
|
* generator is that it uses the executor as its only synchronization object.
|
||||||
|
* This means that all the fields with the exception of the executor can only
|
||||||
|
* be accessed while running in the executor thread.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//#@ThreadSafe
|
||||||
|
//#endif
|
||||||
|
public class DataGeneratorWithExecutor implements IDataGenerator {
|
||||||
|
|
||||||
|
// Request objects are used to serialize the interface calls into objects
|
||||||
|
// which can then be pushed into a queue.
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @Immutable
|
||||||
|
//#endif
|
||||||
|
abstract class Request {
|
||||||
|
final RequestMonitor fRequestMonitor;
|
||||||
|
|
||||||
|
Request(RequestMonitor rm) {
|
||||||
|
fRequestMonitor = rm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @Immutable
|
||||||
|
//#endif
|
||||||
|
class CountRequest extends Request {
|
||||||
|
CountRequest(DataRequestMonitor<Integer> rm) {
|
||||||
|
super(rm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @Immutable
|
||||||
|
//#endif
|
||||||
|
class ItemRequest extends Request {
|
||||||
|
final int fIndex;
|
||||||
|
ItemRequest(int index, DataRequestMonitor<String> rm) {
|
||||||
|
super(rm);
|
||||||
|
fIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The executor used to access all internal data of the generator.
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotation indicating allowed concurrency access
|
||||||
|
//#endif
|
||||||
|
private DsfExecutor fExecutor;
|
||||||
|
|
||||||
|
// Main request queue of the data generator. The getValue(), getCount(),
|
||||||
|
// and shutdown() methods write into the queue, while the serviceQueue()
|
||||||
|
// method reads from it.
|
||||||
|
// The executor used to access all internal data of the generator.
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @ConfinedToDsfExecutor("fExecutor")
|
||||||
|
//#endif
|
||||||
|
private List<Request> fQueue = new LinkedList<Request>();
|
||||||
|
|
||||||
|
// List of listeners is not synchronized, it also has to be accessed
|
||||||
|
// using the executor.
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @ConfinedToDsfExecutor("fExecutor")
|
||||||
|
//#endif
|
||||||
|
private List<Listener> fListeners = new LinkedList<Listener>();
|
||||||
|
|
||||||
|
// Current number of elements in this generator.
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @ConfinedToDsfExecutor("fExecutor")
|
||||||
|
//#endif
|
||||||
|
private int fCount = MIN_COUNT;
|
||||||
|
|
||||||
|
// Counter used to determine when to reset the element count.
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @ConfinedToDsfExecutor("fExecutor")
|
||||||
|
//#endif
|
||||||
|
private int fCountResetTrigger = 0;
|
||||||
|
|
||||||
|
// Elements which were modified since the last reset.
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @ConfinedToDsfExecutor("fExecutor")
|
||||||
|
//#endif
|
||||||
|
private Set<Integer> fChangedIndexes = new HashSet<Integer>();
|
||||||
|
|
||||||
|
// Flag used to ensure that requests are processed sequentially.
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @ConfinedToDsfExecutor("fExecutor")
|
||||||
|
//#endif
|
||||||
|
private boolean fServiceQueueInProgress = false;
|
||||||
|
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotation indicating allowed concurrency access
|
||||||
|
//#endif
|
||||||
|
public DataGeneratorWithExecutor() {
|
||||||
|
// Create the executor
|
||||||
|
fExecutor = new DefaultDsfExecutor("Supplier Executor");
|
||||||
|
|
||||||
|
// Schedule a runnable to make the random changes.
|
||||||
|
fExecutor.scheduleAtFixedRate(
|
||||||
|
new DsfRunnable() {
|
||||||
|
public void run() {
|
||||||
|
randomChanges();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RANDOM_CHANGE_INTERVAL,
|
||||||
|
RANDOM_CHANGE_INTERVAL,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotation indicating allowed concurrency access
|
||||||
|
//#endif
|
||||||
|
public void shutdown(final RequestMonitor rm) {
|
||||||
|
try {
|
||||||
|
fExecutor.execute( new DsfRunnable() {
|
||||||
|
public void run() {
|
||||||
|
// Empty the queue of requests and fail them.
|
||||||
|
for (Request request : fQueue) {
|
||||||
|
request.fRequestMonitor.setStatus(
|
||||||
|
new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
|
||||||
|
request.fRequestMonitor.done();
|
||||||
|
}
|
||||||
|
fQueue.clear();
|
||||||
|
|
||||||
|
// Kill executor.
|
||||||
|
fExecutor.shutdown();
|
||||||
|
rm.done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (RejectedExecutionException e) {
|
||||||
|
rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
|
||||||
|
rm.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotation indicating allowed concurrency access
|
||||||
|
//#endif
|
||||||
|
public void getCount(final DataRequestMonitor<Integer> rm) {
|
||||||
|
try {
|
||||||
|
fExecutor.execute( new DsfRunnable() {
|
||||||
|
public void run() {
|
||||||
|
fQueue.add(new CountRequest(rm));
|
||||||
|
serviceQueue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (RejectedExecutionException e) {
|
||||||
|
rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
|
||||||
|
rm.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotation indicating allowed concurrency access
|
||||||
|
//#endif
|
||||||
|
public void getValue(final int index, final DataRequestMonitor<String> rm) {
|
||||||
|
try {
|
||||||
|
fExecutor.execute( new DsfRunnable() {
|
||||||
|
public void run() {
|
||||||
|
fQueue.add(new ItemRequest(index, rm));
|
||||||
|
serviceQueue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (RejectedExecutionException e) {
|
||||||
|
rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
|
||||||
|
rm.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotation indicating allowed concurrency access
|
||||||
|
//#endif
|
||||||
|
public void addListener(final Listener listener) {
|
||||||
|
try {
|
||||||
|
fExecutor.execute( new DsfRunnable() {
|
||||||
|
public void run() {
|
||||||
|
fListeners.add(listener);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (RejectedExecutionException e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotation indicating allowed concurrency access
|
||||||
|
//#endif
|
||||||
|
public void removeListener(final Listener listener) {
|
||||||
|
try {
|
||||||
|
fExecutor.execute( new DsfRunnable() {
|
||||||
|
public void run() {
|
||||||
|
fListeners.remove(listener);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (RejectedExecutionException e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main processing function of this generator.
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @ConfinedToDsfExecutor("fExecutor")
|
||||||
|
//#endif
|
||||||
|
private void serviceQueue() {
|
||||||
|
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 4 - Add logic to discard requests from queue.
|
||||||
|
//#else
|
||||||
|
//# for (Iterator<Request> requestItr = fQueue.iterator(); requestItr.hasNext();) {
|
||||||
|
//# Request request = requestItr.next();
|
||||||
|
//# if (request.fRequestMonitor.isCanceled()) {
|
||||||
|
//# request.fRequestMonitor.setStatus(
|
||||||
|
//# new Status(IStatus.CANCEL, DsfExamplesPlugin.PLUGIN_ID, "Request canceled"));
|
||||||
|
//# request.fRequestMonitor.done();
|
||||||
|
//# requestItr.remove();
|
||||||
|
//# }
|
||||||
|
//# }
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
// If a queue servicing is already scheduled, do nothing.
|
||||||
|
if (fServiceQueueInProgress) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fQueue.size() != 0) {
|
||||||
|
// If there are requests to service, remove one from the queue and
|
||||||
|
// schedule a runnable to process the request after a processing
|
||||||
|
// delay.
|
||||||
|
fServiceQueueInProgress = true;
|
||||||
|
final Request request = fQueue.remove(0);
|
||||||
|
fExecutor.schedule(
|
||||||
|
new DsfRunnable() {
|
||||||
|
public void run() {
|
||||||
|
if (request instanceof CountRequest) {
|
||||||
|
processCountRequest((CountRequest)request);
|
||||||
|
} else if (request instanceof ItemRequest) {
|
||||||
|
processItemRequest((ItemRequest)request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the processing flag and process next
|
||||||
|
// request.
|
||||||
|
fServiceQueueInProgress = false;
|
||||||
|
serviceQueue();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PROCESSING_DELAY, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @ConfinedToDsfExecutor("fExecutor")
|
||||||
|
//#endif
|
||||||
|
private void processCountRequest(CountRequest request) {
|
||||||
|
@SuppressWarnings("unchecked") // Suppress warning about lost type info.
|
||||||
|
DataRequestMonitor<Integer> rm = (DataRequestMonitor<Integer>)request.fRequestMonitor;
|
||||||
|
|
||||||
|
rm.setData(fCount);
|
||||||
|
rm.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @ConfinedToDsfExecutor("fExecutor")
|
||||||
|
//#endif
|
||||||
|
private void processItemRequest(ItemRequest request) {
|
||||||
|
@SuppressWarnings("unchecked") // Suppress warning about lost type info.
|
||||||
|
DataRequestMonitor<String> rm = (DataRequestMonitor<String>)request.fRequestMonitor;
|
||||||
|
|
||||||
|
if (fChangedIndexes.contains(request.fIndex)) {
|
||||||
|
rm.setData("Changed: " + request.fIndex);
|
||||||
|
} else {
|
||||||
|
rm.setData(Integer.toString(request.fIndex));
|
||||||
|
}
|
||||||
|
rm.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method simulates changes in the supplier's data set.
|
||||||
|
*/
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @ConfinedToDsfExecutor("fExecutor")
|
||||||
|
//#endif
|
||||||
|
private void randomChanges() {
|
||||||
|
// Once every number of changes, reset the count, the rest of the
|
||||||
|
// times just change certain values.
|
||||||
|
if (++fCountResetTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0){
|
||||||
|
randomCountReset();
|
||||||
|
} else {
|
||||||
|
randomDataChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates new size for provider's data set.
|
||||||
|
*/
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @ConfinedToDsfExecutor("fExecutor")
|
||||||
|
//#endif
|
||||||
|
private void randomCountReset() {
|
||||||
|
// Calculate the new count.
|
||||||
|
Random random = new java.util.Random();
|
||||||
|
fCount = MIN_COUNT + Math.abs(random.nextInt()) % (MAX_COUNT - MIN_COUNT);
|
||||||
|
|
||||||
|
// Reset the changed values.
|
||||||
|
fChangedIndexes.clear();
|
||||||
|
|
||||||
|
// Notify listeners
|
||||||
|
for (Listener listener : fListeners) {
|
||||||
|
listener.countChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates a random range of indexes.
|
||||||
|
*/
|
||||||
|
//#ifdef excercises
|
||||||
|
// TODO Excercise 3 - Add an annotationindicating allowed concurrency access
|
||||||
|
//#else
|
||||||
|
//# @ConfinedToDsfExecutor("fExecutor")
|
||||||
|
//#endif
|
||||||
|
private void randomDataChange() {
|
||||||
|
// Calculate the indexes to change.
|
||||||
|
Random random = new java.util.Random();
|
||||||
|
Set<Integer> set = new HashSet<Integer>();
|
||||||
|
for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) {
|
||||||
|
set.add( new Integer(Math.abs(random.nextInt()) % fCount) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the indexes to an overall set of changed indexes.
|
||||||
|
fChangedIndexes.addAll(set);
|
||||||
|
|
||||||
|
// Notify listeners
|
||||||
|
for (Listener listener : fListeners) {
|
||||||
|
listener.valuesChanged(set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2006 Wind River Systems and others.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Wind River Systems - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.dd.examples.dsf.dataviewer;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import org.eclipse.core.runtime.IStatus;
|
||||||
|
import org.eclipse.core.runtime.ListenerList;
|
||||||
|
import org.eclipse.core.runtime.Status;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
|
||||||
|
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread-based implementation of the data generator.
|
||||||
|
* <p>
|
||||||
|
* This generator is based around a queue of client requests and a thread which
|
||||||
|
* reads the requests from the queue and processes them. The distinguishing
|
||||||
|
* feature of this generator is that it uses a a blocking queue as the main
|
||||||
|
* synchronization object. However, fListeners, fShutdown, and fChangedIndexes
|
||||||
|
* fields also need to be thread-safe and so they implement their own
|
||||||
|
* synchronization.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class DataGeneratorWithThread extends Thread implements IDataGenerator {
|
||||||
|
|
||||||
|
// Request objects are used to serialize the interface calls into objects
|
||||||
|
// which can then be pushed into a queue.
|
||||||
|
abstract class Request {
|
||||||
|
final RequestMonitor fRequestMonitor;
|
||||||
|
|
||||||
|
Request(RequestMonitor rm) {
|
||||||
|
fRequestMonitor = rm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CountRequest extends Request {
|
||||||
|
CountRequest(DataRequestMonitor<Integer> rm) {
|
||||||
|
super(rm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ItemRequest extends Request {
|
||||||
|
final int fIndex;
|
||||||
|
ItemRequest(int index, DataRequestMonitor<String> rm) {
|
||||||
|
super(rm);
|
||||||
|
fIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShutdownRequest extends Request {
|
||||||
|
ShutdownRequest(RequestMonitor rm) {
|
||||||
|
super(rm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main request queue of the data generator. The getValue(), getCount(),
|
||||||
|
// and shutdown() methods write into the queue, while the run() method
|
||||||
|
// reads from it.
|
||||||
|
private final BlockingQueue<Request> fQueue = new LinkedBlockingQueue<Request>();
|
||||||
|
|
||||||
|
// ListenerList class provides thread safety.
|
||||||
|
private ListenerList fListeners = new ListenerList();
|
||||||
|
|
||||||
|
// Current number of elements in this generator.
|
||||||
|
private int fCount = MIN_COUNT;
|
||||||
|
|
||||||
|
// Counter used to determine when to reset the element count.
|
||||||
|
private int fCountResetTrigger = 0;
|
||||||
|
|
||||||
|
// Elements which were modified since the last reset.
|
||||||
|
private Set<Integer> fChangedIndexes = Collections.synchronizedSet(new HashSet<Integer>());
|
||||||
|
|
||||||
|
// Used to determine when to make changes in data.
|
||||||
|
private long fLastChangeTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// Flag indicating when the generator has been shut down.
|
||||||
|
private AtomicBoolean fShutdown = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
public DataGeneratorWithThread() {
|
||||||
|
// Immediately kick off the request processing thread.
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown(RequestMonitor rm) {
|
||||||
|
// Mark the generator as shut down. After the fShutdown flag is set,
|
||||||
|
// all new requests should be shut down.
|
||||||
|
if (!fShutdown.getAndSet(true)) {
|
||||||
|
fQueue.add(new ShutdownRequest(rm));
|
||||||
|
} else {
|
||||||
|
//
|
||||||
|
rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
|
||||||
|
rm.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getCount(DataRequestMonitor<Integer> rm) {
|
||||||
|
if (!fShutdown.get()) {
|
||||||
|
fQueue.add(new CountRequest(rm));
|
||||||
|
} else {
|
||||||
|
rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
|
||||||
|
rm.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getValue(int index, DataRequestMonitor<String> rm) {
|
||||||
|
if (!fShutdown.get()) {
|
||||||
|
fQueue.add(new ItemRequest(index, rm));
|
||||||
|
} else {
|
||||||
|
rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down"));
|
||||||
|
rm.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(Listener listener) {
|
||||||
|
fListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeListener(Listener listener) {
|
||||||
|
fListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
while(true) {
|
||||||
|
// Get the next request from the queue. The time-out
|
||||||
|
// ensures that that the random changes get processed.
|
||||||
|
final Request request = fQueue.poll(100, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
// If a request was dequeued, process it.
|
||||||
|
if (request != null) {
|
||||||
|
// Simulate a processing delay.
|
||||||
|
Thread.sleep(PROCESSING_DELAY);
|
||||||
|
|
||||||
|
if (request instanceof CountRequest) {
|
||||||
|
processCountRequest((CountRequest)request);
|
||||||
|
} else if (request instanceof ItemRequest) {
|
||||||
|
processItemRequest((ItemRequest)request);
|
||||||
|
} else if (request instanceof ShutdownRequest) {
|
||||||
|
// If shutting down, just break out of the while(true)
|
||||||
|
// loop and thread will exit.
|
||||||
|
request.fRequestMonitor.done();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate data changes.
|
||||||
|
randomChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InterruptedException x) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processCountRequest(CountRequest request) {
|
||||||
|
@SuppressWarnings("unchecked") // Suppress warning about lost type info.
|
||||||
|
DataRequestMonitor<Integer> rm = (DataRequestMonitor<Integer>)request.fRequestMonitor;
|
||||||
|
|
||||||
|
rm.setData(fCount);
|
||||||
|
rm.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processItemRequest(ItemRequest request) {
|
||||||
|
@SuppressWarnings("unchecked") // Suppress warning about lost type info.
|
||||||
|
DataRequestMonitor<String> rm = (DataRequestMonitor<String>)request.fRequestMonitor;
|
||||||
|
|
||||||
|
if (fChangedIndexes.contains(request.fIndex)) {
|
||||||
|
rm.setData("Changed: " + request.fIndex);
|
||||||
|
} else {
|
||||||
|
rm.setData(Integer.toString(request.fIndex));
|
||||||
|
}
|
||||||
|
rm.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void randomChanges() {
|
||||||
|
// Check if enough time is elapsed.
|
||||||
|
if (System.currentTimeMillis() > fLastChangeTime + RANDOM_CHANGE_INTERVAL) {
|
||||||
|
fLastChangeTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// Once every number of changes, reset the count, the rest of the
|
||||||
|
// times just change certain values.
|
||||||
|
if (++fCountResetTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0){
|
||||||
|
randomCountReset();
|
||||||
|
} else {
|
||||||
|
randomDataChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void randomCountReset() {
|
||||||
|
// Calculate the new count.
|
||||||
|
Random random = new java.util.Random();
|
||||||
|
fCount = MIN_COUNT + Math.abs(random.nextInt()) % (MAX_COUNT - MIN_COUNT);
|
||||||
|
|
||||||
|
// Reset the changed values.
|
||||||
|
fChangedIndexes.clear();
|
||||||
|
|
||||||
|
// Notify listeners
|
||||||
|
for (Object listener : fListeners.getListeners()) {
|
||||||
|
((Listener)listener).countChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void randomDataChange() {
|
||||||
|
// Calculate the indexes to change.
|
||||||
|
Random random = new java.util.Random();
|
||||||
|
Set<Integer> set = new HashSet<Integer>();
|
||||||
|
for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) {
|
||||||
|
set.add( new Integer(Math.abs(random.nextInt()) % fCount) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the indexes to an overall set of changed indexes.
|
||||||
|
fChangedIndexes.addAll(set);
|
||||||
|
|
||||||
|
// Notify listeners
|
||||||
|
for (Object listener : fListeners.getListeners()) {
|
||||||
|
((Listener)listener).valuesChanged(set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2006 Wind River Systems and others.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Wind River Systems - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.dd.examples.dsf.dataviewer;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data generator is simple source of data used to populate the example table
|
||||||
|
* view. It contains two asynchronous methods for retrieving the data
|
||||||
|
* parameters: the count and the value for a given index. It also allows the
|
||||||
|
* view to receive events indicating when the data supplied by the generator
|
||||||
|
* is changed.
|
||||||
|
*/
|
||||||
|
@ThreadSafe
|
||||||
|
public interface IDataGenerator {
|
||||||
|
|
||||||
|
// Constants which control the data generator behavior.
|
||||||
|
// Changing the count range can stress the scalability of the system, while
|
||||||
|
// changing of the process delay and random change interval can stress
|
||||||
|
// its performance.
|
||||||
|
final static int MIN_COUNT = 50;
|
||||||
|
final static int MAX_COUNT = 100;
|
||||||
|
final static int PROCESSING_DELAY = 10;
|
||||||
|
final static int RANDOM_CHANGE_INTERVAL = 10000;
|
||||||
|
final static int RANDOM_COUNT_CHANGE_INTERVALS = 3;
|
||||||
|
final static int RANDOM_CHANGE_SET_PERCENTAGE = 10;
|
||||||
|
|
||||||
|
|
||||||
|
// Listener interface that the view needs to implement to react
|
||||||
|
// to the changes in data.
|
||||||
|
public interface Listener {
|
||||||
|
void countChanged();
|
||||||
|
void valuesChanged(Set<Integer> indexes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data access methods.
|
||||||
|
void getCount(DataRequestMonitor<Integer> rm);
|
||||||
|
void getValue(int index, DataRequestMonitor<String> rm);
|
||||||
|
|
||||||
|
// Method used to shutdown the data generator including any threads that
|
||||||
|
// it may use.
|
||||||
|
void shutdown(RequestMonitor rm);
|
||||||
|
|
||||||
|
// Methods for registering change listeners.
|
||||||
|
void addListener(Listener listener);
|
||||||
|
void removeListener(Listener listener);
|
||||||
|
}
|
|
@ -0,0 +1,180 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2008 Wind River Systems and others.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Wind River Systems - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.dd.examples.dsf.dataviewer;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.ImmediateExecutor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.Query;
|
||||||
|
import org.eclipse.jface.viewers.IStructuredContentProvider;
|
||||||
|
import org.eclipse.jface.viewers.TableViewer;
|
||||||
|
import org.eclipse.jface.viewers.Viewer;
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.graphics.Font;
|
||||||
|
import org.eclipse.swt.layout.GridData;
|
||||||
|
import org.eclipse.swt.layout.GridLayout;
|
||||||
|
import org.eclipse.swt.widgets.Display;
|
||||||
|
import org.eclipse.swt.widgets.Shell;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data viewer based on a table, which reads data using synchronous methods.
|
||||||
|
* <p>
|
||||||
|
* This viewer implements the {@link IStructuredContentProvider} interface
|
||||||
|
* which is used by the JFace TableViewer class to populate a Table. This
|
||||||
|
* interface contains one principal methods for reading data {@link #getElements(Object)},
|
||||||
|
* which synchronously returns an array of elements. In order to implement this
|
||||||
|
* method using the asynchronous data generator, this provider uses the
|
||||||
|
* {@link Query} object.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class SyncDataViewer
|
||||||
|
implements IStructuredContentProvider, IDataGenerator.Listener
|
||||||
|
{
|
||||||
|
// The viewer and generator that this content provider using.
|
||||||
|
final private TableViewer fViewer;
|
||||||
|
final private IDataGenerator fDataGenerator;
|
||||||
|
|
||||||
|
public SyncDataViewer(TableViewer viewer, IDataGenerator generator) {
|
||||||
|
fViewer = viewer;
|
||||||
|
fDataGenerator = generator;
|
||||||
|
fDataGenerator.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
|
||||||
|
// Not used
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Object[] getElements(Object inputElement) {
|
||||||
|
|
||||||
|
// Create the query object for reading data count.
|
||||||
|
Query<Integer> countQuery = new Query<Integer>() {
|
||||||
|
@Override
|
||||||
|
protected void execute(DataRequestMonitor<Integer> rm) {
|
||||||
|
fDataGenerator.getCount(rm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Submit the query to be executed. A query implements a runnable
|
||||||
|
// interface and it has to be executed in order to do its work.
|
||||||
|
ImmediateExecutor.getInstance().execute(countQuery);
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
// Block until the query completes, which will happen when the request
|
||||||
|
// monitor of the execute() method is marked done.
|
||||||
|
try {
|
||||||
|
count = countQuery.get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// InterruptedException and ExecutionException can be thrown here.
|
||||||
|
// ExecutionException containing a CoreException will be thrown
|
||||||
|
// if an error status is set to the Query's request monitor.
|
||||||
|
return new Object[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the array that will be filled with elements.
|
||||||
|
// For each index in the array execute a query to get the element at
|
||||||
|
// that index.
|
||||||
|
final Object[] elements = new Object[count];
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
final int index = i;
|
||||||
|
Query<String> valueQuery = new Query<String>() {
|
||||||
|
@Override
|
||||||
|
protected void execute(DataRequestMonitor<String> rm) {
|
||||||
|
fDataGenerator.getValue(index, rm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ImmediateExecutor.getInstance().execute(valueQuery);
|
||||||
|
try {
|
||||||
|
elements[i] = valueQuery.get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
elements[i] = "error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
fDataGenerator.removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void countChanged() {
|
||||||
|
// For any event from the generator, refresh the whole viewer.
|
||||||
|
refreshViewer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void valuesChanged(Set<Integer> indexes) {
|
||||||
|
// For any event from the generator, refresh the whole viewer.
|
||||||
|
refreshViewer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshViewer() {
|
||||||
|
// This method may be called on any thread, switch to the display
|
||||||
|
// thread before calling the viewer.
|
||||||
|
Display display = fViewer.getControl().getDisplay();
|
||||||
|
display.asyncExec( new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
if (!fViewer.getControl().isDisposed()) {
|
||||||
|
fViewer.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Create the shell to hold the viewer.
|
||||||
|
Display display = new Display();
|
||||||
|
Shell shell = new Shell(display, SWT.SHELL_TRIM);
|
||||||
|
shell.setLayout(new GridLayout());
|
||||||
|
GridData data = new GridData(GridData.FILL_BOTH);
|
||||||
|
shell.setLayoutData(data);
|
||||||
|
Font font = new Font(display, "Courier", 10, SWT.NORMAL);
|
||||||
|
|
||||||
|
// Create the table viewer.
|
||||||
|
TableViewer tableViewer = new TableViewer(shell, SWT.BORDER);
|
||||||
|
tableViewer.getControl().setLayoutData(data);
|
||||||
|
|
||||||
|
// Create the data generator.
|
||||||
|
final IDataGenerator generator = new DataGeneratorWithThread();
|
||||||
|
|
||||||
|
// Create the content provider which will populate the viewer.
|
||||||
|
SyncDataViewer contentProvider = new SyncDataViewer(tableViewer, generator);
|
||||||
|
tableViewer.setContentProvider(contentProvider);
|
||||||
|
tableViewer.setInput(new Object());
|
||||||
|
|
||||||
|
// Open the shell and service the display dispatch loop until user
|
||||||
|
// closes the shell.
|
||||||
|
shell.open();
|
||||||
|
while (!shell.isDisposed()) {
|
||||||
|
if (!display.readAndDispatch())
|
||||||
|
display.sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The IDataGenerator.shutdown() method is asynchronous, this requires
|
||||||
|
// using a query again in order to wait for its completion.
|
||||||
|
Query<Object> shutdownQuery = new Query<Object>() {
|
||||||
|
@Override
|
||||||
|
protected void execute(DataRequestMonitor<Object> rm) {
|
||||||
|
generator.shutdown(rm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ImmediateExecutor.getInstance().execute(shutdownQuery);
|
||||||
|
try {
|
||||||
|
shutdownQuery.get();
|
||||||
|
} catch (Exception e) {}
|
||||||
|
|
||||||
|
// Shut down the display.
|
||||||
|
font.dispose();
|
||||||
|
display.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
Loading…
Add table
Reference in a new issue