From 90f4dc4c71c3d819bff3d785635d47bed662ea52 Mon Sep 17 00:00:00 2001 From: Pawel Piech Date: Sat, 1 Mar 2008 00:51:12 +0000 Subject: [PATCH] [220446] Updated the "DSF Common Patterns" document. --- .../.externalToolBuilders/PreProcessor.launch | 7 +- .../dsf/dataviewer/AsyncDataViewer.java | 6 + .../dataviewer/DataGeneratorWithExecutor.java | 56 ++-- .../dsf/dataviewer/IDataGenerator.java | 6 +- .../dsf/dataviewer/SyncDataViewer.java | 11 + .../doc-files/dsf_concurrency_model-1.png | Bin 6256 -> 0 bytes .../dd/examples/dsf/dataviewer/package.html | 289 ------------------ .../dsf/requestmonitor/AsyncHelloWorld.java | 7 +- .../dsf/requestmonitor/AsyncQuicksort.java | 45 +-- 9 files changed, 88 insertions(+), 339 deletions(-) delete mode 100644 plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/doc-files/dsf_concurrency_model-1.png delete mode 100644 plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/package.html diff --git a/plugins/org.eclipse.dd.examples.dsf/.externalToolBuilders/PreProcessor.launch b/plugins/org.eclipse.dd.examples.dsf/.externalToolBuilders/PreProcessor.launch index 948b857d441..ed027e9bf2d 100644 --- a/plugins/org.eclipse.dd.examples.dsf/.externalToolBuilders/PreProcessor.launch +++ b/plugins/org.eclipse.dd.examples.dsf/.externalToolBuilders/PreProcessor.launch @@ -3,16 +3,17 @@ - + - + + - + diff --git a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/AsyncDataViewer.java b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/AsyncDataViewer.java index 72feab635da..35f928b0b64 100644 --- a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/AsyncDataViewer.java +++ b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/AsyncDataViewer.java @@ -21,6 +21,8 @@ import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; +import org.eclipse.dd.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.dd.dsf.concurrent.ThreadSafe; import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; import org.eclipse.dd.dsf.concurrent.DsfExecutor; import org.eclipse.dd.dsf.concurrent.ImmediateExecutor; @@ -51,10 +53,12 @@ import org.eclipse.swt.widgets.Table; * to check the canceled state of the requests and ignore them. *

