diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/data_model_1.dia b/plugins/org.eclipse.dd.doc.dsf/docs/common/data_model_1.dia new file mode 100644 index 00000000000..8dbabe73f15 Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/data_model_1.dia differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/data_model_1.png b/plugins/org.eclipse.dd.doc.dsf/docs/common/data_model_1.png new file mode 100644 index 00000000000..41b89aa26be Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/data_model_1.png differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/data_model_2.dia b/plugins/org.eclipse.dd.doc.dsf/docs/common/data_model_2.dia new file mode 100644 index 00000000000..d942c53ae42 Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/data_model_2.dia differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/data_model_2.png b/plugins/org.eclipse.dd.doc.dsf/docs/common/data_model_2.png new file mode 100644 index 00000000000..363100dcca7 Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/data_model_2.png differ 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 79f481f79fb..0fd59f1a441 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 @@ -96,7 +96,7 @@ Following is the snippet from a the "hello world" example of using a request monitor:
- @@ -172,7 +172,7 @@ value to the caller.  DataRequestMonitor can be used for that purpose. A simple example of using the data request monitor:

-
@@ -226,7 +226,7 @@ The following snipped from the AsyncQuicksort example shows a simple example of using the CountingRequestMonitor:

-
@@ -280,7 +280,7 @@ execution paths!
Excercise 2: Converting a -synchronous method into an asynchronous one, is another common task in +synchronous method into an asynchronous one is another common task in DSF.  This excercise converts the AsyncQuicksort.partition() method into asynchronous AsyncQuicksort.asyncPartition(). 

Look @@ -301,7 +301,7 @@ viewer and an asynchronous data generator.
data access methods:

- @@ -355,7 +355,7 @@ following snippet from SyncDataViewer.getElements()  shows the use of Query:

-
@@ -407,7 +407,8 @@ an executor. 
java.util.concurrent.Future.get() method, implemented by Query, until the request monitor from the execute() method is completed. -
+
Image 1: Sequence diagram of Query use in -getElements().
+ style="font-weight: bold;">Image 1: Detailed sequence of calling +IDataGenerator.getCount() in SyncDataViewer.getElements().
-
-

-
-

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

Synchronization

Managing race conditions and deadlocks is one of the most challanging @@ -455,7 +450,7 @@ to the executor thread.  The following is an example of this from the DataGeneratorWithExecutor:

- @@ -503,7 +498,8 @@ left out.
- +
@@ -535,7 +531,7 @@ thread-based implementation this loop is implemented in the run method of the generator's thread:

-

Image 1: Synchronization using multiple + style="font-weight: bold;">Image 2: Synchronization using multiple locks on data.
Image 2: Synchronization using a single + style="font-weight: bold;">Image 3: Synchronization using a single DSF executor thread.
@@ -592,7 +588,7 @@ servicing the queue, which is called by every method that adds a new request to the queue:

-
@@ -780,17 +776,764 @@ module.

+

Timers Example

+The Timers example, found in the org.eclipse.dd.examples.dsf.timers +package, is used as a reference throughout the following +sections.  It is useful to get familiar with this example at this +time.
+

Timer example defines the following two services:
+

+
    +
  • TimerService - +This service manages a set of timers where each timer's value is +incremented every second.  The timer service contains the +following features:
    +
  • +
      +
    • startTimer() method - +Allows user to create a new timer.  It returns the Data Model +context for the new timer.
      +
    • +
    • killTimer() method - +Allows the user to delete the given timer.  It requires a timer +context.
      +
    • +
    • getTimers() method - +Returns the array of contexts for existing timers.
      +
    • +
    • getTimerValue() method +- Returns the current value for the given timer context.
      +
    • +
    • TimerTickEvent event +class - An event that is generated for every timer, every time its +value changes (once per second).  The event contains the timer's +context.
      +
    • +
    +
  • AlarmService - +This service manages a set of triggers and alarms.  Triggers can +be created and destroyed independently.  Alarms represent a timer +and a trigger combined.  The Alarm service has the following +features:
  • +
      +
    • createTrigger() method +- Creates a new trigger with a given value.  It returns a context +to the new trigger.
      +
    • +
    • deleteTrigger() method +- Deletes the trigger for the given context.
      +
    • +
    • setTriggerValue() +method - Sets the value of a trigger to the given value.
      +
    • +
    • getAlarm() method - +Gets the alarm for the specified timer and trigger contexts.  It +returns an alarm context object.
      +
    • +
    • AlarmTriggeredDMEvent +event class - An event that is generated for every timer that trips the +given trigger by surpassing its value.  The event contains an +alarm context.
      +
    • +
    +
