From e75b2b1a1fbddb4af76ceca3dbe9129dbf98efcc Mon Sep 17 00:00:00 2001 From: Pawel Piech Date: Thu, 6 Mar 2008 23:22:16 +0000 Subject: [PATCH] [220446] Updated the "DSF Common Patterns" document. --- .../docs/common/dsf_common_patterns.html | 694 +++++++++++++++--- .../docs/pda/pda_tutorial_outline.html | 56 +- 2 files changed, 662 insertions(+), 88 deletions(-) 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 index 0fd59f1a441..96a68ec621e 100644 --- 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 @@ -4,12 +4,71 @@ DSF Common Patterns -

DSF Common Patterns
+

DSF Common Patterns

-

Summary

-

Examples
+ +

1 Introduction

+This document contains a tutorial for the basic features of the +Debugger Services Framework (DSF), created by the DSDP Device Debugging +project.
+

2 Examples

-Running example code is and performing included excercises is very +Running example code is and performing included exercises is very helpful in following this tutorial.  In order to run the examples in this tutorial the following is needed:
    @@ -39,7 +98,7 @@ directory.
  1. Build the examples plugin:
    1. Execute the build the first time to build and run the -excercises preprocessor.
    2. +exercises preprocessor.
    3. Refresh the resources in the plugin (right-click on project in Navigator and select Refresh), in order to recognize the @@ -48,28 +107,29 @@ sources generated by the preprocessor.
  2. Launch the examples
    1. -
    2. Examples in data org.eclipse.dd.examples.dsf.requestmonitor and -org.eclipse.dd.examples.dsf.dataviewer packages each contain a public +
    3. 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.
    4. -
    5. TODO: Launching timers -example
    6. +
    7. The timers example in org.eclipse.dd.examples.dsf.timers +requires an Eclipse Application to be launched (see the Timers Example section for more details).
      +
-

Asynchronous Methods

+

3 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 +methods can be very contagious 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

+(or risk blocking its calling thread).
+

3.1 Request Monitor

There is a standard callback object used in DSF, the request monitor.  A request monitor has the following features:
+ + + + + + + + + +

+
Image 1: Sequence diagram of the +AsyncHelloWorld example.
+
@@ -151,12 +228,12 @@ but often suble and difficult to track down bug @@ -164,7 +241,8 @@ module.

Excercise 1: A common + style="text-decoration: underline;">Exercise 1: A common problem in DSF is implementing nested asynchronous methods, this -excercise adds a second-level asynchronous method to +exercise adds a second-level asynchronous method to AsyncHelloWorld. 

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


-

Data Request Monitor

+

3.2 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 @@ -214,7 +292,8 @@ methods. the calculation to the console.
-

Multi-Request Monitor

+

3. 4 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.  @@ -279,19 +358,19 @@ execution paths! Excercise 2: Converting a + style="text-decoration: underline;">Exercise 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 +for comments preceeded with "// TODO Exercise 2" in the org.eclipse.dd.examples.dsf.requestmonitor.AsyncQuicksort module.

-

Concurrency

+