*/ +@ConfinedToDsfExecutor("fDisplayExecutor") public class AsyncDataViewer implements ILazyContentProvider, IDataGenerator.Listener { // Executor to use instead of Display.asyncExec(). + @ThreadSafe final private DsfExecutor fDisplayExecutor; // The viewer and generator that this content provider using. @@ -106,10 +110,12 @@ public class AsyncDataViewer return Math.min((table.getBounds().height / table.getItemHeight()) + 2, itemCount - top); } + @ThreadSafe public void countChanged() { queryItemCount(); } + @ThreadSafe public void valuesChanged(final Set indexes) { // Mark the changed items in table viewer as dirty, this will // trigger update requests for these indexes if they are diff --git a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/DataGeneratorWithExecutor.java b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/DataGeneratorWithExecutor.java index fb70c5e15a4..d667b2810d4 100644 --- a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/DataGeneratorWithExecutor.java +++ b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/DataGeneratorWithExecutor.java @@ -50,7 +50,7 @@ import org.eclipse.dd.examples.dsf.DsfExamplesPlugin; *

*/ //#ifdef excercises -// TODO Excercise 3 - Add an annotationindicating allowed concurrency access +// TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //#@ThreadSafe //#endif @@ -59,7 +59,8 @@ 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 + // TODO Excercise 4 - Add an annotationindicating allowed concurrency access + // Hint: Request and its subclasses have all their fields declared as final. //#else //# @Immutable //#endif @@ -72,7 +73,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { } //#ifdef excercises - // TODO Excercise 3 - Add an annotationindicating allowed concurrency access + // TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //# @Immutable //#endif @@ -83,7 +84,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { } //#ifdef excercises - // TODO Excercise 3 - Add an annotationindicating allowed concurrency access + // TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //# @Immutable //#endif @@ -97,7 +98,10 @@ public class DataGeneratorWithExecutor implements IDataGenerator { // The executor used to access all internal data of the generator. //#ifdef excercises - // TODO Excercise 3 - Add an annotation indicating allowed concurrency access + // TODO Excercise 4 - Add an annotation indicating allowed concurrency access + // Hint: If a member does not have an annotation, the programmer can assume + // that the concurrency rule that applies to the class also applies to this + // member. //#endif private DsfExecutor fExecutor; @@ -106,7 +110,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { // 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 + // TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif @@ -115,7 +119,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { // 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 + // TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif @@ -123,7 +127,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { // Current number of elements in this generator. //#ifdef excercises - // TODO Excercise 3 - Add an annotationindicating allowed concurrency access + // TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif @@ -131,7 +135,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { // Counter used to determine when to reset the element count. //#ifdef excercises - // TODO Excercise 3 - Add an annotationindicating allowed concurrency access + // TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif @@ -139,7 +143,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { // Elements which were modified since the last reset. //#ifdef excercises - // TODO Excercise 3 - Add an annotationindicating allowed concurrency access + // TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif @@ -147,14 +151,14 @@ public class DataGeneratorWithExecutor implements IDataGenerator { // Flag used to ensure that requests are processed sequentially. //#ifdef excercises - // TODO Excercise 3 - Add an annotationindicating allowed concurrency access + // TODO Excercise 4 - 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 + // TODO Excercise 4 - Add an annotation indicating allowed concurrency access //#endif public DataGeneratorWithExecutor() { // Create the executor @@ -173,7 +177,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { } //#ifdef excercises - // TODO Excercise 3 - Add an annotation indicating allowed concurrency access + // TODO Excercise 4 - Add an annotation indicating allowed concurrency access //#endif public void shutdown(final RequestMonitor rm) { try { @@ -199,7 +203,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { } //#ifdef excercises - // TODO Excercise 3 - Add an annotation indicating allowed concurrency access + // TODO Excercise 4 - Add an annotation indicating allowed concurrency access //#endif public void getCount(final DataRequestMonitor rm) { try { @@ -216,7 +220,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { } //#ifdef excercises - // TODO Excercise 3 - Add an annotation indicating allowed concurrency access + // TODO Excercise 4 - Add an annotation indicating allowed concurrency access //#endif public void getValue(final int index, final DataRequestMonitor rm) { try { @@ -233,7 +237,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { } //#ifdef excercises - // TODO Excercise 3 - Add an annotation indicating allowed concurrency access + // TODO Excercise 4 - Add an annotation indicating allowed concurrency access //#endif public void addListener(final Listener listener) { try { @@ -246,7 +250,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { } //#ifdef excercises - // TODO Excercise 3 - Add an annotation indicating allowed concurrency access + // TODO Excercise 4 - Add an annotation indicating allowed concurrency access //#endif public void removeListener(final Listener listener) { try { @@ -260,14 +264,18 @@ public class DataGeneratorWithExecutor implements IDataGenerator { // Main processing function of this generator. //#ifdef excercises - // TODO Excercise 3 - Add an annotationindicating allowed concurrency access + // TODO Excercise 4 - 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. + // TODO Excercise 3 - Add logic to discard requests from queue. + // Hint: Since serviceQueue() is called using the executor, and the + // fQueue list can only be modified when running in the executor + // thread. This method can safely iterate and modify fQueue without + // risk of race conditions or concurrent modification exceptions. //#else //# for (Iterator requestItr = fQueue.iterator(); requestItr.hasNext();) { //# Request request = requestItr.next(); @@ -311,7 +319,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { } //#ifdef excercises - // TODO Excercise 3 - Add an annotationindicating allowed concurrency access + // TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif @@ -324,7 +332,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { } //#ifdef excercises - // TODO Excercise 3 - Add an annotationindicating allowed concurrency access + // TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif @@ -344,7 +352,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { * This method simulates changes in the supplier's data set. */ //#ifdef excercises - // TODO Excercise 3 - Add an annotationindicating allowed concurrency access + // TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif @@ -362,7 +370,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { * Calculates new size for provider's data set. */ //#ifdef excercises - // TODO Excercise 3 - Add an annotationindicating allowed concurrency access + // TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif @@ -384,7 +392,7 @@ public class DataGeneratorWithExecutor implements IDataGenerator { * Invalidates a random range of indexes. */ //#ifdef excercises - // TODO Excercise 3 - Add an annotationindicating allowed concurrency access + // TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif diff --git a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/IDataGenerator.java b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/IDataGenerator.java index e31c0edb981..e3eb61bc6b1 100644 --- a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/IDataGenerator.java +++ b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/IDataGenerator.java @@ -18,7 +18,9 @@ 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; +//#ifdef answers +//#import org.eclipse.dd.dsf.concurrent.ThreadSafe; +//#endif /** * Data generator is simple source of data used to populate the example table @@ -28,7 +30,7 @@ import org.eclipse.dd.dsf.concurrent.ThreadSafe; * is changed. */ //#ifdef excercises -//TODO Excercise 3 - Add an annotationindicating allowed concurrency access +//TODO Excercise 4 - Add an annotationindicating allowed concurrency access //#else //#@ThreadSafe //#endif diff --git a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/SyncDataViewer.java b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/SyncDataViewer.java index f0c4c20729e..91e52cee3f9 100644 --- a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/SyncDataViewer.java +++ b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/SyncDataViewer.java @@ -122,6 +122,12 @@ public class SyncDataViewer } private void refreshViewer() { + //#ifdef excercises + // TODO Excercise 5 - Add a call to getElements() to force a deadlock. + //#else +//# getElements(null); + //#endif + // This method may be called on any thread, switch to the display // thread before calling the viewer. Display display = fViewer.getControl().getDisplay(); @@ -148,7 +154,12 @@ public class SyncDataViewer tableViewer.getControl().setLayoutData(data); // Create the data generator. + //#ifdef excercises + // TODO Excercise 5 - Use the DataGeneratorWithExecutor() instead. final IDataGenerator generator = new DataGeneratorWithThread(); + //#else +//# final IDataGenerator generator = new DataGeneratorWithExecutor(); + //#endif // Create the content provider which will populate the viewer. SyncDataViewer contentProvider = new SyncDataViewer(tableViewer, generator); diff --git a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/doc-files/dsf_concurrency_model-1.png b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/doc-files/dsf_concurrency_model-1.png deleted file mode 100644 index 1bb373447d7bd0f17cd54f8298fc6f65658c8420..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6256 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 W(|ar%1~vi^4?S(e+clc@PyPove1psY diff --git a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/package.html b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/package.html deleted file mode 100644 index f0efe705346..00000000000 --- a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/dataviewer/package.html +++ /dev/null @@ -1,289 +0,0 @@ - - - - - DSF Slow Data Provider Example - - -

Version -1.0
-Pawel Piech
-© 2006, Wind River Systems.  Release -under EPL version 1.0.

-

Slow Data Provider Example

-The point of DSF concurrency can be most easily explained through -a practical example.  Suppose there is a viewer which needs to -show data that originates from a remote "provider".  There is a -considerable delay in transmitting the data to and from the provider, -and some delay in processing the data.  The viewer is a -lazy-loading table, which means that it request information only about -items that are visible on the screen, and as the table is scrolled, new -requests for data are generated.  The diagram below illustrates -the -logical relationship between components:
-
-.
-

In detail, these components look like this:

-

-Table Viewer
-

The table viewer is the standard -org.eclipse.jface.viewers.TableViewer, -created with SWT.VIRTUAL -flag.  It has an associated content -provider, SlowDataProviderContentProvider) which handles all the -interactions with the data provider.  The lazy content provider -operates in a very simple cycle:

-
    -
  1. Table viewer tells content provider that the input has changed by -calling IContentProvider.inputChanged().  -This means that the content provider has to query initial state of the -data.
  2. -
  3. Next the content provider tells the viewer how many elements -there are, by calling TableViewer.setItemCount().
  4. -
  5. At this point, the table resizes, and it requests data values for -items that are visible.  So for each visible item it calls: ILazyContentProvider.updateElement().
  6. -
  7. After calculating the value, the content provider tells the table -what the value is, by calling TableViewer.replace().
  8. -
  9. If the data ever changes, the content provider tells the table to -rerequest the data, by calling TableViewer.clear().
  10. -
-Table viewer operates in the -SWT display thread, which means that the content provider must switch -from the display thread to the DSF dispatch thread, whenever it is -called by the table viewer, as in the example below:
-
    public void updateElement(final int index) {
assert fTableViewer != null;
if (fDataProvider == null) return;

fDataProvider.getExecutor().execute(
new Runnable() { public void run() {
// Must check again, in case disposed while redispatching.
if (fDataProvider == null) return;

queryItemData(index);
}});
}
-Likewise, when the content provider calls the table viewer, it also has -to switch back into the display thread as in following example, when -the content provider receives an event from the data provider, that an -item value has changed.
-
    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);
}
}});
}
}
-All of this switching back and forth between threads makes the code -look a lot more complicated than it really is, and it takes some -getting used to, but this is the price to be paid for multi-threading. -Whether the participants use semaphores or the dispatch thread, the -logic is equally complicated, and we believe that using a single -dispatch thread, makes the synchronization very explicit and thus less -error-prone.
-