+The Timers example also features a user interface for displaying and +manipulating the data in the example's services.  The principal +component of this UI is a view that can be opened by following the +menus: Window->Show View->Other, +then selecting DSF +Examples->Timers View in the selection dialog.  This +view contains a tree viewer which displays the timers, triggers, and +alarms in a hierarchy.  The alarms are only shown when triggered +and are automatically selected upon a triggered event.
+ + + + + + + + + +

+
Image 4: Screen shot of the Timers view.
+
+Other features of the Timers example UI include:
+
    +
  • New Timer action +- Adds a new timer, which immediately shows up in the view.
  • +
  • New Trigger action +- Opens a dialog where the user enters the value of the new +trigger.  Upon OK, the dialog creates a new trigger that is added +to the view.
  • +
  • Remove action - +Removes a timer or a trigger, whichever is currently selected in the +viewer.
    +
  • +
  • Toggle Layout action +- Switches the hierarchy in the tree to either Timers->Triggers->Alarm or Triggers->Timers->Alarm
  • +
  • Edit Trigger Value cell +editor - changes the value of the selected trigger.
    +
  • +

Services

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
+
    +
  • Registration - +Services need to register and unregister themselves with OSGi framework
  • +
      +
    • BundleContext.registerService() +- registers a service, it returns a ServiceRegistration object which +should be retained by the caller.
    • +
    • ServiceRegistration.unregister() +- unregisters a service.
    • +
    +
  • References - +Clients wishing to use a service, need to obtain a reference to the +service.  OSGi features reference counting for services.
  • +
      +
    • BundleContext.getServiceReference(), +BundleContext.getServiceReferences(), +BundleContext.getAllServiceReferences() - methods for retrieving a +reference to a service using a class name and/or a property filter.
    • +
    • BundleContext.ungetService() - Releases a service reference and +decrements its use count.
    • +
    +
  • Events - Clients +using services should listen to service events.  Events are issued +when services are added/removed/modified.
  • +
      +
    • org.osgi.framework.ServiceListener - interface for a service +listener.  Objects implementing this interface can be registered +with the BundleContext
    • +
    +
+
+ + + + + + +
Note: The service APIs all use the +BundleContext and they require the BundleContext to be active.  +This means DSF-based debugger integrations initialize after the plugin +is started, but  that they also shut down before the plugin is +stopped.  The first part is not difficult, but the second part +usually requires that the plugin's BundleActivator.stop() method shuts +down the debugger.
+
+

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 +instance and distinguish the services between the instances of +debuggers, DSF services are organized into sessions.
+

DSF Session features include:
+

+
    +
  • Unique Session ID +- This ID is used to distinguish services from different +sessions.  Clients may also obtain a session instance using an ID +through a static method.
    +
  • +
  • Session Lifecycle Events - Clients may register to listen when +sessions are started and ended.
    +
  • +
  • DSF Executor - +Eash 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 +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

+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 +goes back to the primary synchronization mechanism of DSF.  +Following this rule greatly simplifies the task of protecting the +integrity of service state information.
+
+ + + + + + +
Note: All service public methods +should be called using the session's DSF executor.
+

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, +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
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+
org.osgi.util.tracker.ServiceTracker
+
org.eclipse.dd.dsf.service.DsfServicesTracker
+
Services +tracked
+
Tracks all services with a given +class name or filter. 
+
Tracks all services within a +given DSF session. 
+
Thread +safetyThread safeRestricted to the session +executor thread.
Accessor +methods
+
+
    +
  • getService() - +return the first service instance matching the class/filter
  • +
  • getServices() - +returns all references matching the specified class/filter.
  • +
+
    +
+
+
    +
  • getService(Class) +- Returns the first service instance matching given class
  • +
  • getService(Class, String) +- Returns the first service instance matching given class and filter.
    +
  • +