4 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 @@ -343,7 +422,7 @@ synchronous IStructuredContentProvider interface.
  • AsyncDataViewer - Table-based viewer which implements an asynchronous ILazyContentProvider interface.
  • -

    Query

    +

    4.1 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 @@ -418,7 +497,7 @@ the request monitor from the execute() method is completed. Image 1: Detailed sequence of calling + style="font-weight: bold;">Image 2: Detailed sequence of calling IDataGenerator.getCount() in SyncDataViewer.getElements().
    @@ -440,7 +519,7 @@ for completion of the asynchronous methods called by execute().

    -

    Synchronization

    +

    4.2 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 @@ -513,11 +592,11 @@ left out.
    Image 2: Synchronization using multiple + style="font-weight: bold;">Image 3: Synchronization using multiple locks on data.
    Image 3: Synchronization using a single + style="font-weight: bold;">Image 4: Synchronization using a single DSF executor thread.
    @@ -668,21 +747,21 @@ threads can call a request monitor when finished.
    Excercise 3: One benefit of + style="text-decoration: underline;">Exercise 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 +for comments preceeded with "// TODO Exercise 3" in the org.eclipse.dd.examples.dsf.dataviewer.DataGeneratorWithExcecutor module.

    -

    Annotations

    +

    4.3 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 @@ -742,12 +821,12 @@ add them.
    -

    Excercise 4: +

    Exercise 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 +for comments preceeded with "// TODO Exercise 4" in the org.eclipse.dd.examples.dsf.dataviewer.DataGeneratorWithExcecutor module.

    @@ -763,20 +842,21 @@ module.

    Excercise 5: It is all too + style="text-decoration: underline;">Exercise 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 +for comments preceeded with "// TODO Exercise 5" in the org.eclipse.dd.examples.dsf.dataviewer.SyncDataViewer module.

    -

    Timers Example

    +

    5 Timers Example

    The Timers example, found in the org.eclipse.dd.examples.dsf.timers @@ -859,7 +939,7 @@ and are automatically selected upon a triggered event.
    Image 4: Screen shot of the Timers view.
    + style="font-weight: bold;">Image 5: Screen shot of the Timers view.
    @@ -885,8 +965,8 @@ editor - changes the value of the selected trigger.
    -

    Services

    -

    OSGi

    +

    6 Services

    +

    6.1 OSGi

    DSF builds on top of OSGi services APIs.   OSGi offers a rich API for managing services and it is important to understand some of the OSGi service API basics in order to use DSF
    @@ -938,7 +1018,7 @@ down the debugger.

    -

    Session

    +

    6.2 Session

    DSF-based debugger integrations can register many services and there can be multiple instances of debuggers registering services with the same interfaces.  To help coordinate services in a give debugger @@ -952,24 +1032,24 @@ debuggers, DSF services are organized into sessions.
    sessions.  Clients may also obtain a session instance using an ID through a static method.
    -
  • Session Lifecycle Events - Clients may register to listen when +
  • Session Life cycle Events - Clients may register to listen when sessions are started and ended.
  • DSF Executor - -Eash session has a (single-threaded) DSF Executor  associated with +Each session has a (single-threaded) DSF Executor  associated with it, though multiple sessions could share a single executor.  More about session executor in the next section.
  • Service Events - The session is used to dispatch service events.  More on events in following sections.
  • Model Adapters - -A session allws an adapter to be registered, which will be returned by +A session allows an adapter to be registered, which will be returned by all Data Model contexts in a given session for a given adapter type.  More information about Data Model is described in the Data Model section.
  • -

    Executor

    +

    6.3 Executor

    All the services registered with the same session share a single DSF Executor.  By convention, all public service interfaces should be restricted to being called in this executor thread.  This point @@ -988,11 +1068,12 @@ should be called using the session's DSF executor.
    -

    Tracker

    +

    6.4 Tracker

    Using the OSGi APIs for accessing services directly can be very cumbersome.  A client retrieving a service reference is responsible for retaining the ServiceReference object and for calling -BundleContext.ungetService() to avoid leaking the refernce.  Also, +BundleContext.ungetService() to avoid leaking the reference.  +Also, since a service may be un-registered at any time, the clients need to listen for events indicating when a service is unregistered.  Fortunately there are two utilities which help with this task
    @@ -1027,7 +1108,7 @@ safety executor thread. - Accessor + Accessors methods
    @@ -1057,7 +1138,7 @@ methods
    @@ -1065,7 +1146,7 @@ down and un-gets all service references. -

    View Model

    +

    8 View Model

    View Model refers to the ideal user-presentable structure of the data.  This is in contrast to the Data Model, which refers to the natural data structure, although the two often end up being the same.  Never the less, the needs of the user presentation often change so the central feature of the View Model is the ability to customize it.
    -

    Flexible Hierarchy

    +

    8.1 Flexible Hierarchy

    View Model builds on the flexible hierarchy API introduced by Debug Platform team in release 3.2.  The flexible hierarchy API has a @@ -1507,7 +1589,7 @@ related to packaging.
    -

    Adapter Problem
    +

    8.2 Adapter Problem

    The number two feature of flexible hierarchy API is implemented using the adapter pattern.  One down-side of the adapter pattern is that @@ -1515,22 +1597,76 @@ there can only be one instance of an adapter of a particular type registered for a given element.  For flexible hierarchy providers, it means that each provider must implement the element presentation logic for every view that the element appears in, and as a result -adding a new view can force changing a large number of modules.
    -

    TODO: add a diagram of the -adapter pattern used by multiple views.
    -

    -

    To overcome the adapter pattern limitation, the View Model uses +adding a new view can force changing a large number of modules. + + + + + + + + + +

    +
    Image 6: Diagram illustrating problem of +multiple views sharing a single element, when using the adapter +mechanism.
    +
    +To overcome the adapter pattern limitation, the View Model uses wrapper objects.  The wrapper objects are held by the viewer and they redirect the requests for different flexible hierarchy providers to the appropriate modules.
    -

    -

    TODO: add a diagram of the View -Model hierarchy adapter pattern use
    -

    -

    IVMAdapter -> IVMProvider -> IVMNode -> IVMContext

    -
    -
    -
    + + + + + + + + + +

    +
    Image 7: Diagram illustrating use of +wrappers to allow different adapters to be associated with the same +element, for use with different views.
    +
    +

    8.4 API

    +The View Model uses four principal types of elements when processing +adapter requests from flexible hierarchy viewers.  These are:
    +
      +
    1. IVMAdapter - The +adapter is the top-level object in the view model hierarchy. It +implements some of the flexible hierarchy provider interfaces and it is +returned by the elements as the single adapter implementing those +interfaces  The main function the VM Adapter is to redirect the +provider requests based on the view ID that the requests originated +from.
      +
    2. +
    3. IVMProvider - In +the View Model hierarchy, the provider object supplies view content for +a single view.
    4. +
    5. IVMNode - The +node allows for grouping of like-elements in the view.  Examples +of such nodes are: threads node, stack frames node, variables node, etc.
    6. +
    7. IVMContext - The +VM Context is the interface of the wrapper objects that are held by the +flexible hierarchy viewers.  The context holds a reference to the +element that is being wrapped, as well as to the VM Node and VM Adapter +that it originated from.
      +
    8. +
    +

    8.5 Content Provider

    +The most important job of the View Model is to supply the content, in +form of element wrappers, to the viewers.  The flexible hierarchy +interface for this provider is listed below:

    -
    org.eclipse.dd.examples.dsf.requestmonitor.Async2Plus2
    + style="font-family: monospace; font-weight: bold;">org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider


    +
    +
     20: public interface IElementContentProvider {

    22: /**
    23: * Updates the number of children for the given parent elements in the
    24: * specified requests.
    25: *
    26: * @param updates Each update specifies an element to update and provides
    27: * a store for the result. The update array is guaranteed to have at least
    28: * one element, and for all updates to have the same presentation context.
    29: */
    30: public void update(IChildrenCountUpdate[] updates);
    31:
    ...
    39: public void update(IChildrenUpdate[] updates);
    40:
    ...
    48: public void update(IHasChildrenUpdate[] updates);
    49:
    50: }

    +Notice that each request for data is +encapsulated inside an update +object.  Every update +implements the IViewerUpdate interface listed here:
    +
    +
    + + + + + + + + + + +
    org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate +

    +
    +
     23: public interface IViewerUpdate extends IRequest {

    25: /**
    26: * Returns the context this update was requested in.
    27: *
    28: * @return context this update was requested in
    29: */
    30: public IPresentationContext getPresentationContext();
    31:
    32: /**
    33: * Returns the model element associated with this request.
    34: *
    35: * @return associated model element
    36: */
    37: public Object getElement();
    38:
    39: /**
    40: * Returns the viewer tree path to the model element associated with this
    41: * request. An empty path indicates a root element.
    42: *
    43: * @return tree path, possibly empty
    44: */
    45: public TreePath getElementPath();
    46:
    47: /**
    48: * Returns the element that was the viewer input at the time the
    49: * request was made, possibly <code>null</code>.
    50: *
    51: * @return viewer input element, possibly <code>null</code>
    52: */
    53: public Object getViewerInput();
    54: }
    +
    +
    +
    +These update requests are received by the adapter object in the View +Model hierarchy and are channeled to the correct node that should +process the given update.  This procedure is described in a few +steps:
    +
      +
    1. VM Adapter receives the update request.  For example the +update(IChildrenUpdate[]) method is called by a viewer.
    2. +
    3. VM Adapter retrieves the ID in the IPresentationContext object +associated with the update.
    4. +
    5. VM Adapter finds the VM Provider that is assigned for that +presentation context, creating it if necessary.
    6. +
    7. VM Provider finds and calls the VM Node (or nodes) that should +process the +given update. 
      +
    8. +
    9. VM Node retrieves fills the data in the update and marks it as +completed.
    10. +
    +
    -

    +
    Note: In Step 4, the logic used to +determine which VM Node should process the content request is +customizable.  However, in general VM Nodes are arranged in a +parent-child hierarchy which mirrors the layout of elements in the view.

    -
    - +
    Excercise abc: xyz Note: Also in Step 4, the VM +Provider may be customized to do additional processing on the +update.  For example, the provider may cache the results of +previous updates and return cached results without ever calling the VM +Node.
    +

    +As a sample from the Timers example of the VM Node logic that fulfills +the content update request:

    +
    + + + + + + + + + + +
    org.eclipse.dd.examples.dsf.timers.TimersVMNode
    +

    +
    +
     86:     protected void updateElementsInSessionThread(final IChildrenUpdate update) {
    87: if (!checkService(AlarmService.class, null, update)) return;

    89: // Retrieve the timer DMContexts, create the corresponding VMCs array, and
    90: // set them as result.
    91: TimerDMContext[] timers =
    92: getServicesTracker().getService(TimerService.class).getTimers();
    93: fillUpdateWithVMCs(update, timers);
    94: update.done();
    95: }
    +
    +
    + +

    8.6 Model Event Proxy

    +Another important job of the View Model is to translate the events that +originate from the Data Model into generic events (deltas) that the viewer can +understand.  For this purpose, the Flexible Hierarchy has the IModelProxy interface which is used +in the following manner:
    +
      +
    1. An root element is +added into the view.  The root +element could be the view input or another element which is designated +as the root of a model hierarchy.
    2. +
    3. The viewer gets the IModelProxyFactory +adapter from the element.
    4. +
    5. The viewer calls IModelProxyFactory.createModelProxy() +which returns the IModelProxy instance.
    6. +
    7. The viewer adds itself as a listener +to model events with the model proxy +object.
    8. +
    9. The model proxy notifies its listeners (including the viewer) +with model deltas whenever +the model has changed.
    10. +
    11. When the root element is removed from the viewer, the viewer +disposes the model proxy.
      +
    12. +
    +For View Model users, much of the logic of creating the model proxy +factory is implemented using abstract base classes in the org.eclipse.dd.dsf.ui.viewmodel +package.  Instead, a typical DSF application only has to implement +two interfaces in order to support event proxying:
    +

    Event handling in VM +Provider
    +
    The following listing from the Timers example shows the handling +of a model event:
    +

    +
    + + + + + + + + + + +
    org.eclipse.dd.examples.dsf.timers.TimersVMProvider +

    +
    +
    108:     @DsfServiceEventHandler
    109: public void eventDispatched(final TimersChangedEvent event) {
    110: if (isDisposed()) return;

    112: try {
    113: getExecutor().execute(new Runnable() {
    114: public void run() {
    115: if (isDisposed()) return;
    116: handleEvent(event);
    117: }
    118: });
    119: } catch (RejectedExecutionException e) {}
    120: }
    +
    +
    + +Processing events in VM Nodes
    +The following listing shows how the nodes translate the events into +model delta components:
    +
    +
    + + + + + + + + + + +
    org.eclipse.dd.examples.dsf.timers.TimersVMNode +

    +
    +
    141:     public int getDeltaFlags(Object e) {
    142: // This node generates delta if the timers have changed, or if the
    143: // label has changed.
    144: if (e instanceof TimerService.TimerTickDMEvent) {
    145: return IModelDelta.STATE;
    146: } else if (e instanceof TimerService.TimersChangedEvent) {
    147: return IModelDelta.CONTENT;
    148: }
    149: return IModelDelta.NO_CHANGE;
    150: }

    152: public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor requestMonitor) {
    153: if (e instanceof TimerService.TimerTickDMEvent) {
    154: // Add delta indicating that the given timer has changed.
    155: parentDelta.addNode( createVMContext(((TimerService.TimerTickDMEvent)e).getDMContext()), IModelDelta.STATE );
    156: } else if (e instanceof TimerService.TimersChangedEvent) {
    157: // The list of timers has changed, which means that the parent
    158: // node needs to refresh its contents, which in turn will re-fetch the
    159: // elements from this node.
    160: parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT);
    161: }
    162: requestMonitor.done();
    163: }
    +
    +
    + +

    8.7 Property Provider +/ Label Provider
    +

    +The IElementPropertyProvider +interface is not actually part of the Flexible Hierarchy API, but an +extension interface added by DSF.  View Model implementers can use +the property provider to configure an  IElementLabelProvider +object +that the Flexible Hierarchy viewers call in order to get element +presentation details.
    +

    Below is the listing from the Timers VM Node that configures the +label provider:
    +

    +
    + + + + + + + + + + +
    org.eclipse.dd.examples.dsf.timers.TimersVMNode +

    +
    +
     53:     private static final PropertyBasedLabelProvider fgLabelProvider;
    54: static {
    55: fgLabelProvider = new PropertyBasedLabelProvider();

    57: LabelColumnInfo idCol = new LabelColumnInfo(
    58: new LabelAttribute[] {
    59: new LabelText(new MessageFormat("Timer #{0}"),
    60: new String[] { PROP_TIMER_NUMBER }),
    61: new LabelImage(DsfExamplesPlugin.getDefault().getImageRegistry().
    62: getDescriptor(DsfExamplesPlugin.IMG_ALARM))
    63: });
    64: fgLabelProvider.setColumnInfo(TimersViewColumnPresentation.COL_ID, idCol);
    65:
    66: LabelColumnInfo valueCol = ...
    ...
    74: }
    +
    +
    + +All that is left is to implement the property provider:
    +
    +
    + + + + + + + + + + +
    org.eclipse.dd.examples.dsf.timers.TimersVMNode +

    +
    +
     98:     public void update(final IPropertiesUpdate[] updates) {
    99: // Switch to the session thread before processing the updates.
    100: try {
    101: getSession().getExecutor().execute(new DsfRunnable() {
    102: public void run() {
    103: for (IPropertiesUpdate update : updates) {
    104: updatePropertiesInSessionThread(update);
    105: }
    106: }});
    107: } catch (RejectedExecutionException e) {
    108: for (IViewerUpdate update : updates) {
    109: handleFailedUpdate(update);
    110: }
    111: }
    112: }

    114: @ConfinedToDsfExecutor("getSession#getExecutor")
    115: private void updatePropertiesInSessionThread(final IPropertiesUpdate update) {
    116: // Find the timer context in the element being updated
    117: final TimerDMContext dmc = findDmcInPath(
    118: update.getViewerInput(), update.getElementPath(), TimerDMContext.class);
    119:
    120: // If either update or service are not valid, fail the update and exit.
    121: if (!checkDmc(dmc, update) ||
    122: !checkService(TimerService.class, null, update))
    123: {
    124: return;
    125: }
    126:
    127: TimerService timerService =
    128: getServicesTracker().getService(TimerService.class, null);
    129: int value = timerService.getTimerValue(dmc);
    130:
    131: if (value == -1) {
    132: handleFailedUpdate(update);
    133: return;
    134: }

    136: update.setProperty(PROP_TIMER_NUMBER, dmc.getTimerNumber());
    137: update.setProperty(PROP_TIMER_VALUE, value);
    138: update.done();
    139: }
    +
    +
    +
    + diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/pda/pda_tutorial_outline.html b/plugins/org.eclipse.dd.doc.dsf/docs/pda/pda_tutorial_outline.html index b9582503897..d85019cd9b3 100644 --- a/plugins/org.eclipse.dd.doc.dsf/docs/pda/pda_tutorial_outline.html +++ b/plugins/org.eclipse.dd.doc.dsf/docs/pda/pda_tutorial_outline.html @@ -10,9 +10,63 @@

    DSF

    +

    Debug Services

    +Use of services is intended to allow for maximum level of extendability +and customization.  To achieve this, service interfaces should +encapsulate functionality that logically belongs together and at the +same time allow for a managable number of services which have a clear +hierarchy.  DSF defines a set of standard debug interfaces (in the +org.eclipse.dd.dsf.debug plugin), and supplies a set of classes that +populate the standard debug views using those interfaces (in the +org.eclipse.dd.dsf.debug.ui plugin).  However there are very few +dependencies in these service interfaces and a given debugger +implementation may leave out or replace any of them as its custom +functionality dictates. 
    +

    The standard debug service interfaces include:

    + +


    +

    +
    +

    Push Down Automata (PDA)