Data Provider Service

-

The data provider service interface, DataProvider, is very similar -to that of the lazy content provider.  It has methods to:

-
    -
  • get item count
  • -
  • get a value for given item
  • -
  • register as listener for changes in data count and data values
  • -
-But this is a DSF interface, and all methods must be called on the -service's dispatch thread.  For this reason, the DataProvider interface returns -an instance of DsfExecutor, -which must be used with the interface.
-

Slow Data Provider

-

The data provider is actually implemented as a thread which is an -inner class of SlowDataProvider -service.  The provider thread -communicates with the service by reading Request objects from a shared -queue, and by posting Runnable objects directly to the DsfExecutor but -with a simulated transmission delay.  Separately, an additional -flag is also used to control the shutdown of the provider thread.

-To simulate a real back end, the data provider randomly invalidates a -set of items and notifies the listeners to update themselves.  It -also periodically invalidates the whole table and forces the clients to -requery all items.
-

Data and Control Flow
-

-This can be described in following steps:
-
    -
  1. The table viewer requests data for an item at a given index (SlowDataProviderContentProvider.updateElement).
    -
  2. -
  3. The table viewer's content provider executes a Runnable in the DSF -dispatch thread and calls the data provider interface (SlowDataProviderContentProvider.queryItemData).
  4. -
  5. Data provider service creates a Request object, and files it in a -queue (SlowDataProvider.getItem).
  6. -
  7. Data provider thread de-queues the Request object and acts on it, -calculating the value (ProviderThread.processItemRequest).
  8. -
  9. Data provider thread schedules the calculation result to be -posted with DSF executor (SlowDataProvider.java:185).
  10. -
  11. The RequestMonitor callback sets the result data in the table -viewer (SlowDataProviderContentProvider.java:167).
    -
  12. -
-

Running the example and full sources

-This example is implemented in the org.eclipse.dd.examples.dsf -plugin, in the org.eclipse.dd.examples.dsf.concurrent -package. 
-
-To run the example:
-
    -
  1. Build the test plugin (along with the org.eclipse.dsdp.DSF plugin) -and launch the PDE. 
    -
  2. -
  3. Make sure to add the DSF -Tests action set to your current perspective.
  4. -
  5. From the main menu, select DSF -Tests -> Slow Data Provider.
  6. -
  7. A dialog will open and after a delay it will populate with data.
  8. -
  9. Scroll and resize dialog and observe the update behavior.
  10. -
-

Initial Notes
-

-This example is supposed to be representative of a typical embedded -debugger design problem.  Embedded debuggers are often slow in -retrieving and processing data, and can sometimes be accessed through a -relatively slow data channel, such as serial port or JTAG -connection.  But as such, this basic example presents a couple -of major usability problems
-
    -
  1. The data provider service interface mirrors the table's content -provider interface, in that it has a method to retrieve a single piece -of data at a time.  The result of this is visible to the user as -lines of data are filled in one-by-one in the table.  However, -most debugger back ends are in fact capable of retrieving data in -batches and are much more efficient at it than retrieving data items -one-by-one.
  2. -
  3. When scrolling quickly through the table, the requests are -generated by the table viewer for items which are quickly scrolled out -of view, but the service still queues them up and calculates them in -the order they were received.  As a result, it takes a very long -time for the table to be populated with data at the location where the -user is looking. 
    -
  4. -
-These two problems are very common in creating UI for embedded -debugging, and there are common patterns which can be used to solve -these problems in DSF services.
-

Coalescing

-Coalescing many single-item requests into fewer multi-item requests is -the surest way to improve performance in communication with a remote -debugger, although it's not necessarily the simplest.  There are -two basic patterns in which coalescing is achieved:
-
    -
  1. The back end provides an interface for retrieving data in large -chunks.  So when the service implementation receives a request for -a single item, it retrieves a whole chunk of data, returns the single -item, and stores the rest of the data in a local cache.
  2. -
  3. The back end providers an interface for retrieving data in -variable size chunks.  When the service implementation receives a -request for a single item, it buffers the request, and waits for other -requests to come in.  After a delay, the service clears the buffer -and submits a request for the combined items to the data provider.
  4. -