+
Activation/Disposal +methods
+
+
    +
  • open() - Starts +tracking maching services.
  • +
  • close() - Shuts +down and un-gets all service references.
  • +
+
+
    +
  • <constructor> +- DSF services tracker can be used immediately after being constucted.
  • +
  • dispose() - +Disposes and un-gets all service references held by the tracker.
    +
  • +
+
+ + + + + + +
Note: All service trackers must be +disposed (or closed).  Failing to dispose a tracker results in a +service reference leak.
+
+

Initialization / Shutdown

+Every DSF service must +implement the IDsfService.initialize() and IDsfService.shutdown() +methods.  These methods can only be called in the session executor +thread  and are asynchronous.  As the last step in +initialization, a service should register itself.  Likewise as the +first step of shut-down a service should unregister itself.  Also +during initialization, each service should call +DsfSession.getAndIncrementServiceStartupCounter(), in order to obtain +the startup number of the service.  This number is used in +prioritizing the service events.
+

Starting up a large number of DSF services requires calling a number +of asynchronous method in a pre-defined sequence.  Implementing +this startup code can be cumbersome and DSF provides a quitility for +implementing it: org.eclipse.dd.dsf.concurrent.Sequence. 
+

+

Here's +an example of how the Sequence is extended to perform the task of +shutting down the services in the +Timers example:
+

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

+
+
 25: public class ServicesShutdownSequence extends Sequence {

27: // Session to that the services are running in.
28: final private DsfSession fSession;
29:
30: // DSF Services is created as the first step of the sequence. It
31: // cannot be created by the constructor because it can only be called
32: // in the session thread.
33: DsfServicesTracker fTracker;

35: public ServicesShutdownSequence(DsfSession session) {
36: super(session.getExecutor());
37: fSession = session;
38: }
39:
40: Step[] fSteps = new Step[] {
41: new Step() {
42: @Override
43: public void execute(RequestMonitor requestMonitor) {
44: fTracker = new DsfServicesTracker(DsfExamplesPlugin.getBundleContext(), fSession.getId());
45: requestMonitor.done();
46: }
47:
48: @Override
49: public void rollBack(RequestMonitor requestMonitor) {
50: // Dispose the tracker in case shutdown sequence is aborted
51: // and is rolled back.
52: fTracker.dispose();
53: fTracker = null;
54: requestMonitor.done();
55: }
56: },
57: new Step() {
58: @Override
59: public void execute(RequestMonitor requestMonitor) {
60: shutdownService(AlarmService.class, requestMonitor);
61: }
62: },
63: new Step() {
64: @Override
65: public void execute(RequestMonitor requestMonitor) {
66: shutdownService(TimerService.class, requestMonitor);
67: }
68: },
69: new Step() {
70: @Override
71: public void execute(RequestMonitor requestMonitor) {
72: // Dispose the tracker after the services are shut down.
73: fTracker.dispose();
74: fTracker = null;
75: requestMonitor.done();
76: }
77: }
78: };
79:
80: @Override
81: public Step[] getSteps() { return fSteps; }

83: // A convenience method that shuts down given service. Only service class
84: // is used to identify the service.
85: private <V extends IDsfService> void shutdownService(Class<V> clazz, RequestMonitor requestMonitor) {
86: IDsfService service = fTracker.getService(clazz);
87: if (service != null) {
88: service.shutdown(requestMonitor);
89: }
90: else {
91: requestMonitor.setStatus(new Status(
92: IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID,
93: IDsfService.INTERNAL_ERROR,
94: "Service '" + clazz.getName() + "' not found.", null));
95: requestMonitor.done();
96: }
97: }
99: }
+
+
+
    +
  • Line 40 initializes an array of Step objects which are invoked by +the Sequence logic.  Each Step class is an inner class with access +to
    +shared data in the ServicesShutdownSequence class.
  • +
  • Line 81 implements the protected method used by the Sequence +class to access the steps.
  • +
  • Line 85 encapsulates the repetitive logic of finding and shutting +down a given service.
  • +
  • Line 73 disposes the DsfServicesTracker used by the sequence.
  • +
+

+Below is the code snipped that invokes the ServicesShutdownSequence in +the Timers example:
+

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

