diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/dsf_common_patterns.html b/plugins/org.eclipse.dd.doc.dsf/docs/common/dsf_common_patterns.html new file mode 100644 index 00000000000..79f481f79fb --- /dev/null +++ b/plugins/org.eclipse.dd.doc.dsf/docs/common/dsf_common_patterns.html @@ -0,0 +1,837 @@ + + + + DSF Common Patterns + + +

DSF Common Patterns
+

+

Summary

+

Examples
+

+Running example code is and performing included excercises is very +helpful in following this tutorial.  In order to run the examples +in this tutorial the following is needed:
+
    +
  1. Download and install Eclipse development environment, either the Eclipse Classic 3.4 or Eclipse IDE for C/C++ Developers
    +
  2. +
  3. Install the DSF SDK feature to build against, by performing +either:
    +
  4. +
      +
    1. Using update manager, install the Debugger Services Framework end-user and +extender SDK, found in the Ganymede +Discovery Site under Remote +Access and Device Development.
    2. +
    3. Check out org.eclipse.dd.dsf +and org.eclipse.dd.dsf.ui plugins, found in the /cvsroot/dsdp repository under org.eclipse.dd.dsf/plugins +directory.
    4. +
    +
  5. Check out the org.eclipse.dd.examples.dsf +plugin, found /cvsroot/dsdp +under org.eclipse.dd.dsf/plugins +directory.
  6. +
  7. Build the examples plugin:
  8. +
      +
    1. Execute the build the first time to build and run the +excercises preprocessor.
    2. +
    3. Refresh the resources in the plugin (right-click on project in Navigator and select Refresh), in order to recognize the +sources generated by the preprocessor.
    4. +
    5. Build the plugin again to compile the generated sources.
    6. +
    +
  9. Launch the examples
  10. +
      +
    1. Examples in data org.eclipse.dd.examples.dsf.requestmonitor and +org.eclipse.dd.examples.dsf.dataviewer packages each contain a public +main() function.  They can be launched using the Java Application +launch type.
    2. +
    3. TODO: Launching timers +example
    4. +
    +
+

Asynchronous Methods

+One of the central features of DSF is that it relies very heavily on +the use of asynchronous methods.  Asynchronous methods here mean +simply methods that use a callback +object to indicate their completion. The use of asynchronous +methods can be very contageous in a system, where if a lower level API +is composed of asynchronous methods, a higher level system which uses +those methods also has to have asynchronous methods in its interface +(or risk blocking its calling thread).
+
+TODO? : +diagram of a layered system with asynchronous APIs
+

Request Monitor

+There is a standard callback object used in DSF, the request +monitor.  A request monitor has the following features:
+ +Following is the snippet from a the +"hello world" example of using a +request monitor:
+
+ + + + + + + + + + +
org.eclipse.dd.examples.dsf.requestmonitor.AsyncHelloWorld
+