-In practice, a combination of the two patterns is needed, but for -purpose of an example, we implemented the second pattern in the -"Input-Coalescing Slow Data Provider" (InputCoalescingSlowDataProvider.java).  -
-

Input Buffer

-

The main feature of this pattern is a buffer for holding the -requests before sending them to the data provider.  In this -example the user requests are buffered in two arrays: fGetItemIndexesBuffer and fGetItemRequestMonitorsBuffer.  The -DataProvider.getItem() -implementation is changed as follows:

-
    public void getItem(final int index, final DataRequestMonitor<String> rm) {
// Schedule a buffer-servicing call, if one is needed.
if (fGetItemRequestMonitorsBuffer.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);
}

-And method that services the buffer looks like this:
-
    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.fDones[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();
}});
}
}
-The most interesting feature of this implementation is the fact that -there are no semaphores anywhere to control access to the input -buffers.  Even though the buffers are serviced with a delay and -multiple clients can call the getItem() -method, the use of a single -dispatch thread prevents any race conditions that could corrupt the -buffer data.  In real-world implementations, the buffers and -caches that need to be used are far more sophisticated with much more -complicated logic, and this is where managing access to them using the -dispatch thread is ever more important.
-

Cancellability

-

Table Viewer

-

-Unlike coalescing, which can be implemented entirely within the -service, cancellability requires that the client be modified as well -to take advantage of this capability.  For the table viewer -content provider, this means that additional features have to be -added.  In CancellingSlowDataProviderContentProvider.java -ILazyContentProvider.updateElement() -was changes as follows:
-
    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.getExecutor().execute(
new Runnable() { public void run() {
// Must check again, in case disposed while redispatching.
if (fDataProvider == null || fTableViewer.getTable().isDisposed()) return;
if (index >= topIdx && index <= botIdx) {
queryItemData(index);
}
cancelStaleRequests(topIdx, botIdx);
}});
}
-Now the client keeps track of the requests it made to the service in fItemDataDones, and above, cancelStaleRequests() iterates -through all the outstanding requests and cancels the ones that are no -longer in the visible range.
-

Data Provider Service

-

-

The data provider implementation -(CancellableInputCoalescingSlowDataProvider.java), -builds on top of the -coalescing data provider.  To make the canceling feature useful, -the data provider service has to limit the size of the request -queue.  This is because in this example which simulates -communication with a target and once requests are filed into the -request -queue, they cannot be canceled, just like a client can't cancel -request once it sends them over a socket.  So instead, if a flood -of getItem() -calls comes in, the service has to hold most of them in the coalescing -buffer in case the client decides to cancel them.  Therefore the -fileBufferedRequests() -method includes a simple check before servicing -the buffer, and if the request queue is full, the buffer servicing call -is delayed.

-
        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;
}
-Beyond this change, the only other significant change is that before -the requests are queued, they are checked for cancellation.
-

Final Notes
-

-The example given here is fairly simplistic, and chances are that the -same example could be implemented using semaphores and free threading -with perhaps fewer lines of code.  But what we have found is that -as the problem gets bigger, the amount of -features in the data provider increases, the state of the -communication protocol gets more complicated, and the number of modules -needed in the service layer increases, using free threading and -semaphores does not safely scale.  Using a dispatch thread for -synchronization certainly doesn't make the inherent problems of the -system less complicated, but it does help eliminate the race conditions -and deadlocks from the overall system.
-

Coalescing and Cancellability are both optimizations.  Neither -of these optimizations affected the original interface of the service, -and one of them only needed a service-side modification.  But as -with all optimizations, it is often better to first make sure that the -whole system is working correctly and then add optimizations where they -can make the biggest difference in user experience. 

-

The above examples of optimizations can take many forms, and as -mentioned with coalescing, caching data that is retrieved from the data -provider is the most common form of data coalescing.  For -cancellation, many services in DSF build on top of other services, -which means that even a low-level service can cause a higher -level service to retrieve data, while another event might cause it to -cancel those requests.  The perfect example of this is a Variables -service, which is responsible for calculating the value of expressions -shown in the Variables view.  The Variables service reacts to the -Run Control service, which issues a suspended event and then requests a -set of variables to be evaluated by the debugger back end.  But as -soon as a resumed event is issued by Run Control, the Variables service -needs to cancel  the pending evaluation requests.
-