+
+
181:             ServicesShutdownSequence shutdownSeq = 
182: new ServicesShutdownSequence(fSession);
183: fSession.getExecutor().execute(shutdownSeq);
184: try {
185: shutdownSeq.get();
186: } catch (InterruptedException e) { assert false;
187: } catch (ExecutionException e) { assert false;
188: }
+
+
+
    +
  • Line 183 submits the sequence to the session executor. 
    +
  • +
  • Line 185 calls the Future.get() method of the sequence to block +the calling thread until the sequence completes.
  • +
+ + + + + + +
Note: Sequence implements the +java.util.concurrent.Future interface just like the DSF Query +object.  However, if the sequence needs to be invoked from the +executor thread, the Future.get() method cannot be used (or a deadlock +would occur).  Instead the sequence should be constructed with a +custom request monitor to be invoked at the completion of the sequence.
+
+
+

Events

+DSF provides a somewhat unusual event mechanism, where event listeners +do not implement any particular listener interface.  Instead, +event listeners use the DsfServiceEventHandler +annotation to identify listener methods.  DSF finds the annotated +listener methods using reflection. 
+

To generate an event a service must:
+

+
    +
  1. Call DsfSession.dispatchEvent(Object +event, Dictionary<String, String> serviceProperties) +method.  The second parameter allows service listeners to filter +events using specific service properties.
  2. +
+In order to receive DSF events a client must:
+
    +
  1. Declare a public event +listener method (method name is not important), which takes an event parameter. The type of the +event parameter depends on the event, where the listener will receive +all service events which can be cast to the declared type.  A +second optional parameter of type Dictionary<String, +String> allows the event listener to examine the properties +of the service that is sending the event.
  2. +
  3. Add itself as a service event listener by calling DsfSession.addServiceEventListener().
  4. +
+ + + + + + +
Note: DsfSession.dispatchEvent() +calls event listeners in a separate Runnable submitted to the session +executor.  This is significant because the event listeners may +call other service methods changing the overall state of the +system.  It also implies that the event listeners are always +called in the session executor thread.
+
+
+ + + + + + +
Note: Service events are +prioritized.  Listeners which themselves are services are called +first, in the order that they were initialized.  All other +listenres are called after the services.
+

Data Model

-

View Model

-

Adapter, Provider, Node

-

Timers

+The term Data Model refers to +the natural structure of data that is being retrieved by the DSF +services.  One of the great challanges of creating an user +interface for a debugger is that the amount of of data that is +available on the target is much greater than what can be practially +presented to the user.  Therefere the debugger services need to +break up the data into chunks with appropriate granularity to achieve +maximum performance and usability.
+

IDMContext

+The IDMContext represents a handle to a chunk of data in the Data +Model.  This interface is a minimal, yet central feature of the +Data Model API.
+

What a Data Model context is:
+

+
    +
  • It is hierarchical.  +Contexts can have other contexts as parents.  The hierarchy of +contexts in a given system roughly defines that system's overall Data +Model.  More on context hierarchy
    +
  • +
  • It extends the org.eclipse.core.runtime.IAdaptable interface.  This +allows decorators, retargetable actions, etc. to be associated with a +context.
  • +
  • It is associated with a +single DSF session.  The IDMContext.getSessionID() returns +the session ID of the given context.  This allows all clients to +get a handle on the session and the executor needed to access the DSF +services that the context originated from.
    +
  • +
  • It is thread safe. +This allows context objects to be stored and compared in viewers, +caches, and other clients which may implement their own threading model.
    +
  • +
  • It is light-weight and +preferably immutable. This allows contexts to be stored by +clients that may persist beyond the life of the services that +originated them.  If a context holds references to a lot of data +or it may prevent that data from being garbage collected.
  • +
+What a Data Model context is NOT:
+
    +
  • It is NOT a reference +to a service.  Context should not return a reference to a +service directly because clients should use the appropriate OSGi APIs +to obtain references to DSF services. 
    +
  • +
  • It is NOT persistable.  +Since a context returns a context ID, it is valid only for the life of +a single DSF session. 
  • +
+
+ + + + + + +
Note: An IDMContext object can be +used to retrieve any +type of data object from the service.  Although there is an +IDMData marker interface defined, its presence it historical and its +use is optional.
+
+