+
+
 26: public class AsyncHelloWorld {

28: public static void main(String[] args) {
29: Executor executor = ImmediateExecutor.getInstance();
30: RequestMonitor rm = new RequestMonitor(executor, null);
31: asyncHelloWorld(rm);
32: }

34: static void asyncHelloWorld(RequestMonitor rm) {
35: System.out.println("Hello world");
36: rm.done();
37: }
+
+
+

+

+ + + + + + + +
Excercise 1: A common +problem in DSF is implementing nested asynchronous methods, this +excercise adds a second-level asynchronous method to +AsyncHelloWorld. 
+

Look +for comments preceeded with "// TODO Excercise 1" in the +org.eclipse.dd.examples.dsf.requestmonitor.AsyncHelloWorld +module.

+
+
+

Data Request Monitor

+The base request monitor is useful for returning +status of the +asynchronous method, but they do not have an option of returning a +value to the caller.  DataRequestMonitor can be used for that +purpose. A simple example of using the data request monitor:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.dsf.requestmonitor.Async2Plus2
+

+
+
 22: public class Async2Plus2 {
23:
24: public static void main(String[] args) {
25: Executor executor = ImmediateExecutor.getInstance();
26: DataRequestMonitor<Integer> rm =
27: new DataRequestMonitor<Integer>(executor, null) {
28: @Override
29: protected void handleCompleted() {
30: System.out.println("2 + 2 = " + getData());
31: }
32: };
33: asyncAdd(2, 2, rm);
34: }

36: static void asyncAdd(int value1, int value2, DataRequestMonitor<Integer> rm) {
37: rm.setData(value1 + value2);
38: rm.done();
39: }
40: }
+
+
+ +

Multi-Request Monitor

+A common problem when using asynchronous is that several asynchronous +methods need to be called in parallel, so the calling method needs to +somehow manage the completion of several request monitors.  +CountingRequestMonitor can be used for this purpose.  It is +configured such that it's done() method needs to be called a count number of times before the +callback method is invoked. 
+The following snipped from the AsyncQuicksort example shows a simple +example of using the CountingRequestMonitor:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.dsf.requestmonitor.AsyncQuicksort.asyncQuickSort()
+

+
+
 42:     static void asyncQuicksort(final int[] array, final int left, 
43: final int right, final RequestMonitor rm)
44: {
45: if (right > left) {
46: int pivot = left;
48: int newPivot = partition(array, left, right, pivot);
49: printArray(array, left, right, newPivot);

51: CountingRequestMonitor countingRm = new CountingRequestMonitor(fgExecutor, rm);
52: asyncQuicksort(array, left, newPivot - 1, countingRm);
53: asyncQuicksort(array, newPivot + 1, right, countingRm);
54: countingRm.setDoneCount(2);
55: } else {
56: rm.done();
57: }
58: }
+
+
+ + + + + + + +
Excercise 2: Converting a +synchronous method into an asynchronous one, is another common task in +DSF.  This excercise converts the AsyncQuicksort.partition() +method into asynchronous AsyncQuicksort.asyncPartition(). 
+

Look +for comments preceeded with "// TODO Excercise 2" in the +org.eclipse.dd.examples.dsf.requestmonitor.AsyncQuicksort +module.

+
+

Concurrency

+The simple examples in previous section used asynchronous method +signatures, however no real asynchronous work was performed since all +execution was performed in the main thread.  This section examines +a more typical example of a problem that DSF is intended to solve: a +viewer and an asynchronous data generator.
+

The IDataGenerator interface contains the following two asynchronous +data access methods:
+

+
+ + + + + + + + + + +
org.eclipse.dd.examples.dsf.dataviewer.IDataGenerator
+

+
+
 49:  void getCount(DataRequestMonitor<Integer> rm);
50: void getValue(int index, DataRequestMonitor<String> rm);
+
+
+

The example is intended to simulate a realistic problem therefore, +it +can be assumed that these methods do not complete the request monitor +immediately, but rather that the requests are completed on a separate +thread and with some delay.  There are two implementations of this +service provided:

+
    +
  1. DataGeneratorWithThread - Uses a java thread directly and various +synchronization mechanisms for data integrity.
    +
  2. +
  3. DataGeneratorWithExecutor - Uses a DSF executor for both +asynchronous execution and synchronization.
  4. +
+There are also two viewers provided which display data from the data +generator:
+
    +
  1. SyncDataViewer - Table-based viewer which implements a +synchronous IStructuredContentProvider interface.
    +
  2. +
  3. AsyncDataViewer - Table-based viewer which implements an +asynchronous ILazyContentProvider interface.
  4. +
+

Query

+DSF is designed to facilitate use of asynchronous APIs.  However, +sometimes there are situations where a synchronous method has to be +implemented to call an asynchronous method.  One utility used to +accomplish this is a DSF Query object.  The Query object is meant +to be extended by clients in order to override the asynchronous +execute() method. The client code using a query can use the execute() +implementation in order to call other asynchronous methods.  The +following snippet +from SyncDataViewer.getElements()  shows the use of Query:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.dsf.dataviewer.SyncDataViewer.getElements() +

+
+
 59:         // Create the query object for reading data count. 
60: Query<Integer> countQuery = new Query<Integer>() {
61: @Override
62: protected void execute(DataRequestMonitor<Integer> rm) {
63: fDataGenerator.getCount(rm);
64: }
65: };
66:
67: // Submit the query to be executed. A query implements a runnable
68: // interface and it has to be executed in order to do its work.
69: ImmediateExecutor.getInstance().execute(countQuery);
70: int count = 0;
71:
72: // Block until the query completes, which will happen when the request
73: // monitor of the execute() method is marked done.
74: try {
75: count = countQuery.get();
76: } catch (Exception e) {
77: // InterruptedException and ExecutionException can be thrown here.
78: // ExecutionException containing a CoreException will be thrown
79: // if an error status is set to the Query's request monitor.
80: return new Object[0];
81: }
+
+
+ + + + + + + + + + +

+
Image 1: Sequence diagram of Query use in +getElements().
+
+
+
+
+ + + + + + +
Note: Using the query object +requires a great deal of care because calling +a blocking method can create performance problems and raises +possibility of deadlock. One common deadlock scenario occurs when +the get() method is being called by a thread which is itself required +for completion of the asynchronous methods called by execute().
+
+

TODO +?: add a sequence diagram of the deadlock scenario
+

+

+

Synchronization

+Managing race conditions and deadlocks is one of the most challanging +problems of large multi-threaded systems.  DSF uses a +single-threaded executor as the primary mechanism for safe-guarding +access to data.  Methods, which need to access data protected by +the DSF executor, have to access this data inside a runnable submitted +to the executor thread.  The following is an example of this from +the DataGeneratorWithExecutor:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.dsf.dataviewer.DataGeneratorWithExecutor.addListener()
+

+
+
174:     public void addListener(final Listener listener) {
175: try {
176: fExecutor.execute( new DsfRunnable() {
177: public void run() {
178: fListeners.add(listener);
179: }
180: });
181: } catch (RejectedExecutionException e) {}
182: }
+
+
+ + + + + + + +
Note: +It is immediately apparent that this synchronization mechanism +adds a lot of overhead and for such a simple example, it is much less +efficient than using a synchronized section or an atomic +variable.  It +is less obvious how this mechanism adds value, however this document is +just a tutorial so the discussion of the merits of the design will be +left out.
+
+ + + + + + + + + + + +

+
Image 1: Synchronization using multiple +locks on data.
+
Image 2: Synchronization using a single +DSF executor thread.
+
+Comparing other parts of the two data generator implementation shows +that using the synchronization mechanism above is the principal +difference between the two implementations.  One notable exception +is the principal processing loop in each data generator.  In the +thread-based implementation this loop is implemented in the run method +of the generator's thread:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.dsf.dataviewer.DataGeneratorWithThread.run()
+

+
+
139:     public void run() {
140: try {
141: while(true) {
142: // Get the next request from the queue. The time-out
143: // ensures that that the random changes get processed.
144: final Request request = fQueue.poll(100, TimeUnit.MILLISECONDS);
145:
146: // If a request was dequeued, process it.
147: if (request != null) {
148: // Simulate a processing delay.
149: Thread.sleep(PROCESSING_DELAY);
150:
151: if (request instanceof CountRequest) {
152: processCountRequest((CountRequest)request);
153: } else if (request instanceof ItemRequest) {
154: processItemRequest((ItemRequest)request);
155: } else if (request instanceof ShutdownRequest) {
156: // If shutting down, just break out of the while(true)
157: // loop and thread will exit.
158: request.fRequestMonitor.done();
159: break;
160: }
161: }
162:
163: // Simulate data changes.
164: randomChanges();
165: }
166: }
167: catch (InterruptedException x) {}
168: }
+
+
+ +

In contrast the executor-based generator uses a dedicated method for +servicing the queue, which is called by every method that adds a new +request to the queue:
+

+
+ + + + + + + + + + +
org.eclipse.dd.examples.dsf.dataviewer.DataGeneratorWithExecutor.serviceQueue() +

+
+
197:     private void serviceQueue() {
...
201: // If a queue servicing is already scheduled, do nothing.
202: if (fServiceQueueInProgress) {
203: return;
204: }
205:
206: if (fQueue.size() != 0) {
207: // If there are requests to service, remove one from the queue and
208: // schedule a runnable to process the request after a processing
209: // delay.
210: fServiceQueueInProgress = true;
211: final Request request = fQueue.remove(0);
212: fExecutor.schedule(
213: new DsfRunnable() {
214: public void run() {
215: if (request instanceof CountRequest) {
216: processCountRequest((CountRequest)request);
217: } else if (request instanceof ItemRequest) {
218: processItemRequest((ItemRequest)request);
219: }
220:
221: // Reset the processing flag and process next
222: // request.
223: fServiceQueueInProgress = false;
224: serviceQueue();
225: }
226: },
227: PROCESSING_DELAY, TimeUnit.MILLISECONDS);
228: }
229: }
+
+
+ + + + + + + +
Note: When using a single-threaded +executor as the synchronization +method very few other synchronization mechanisms need to be used.  +For example the DataGeneratorWithExecutor.fQueue member is just a plain +un-synchronized list.  This is true even when using background +threads to perform long-running tasks, as long as these background +threads can call a request monitor when finished.
+
+
+ + + + + + +
Excercise 3: One benefit of +the single-threaded executor concurrency model is that as long as a +method is guaranteed to run in the executor thread, this method may +access and modify any of the variables protected by this +executor.  This excercise demonstrates performing a somewhat more +complicated operation on protected state data.
+

Look +for comments preceeded with "// TODO Excercise 3" in the +org.eclipse.dd.examples.dsf.dataviewer.DataGeneratorWithExcecutor +module.

+
+

Annotations

+In any multi-threaded system it can become very difficult to determine +what are the rules governing access to the various data objects.  +In a DSF system, it is even more important to identify which data +objects can only be accessed using a designated DSF executor.  +Since there is no Java language mechanisms for this purpose, DSF +defines a number annotations that can be used for this purpose.  +The annotations are hierarchical, so that if a class has a given +annotation in its declaration, its members and fields are assumed to +have the same access restriction unless otherwise specified.
+

DSF synchronization annotations defined in +org.eclipse.dd.dsf.concurrent
+

+ +
+ + + + + + +
Note: The DSF synchronization +annotations are no more than a comment intended to help make the code +more understandable and maintainable.  Unfortunately, since there +is no compiler enforcment of their presence, it is easy to forget to +add them.
+
+
+
+ + + + + + +
+

Excercise 4: +This excercise adds the appropriate synchronization annotations to the +methods and fields of DataProviderWithExecutor.
+

+

Look +for comments preceeded with "// TODO Excercise 4" in the +org.eclipse.dd.examples.dsf.dataviewer.DataGeneratorWithExcecutor +module.

+
+
+
+ + + + + + +
Excercise 5: It is all too +easy to get into a deadlock situation.  This excercise +purposefully puts the data viewer system into a deadlock.  The +deadlock first renders the data viewer unusable, but the main thread +also gets deadlocked when attempting to exit the program.
+

Look +for comments preceeded with "// TODO Excercise 5" in the +org.eclipse.dd.examples.dsf.dataviewer.SyncDataViewer +module.

+
+

Services

+

OSGi

+

Session

+

Tracker

+

Data Model

+

View Model

+

Adapter, Provider, Node

+

Timers

+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.dsf.requestmonitor.Async2Plus2
+

+

+
+
+
+ + + + + + +

+
+
+
+ + + + + + +
Excercise abc: xyz
+
+
+ + diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/query_1.dia b/plugins/org.eclipse.dd.doc.dsf/docs/common/query_1.dia new file mode 100644 index 00000000000..68078a3d47b Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/query_1.dia differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/query_1.png b/plugins/org.eclipse.dd.doc.dsf/docs/common/query_1.png new file mode 100644 index 00000000000..e5563fcb6f2 Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/query_1.png differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/synchronization_1.dia b/plugins/org.eclipse.dd.doc.dsf/docs/common/synchronization_1.dia new file mode 100644 index 00000000000..6d523d8702b Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/synchronization_1.dia differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/synchronization_1.png b/plugins/org.eclipse.dd.doc.dsf/docs/common/synchronization_1.png new file mode 100644 index 00000000000..181a77f4401 Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/synchronization_1.png differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/synchronization_2.dia b/plugins/org.eclipse.dd.doc.dsf/docs/common/synchronization_2.dia new file mode 100644 index 00000000000..8a725e36e02 Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/synchronization_2.dia differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/synchronization_2.png b/plugins/org.eclipse.dd.doc.dsf/docs/common/synchronization_2.png new file mode 100644 index 00000000000..63124ecdf1b Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/synchronization_2.png differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/pda/dsf_common_patterns.html b/plugins/org.eclipse.dd.doc.dsf/docs/pda/dsf_common_patterns.html deleted file mode 100644 index da0a8d99b5b..00000000000 --- a/plugins/org.eclipse.dd.doc.dsf/docs/pda/dsf_common_patterns.html +++ /dev/null @@ -1,159 +0,0 @@ - - - - DSF Common Patterns - - -

DSF Common Patterns
-

-

Summary

-

DSF

- -

Asynchronous Methods

-One of the central features of DSF is that it relies very heavily on -the use of asynchronous methods.  Asynchronous methods here mean -simply methods that use a callback -object to indicate their completion. The use of asynchronous -methods can be very contageous in a system, where if a lower level API -is composed of asynchronous methods, a higher level system which uses -those methods also has to have asynchronous methods in its interface -(or risk blocking its calling thread).
-
-TODO? : diagram of a layered system with asynchronous APIs
-

Request Monitor

-There is a standard callback object used in DSF, the request -monitor.  A request monitor has the following features:
- -Following is the snippet from a the "hello world" example of using a -request monitor:
-
 26: public class AsyncHelloWorld {

28: public static void main(String[] args) {
29: Executor executor = ImmediateExecutor.getInstance();
30: RequestMonitor rm = new RequestMonitor(executor, null);
31: asyncHelloWorld(rm);
32: }

34: static void asyncHelloWorld(RequestMonitor rm) {
35: System.out.println("Hello world");
36: rm.done();
37: }
- -

Data Request Monitor

-The base request monitor is useful for returning status of the -asynchronous method, but they do not have an option of returning a -value to the caller.  DataRequestMonitor can be used for that -purpose. A simple example of using the data request monitor:
-
 22: public class Async2Plus2 {

24: public static void main(String[] args) {
25: Executor executor = ImmediateExecutor.getInstance();
26: DataRequestMonitor<Integer> rm =
27: new DataRequestMonitor<Integer>(executor, null) {
28: @Override
29: protected void handleCompleted() {
30: System.out.println("2 + 2 = " + getData());
31: }
32: };
33: asyncAdd(2, 2, rm);
34: }

36: static void asyncAdd(int value1, int value2, DataRequestMonitor<Integer> rm) {
37: rm.setData(value1 + value2);
38: rm.done();
39: }
40: }
- -

Multi-Request Monitor

-A common problem when using asynchronous is that several asynchronous -methods need to be called in parallel, so the calling method needs to -somehow manage the completion of several request monitors.  -CountingRequestMonitor can be used for this purpose.  It is -configured such that it's done() method needs to be called a count number of times before the -callback method is invoked. 
-The following snipped from the AsyncQuicksort example shows a simple -example of using the CountingRequestMonitor:
-
 42:     static void asyncQuicksort(final int[] array, final int left, 
43: final int right, RequestMonitor rm)
44: {
45: if (right > left) {
46: int pivot = left;
47: int newPivot = partition(array, left, right, pivot);
48: printArray(array, left, right, newPivot);
49:
50: CountingRequestMonitor countingRm = new CountingRequestMonitor(fgExecutor, rm);
51: asyncQuicksort(array, left, newPivot - 1, countingRm);
52: asyncQuicksort(array, newPivot + 1, right, countingRm);
53: countingRm.setDoneCount(2);
54: } else {
55: rm.done();
56: }
57: }
- -

Non-Executor Thread

-

Future

-

Query

-

Concurrency Annotations
-

-

Services

-

OSGi

-

Session

-

Tracker

-

Data Model

-

View Model

-

Adapter, Provider, Node

-

Timers

- -