-
-
- - diff --git a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/requestmonitor/AsyncHelloWorld.java b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/requestmonitor/AsyncHelloWorld.java index b2272f97533..d7f08ea312e 100644 --- a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/requestmonitor/AsyncHelloWorld.java +++ b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/requestmonitor/AsyncHelloWorld.java @@ -38,7 +38,12 @@ public class AsyncHelloWorld { static void asyncHelloWorld(RequestMonitor rm) { System.out.println("Hello world"); //#ifdef excercises - // TODO Exercise 1: - Call the second async. "Hello world 2" method. + // TODO Exercise 1: - Call the second async. "Hello world 2" method. + // Hint: Calling an asynchronous method requires passing to it a + // request monitor. A new request monitor can be constructed with + // a parent RequestMonitor as an argument argument. The parent gets + // completed automatically when the lower level request monitor is + // completed. rm.done(); //#else //# RequestMonitor rm2 = new RequestMonitor(ImmediateExecutor.getInstance(), rm); diff --git a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/requestmonitor/AsyncQuicksort.java b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/requestmonitor/AsyncQuicksort.java index 21f861e6a87..f1ff70d406c 100644 --- a/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/requestmonitor/AsyncQuicksort.java +++ b/plugins/org.eclipse.dd.examples.dsf/src_preprocess/org/eclipse/dd/examples/dsf/requestmonitor/AsyncQuicksort.java @@ -34,7 +34,7 @@ public class AsyncQuicksort { public static void main(String[] args) { final int[] array = {5, 7, 8, 3, 2, 1, 9, 5, 4}; - + System.out.println("To sort: " + Arrays.toString(array)); asyncQuicksort( array, 0, array.length - 1, @@ -51,16 +51,19 @@ public class AsyncQuicksort { { if (right > left) { int pivot = left; - //#ifdef excercises - // TODO: Request Monitors Exercise 2 - Convert partition to an async. method. - int newPivot = partition(array, left, right, pivot); - printArray(array, left, right, newPivot); - - CountingRequestMonitor countingRm = new CountingRequestMonitor(fgExecutor, rm); - asyncQuicksort(array, left, newPivot - 1, countingRm); - asyncQuicksort(array, newPivot + 1, right, countingRm); - countingRm.setDoneCount(2); - //#else + //#ifdef excercises + // TODO: Exercise 2 - Convert the call to partition into an + // asynchronous call to asyncPartition(). + // Hint: The rest of the code below should be executed inside + // the DataRequestMonitor.handleCompleted() overriding method. + int newPivot = partition(array, left, right, pivot); + printArray(array, left, right, newPivot); + + CountingRequestMonitor countingRm = new CountingRequestMonitor(fgExecutor, rm); + asyncQuicksort(array, left, newPivot - 1, countingRm); + asyncQuicksort(array, newPivot + 1, right, countingRm); + countingRm.setDoneCount(2); + //#else //# asyncPartition( //# array, left, right, pivot, //# new DataRequestMonitor(fgExecutor, rm) { @@ -75,15 +78,16 @@ public class AsyncQuicksort { //# countingRm.setDoneCount(2); //# } //# }); - //#endif - + //#endif } else { rm.done(); } } - + //#ifdef excercises // TODO Exercise 2 - Convert partition to an asynchronous method. + // Hint: a DataRequestMonitor should be used to carry the + // return value to the caller. static int partition(int[] array, int left, int right, int pivot) //#else //# static void asyncPartition(int[] array, int left, int right, int pivot, DataRequestMonitor rm) @@ -103,16 +107,17 @@ public class AsyncQuicksort { } array[right] = array[store]; array[store] = pivotValue; - + //#ifdef excercises - // TODO: Request Monitors Exercise 2 - Convert partition to an async. method. + // TODO: Request Monitors Exercise 2 - Return the data to caller using + // a request monitor. return store; //#else -//# rm.setData(store); -//# rm.done(); + //# rm.setData(store); + //# rm.done(); //#endif } - + static void printArray(int[] array, int left, int right, int pivot) { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < array.length; i++ ) { @@ -133,7 +138,7 @@ public class AsyncQuicksort { buffer.append(' '); } } - + System.out.println(buffer); } }