Context Hierarchy

+One of the most powerful features of the IDMContext interface is that +is is hierarchical.  The IDMContext.getParents() +method returns the immediate ancestors of a given context and following +the parents' parents allows clients to traverse the full hierarchy of a +context. 
+

The use of the context hierarchy may be best explained with use of +the Timers example.  In the timers example there are three +contexts that are used:
+

+
    +
+
    +
  1. Timer - no +parent contexts
    +
  2. +
  3. Trigger - no +parent contexts
    +
  4. +
  5. Alarm - requires +both a timer and a trigger as parent contexts
  6. +
+
    +
+From these, only the third one has any parents (and any hierarchy), the +code snippet below shows how these parents are used in the AlarmService:

- + + + + + + + + + +
org.eclipse.dd.examples.dsf.timers.AlarmService.isAlarmTriggered()
+

+
+
209:     public boolean isAlarmTriggered(AlarmDMContext alarmCtx) {
210: // Extract the timer and trigger contexts. They should always be part
211: // of the alarm.
212: TimerService.TimerDMContext timerCtx = DMContexts.getAncestorOfType(
213: alarmCtx, TimerService.TimerDMContext.class);
214: TriggerDMContext triggerCtx = DMContexts.getAncestorOfType(
215: alarmCtx, TriggerDMContext.class);

217: assert triggerCtx != null && timerCtx != null;

219: // Find the trigger and check whether the timers value has surpassed it.
220: if (fTriggers.containsKey(triggerCtx)) {
221: int timerValue = getServicesTracker().getService(TimerService.class).
222: getTimerValue(timerCtx);
223:
224: return timerValue >= fTriggers.get(triggerCtx);
225: }
226:
227: return false;
228: }
+
+
+
    +
  • Lines 212 and 214 search the context hierarchy of the alarm +context for the timer and trigger contexts. 
    +
  • +
+ + + + + + +
Note: Methods that take a context +as an argument can specify the generic IDMContext as the argument type, +then search this context for a specific context type.  The benefit +of this technique is increased flexibility, at the cost of compile-time +type checking, and it is used throughout DSF to avoid dependencies +between service interfaces.
+
+

DMContexts

+Searching the context hierarchy can be tedious to implement, the +DMContexts utility class contains a few static methods to simplify this +task:
+
    +
  • getAncestorOfType() +- Searches for a context of a specific type in the hierarchy of the +given context.
  • +
  • isAncestorOf() - +Checks whether the one of the argument contexts is in the hierarchy of +the other.
  • +
  • toList() - +Converts all the contexts in a hierarchy of the give context into a +list.
  • +
+

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

+View Model builds on the flexible +hierarchy API introduced by Debug +Platform team in release 3.2.  The flexible hierarchy API has a +few distinguishing features:
+
    +
  1. There are provider interfaces for every aspect of data +presentation in the viewer (content, label, columns, etc.). 
    +
  2. +
  3. The provider interfaces are retrieved by the viewer for each element in the +viewer.  This allows the +view content to be populated from multiple sources.
  4. +
  5. Provider interfaces are asynchronous. 
    +
  6. +
+ + + + + + +
Note: Flexible Hierarchy is still +a provisional API in Eclipse Platform 3.4.  This virtually +guarantees that DSF will break backward API compatibility in future +releases.  However, these APIs have now been widely used by open +source projects such as DD and CDT and also by many commercial Eclipse +integrations, so the API changes are likely to be small and mostly +related to packaging.
+
+

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 +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 +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

+
+
+
+
+
+ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/request_monitor_1.dia b/plugins/org.eclipse.dd.doc.dsf/docs/common/request_monitor_1.dia new file mode 100644 index 00000000000..c2bd2b8b805 Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/request_monitor_1.dia differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/request_monitor_1.png b/plugins/org.eclipse.dd.doc.dsf/docs/common/request_monitor_1.png new file mode 100644 index 00000000000..f9fec21cd3e Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/request_monitor_1.png differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/timers_1.png b/plugins/org.eclipse.dd.doc.dsf/docs/common/timers_1.png new file mode 100644 index 00000000000..172f5e1f279 Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/timers_1.png differ