1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

[220446][219907] Updated the doc plugin to point to the new DSF tutorials.

This commit is contained in:
Pawel Piech 2008-03-24 16:34:37 +00:00
parent 0b3ad17747
commit 7158eeed07
8 changed files with 9 additions and 1223 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

View file

@ -1,432 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title>DSF Concurrency Model</title>
</head>
<body>
<h2>DSF Concurrency Model</h2>
<h3>
</h3>
<p class="MsoNormal" style="line-height: normal;"><b><span
style="font-size: 12pt; font-family: &quot;Times New Roman&quot;;">Version
1.0<br>
Pawel Piech<br>
&copy; 2006, Wind River Systems.<span style="">&nbsp; </span>Release
under EPL version 1.0.</span></b><b><span
style="font-size: 18pt; font-family: &quot;Times New Roman&quot;;"><o:p></o:p></span></b></p>
<h3>Introduction</h3>
Providing a solution to concurrency problems is the primary design goal
of DSF.&nbsp; To that end DSF imposes a rather draconian
restriction on services that use it: <span style="font-weight: bold;">1)
All service interface methods must be called using a single designated
dispatch thread, unless explicitly stated otherwise, 2) The dispatch
thread should never be used to make a blocking call (a call that waits
on I/O or a call that makes a long-running computation).&nbsp; </span>What
the first restriction effectively means, is that the dispatch thread
becomes a global "lock" that all DSF services in a given session
share with each other, and which controls access to most of services'
shared data.&nbsp; It's important to note that <span
style="font-weight: bold;">multi-threading is still allowed</span>
within individual service implementation. but when crossing the service
interface boundaries, only the dispatch thread can be used.&nbsp; The
second restriction just ensures that the performance of the whole
system is not killed by one service that needs to read a huge file over
the network.&nbsp; Another way of looking at it is that the
service implementations practice co-operative multi-threading using the
single dispatch thread.<br>
<br>
There are a couple of obvious side effects that result from this rule:<br>
<ol>
<li>When executing within the dispatch thread, the state of the
services is guaranteed not to change.&nbsp; This means that
thread-defensive programming techniques, such as making duplicates of
lists before iterating over them, are not necessary.&nbsp; Also it's
possible to implement much more complicated logic which polls the state
of many objects, without the worry about dead-locks.</li>
<li>Whenever a blocking operation needs to be performed, it must be
done using an asynchronous method.&nbsp; By the time the operation is
completed, and the caller regains the dispatch thread, this caller may
need to retest the relevant state of the system, because it could
change completely while the asynchronous operation was executing.</li>
</ol>
<h3>The Mechanics</h3>
<h4><span style="font-family: monospace;">java.util.concurrent.ExecutorService</span><br>
</h4>
DSF builds on the vast array of tools added in Java 5.0's
java.util.concurrent package (see <a
href="http://java.sun.com/j2se/1.5.0/docs/guide/concurrency/index.html">http://java.sun.com/j2se/1.5.0/docs/guide/concurrency/index.html</a>
for details), where the most important is the <a
style="font-family: monospace;"
href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ExecutorService.html">ExecutorService</a>
interface.&nbsp; <span style="font-family: monospace;">ExecutorService
</span>is a formal interface for submitting <span
style="font-family: monospace;">Runnable</span> objects that will be
executed according to executor's rules, which could be to execute the
<span style="font-family: monospace;">Runnable </span>immediately,
within a thread pool, using a display thread,
etc.&nbsp; For DSF, the main rule for executors is that they have
to use a single thread to execute the runnable and that the runnables
be executed in the order that they were submitted.&nbsp; To give the
DSF clients and services a method for checking whether they are
being called on the dispatch thread, we extended the <span
style="font-family: monospace;">ExecutorService
</span>interface as such:<br>
<pre>public interface DsfExecutor extends ScheduledExecutorService<br>{<br> /**<br> * Checks if the thread that this method is called in is the same as the<br> * executor's dispatch thread.<br> * @return true if in DSF executor's dispatch thread<br> */<br> public boolean isInExecutorThread();<br>}<br></pre>
<h4><a
href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/Future.html"><span
style="font-family: monospace;">java.lang.concurrent.Future</span></a>
vs <a
href="http://dsdp.eclipse.org/help/latest/topic/org.eclipse.dd.dsf.doc/reference/api/org/eclipse/dd/dsf/concurrent/Done.html"><span
style="font-family: monospace;">org.eclipse.dd.dsf.concurrent.Done</span></a></h4>
The <span style="font-family: monospace;">Done </span>object
encapsulates the return value of an asynchronous call in DSF.&nbsp; It
is actually merely a <span style="font-family: monospace;">Runnable </span>with
an attached <span style="font-family: monospace;">org.eclipse.core.runtime.IStatus</span>
object , but it can be extended by the services or clients to hold
whatever additional data is needed.&nbsp;&nbsp; Typical pattern in how
the <span style="font-family: monospace;">Done </span>object is used,
is as follows:<br>
<pre>Service:<br> public class Service {<br> void asyncMethod(Done done) {<br> new Job() {<br> public void run() {<br> // perform calculation <br> ... <br> done.setStatus(new Status(IStatus.ERROR, ...));<br> fExecutor.execute(done);<br> }<br> }.schedule();<br> }<br> }<br><br>Client:<br> ...<br> Service service = new Service();<br> final String clientData = "xyz";<br> ...<br> service.asynMethod(new Done() {<br> public void run() {<br> if (getStatus().isOK()) {<br> // Handle return data<br> ...<br> } else {<br> // Handle error<br> ...<br> }<br> }<br> }<br></pre>
The service performs the asynchronous operation a background thread,
but
it can still submit the <span style="font-family: monospace;">Done </span>runnable
with the executor.&nbsp; In other words, the <span
style="font-family: monospace;">Done</span> and other runnables can be
submitted from any thread, but will always execute in the single
dispatch thread.&nbsp; Also if the implementation of the <span
style="font-family: monospace;">asyncMethod()</span> is non-blocking,
it does not need to start a job, it could just perform the operation in
the dispatch thread.&nbsp; On the client side, care has to be taken to
save appropriate state before the asynchronous method is called,
because by the time the <span style="font-family: monospace;">Done </span>is
executed, the client state may change.<br>
<br>
The <span style="font-family: monospace;">java.lang.concurrent</span>
package
doesn't already have a <span style="font-family: monospace;">Done</span>,
because the generic concurrent
package is geared more towards large thread pools, where clients submit
tasks to be run in a style similar to Eclipse's Jobs, rather than using
the single dispatch thread model of DSF.&nbsp; To this end<span
style="font-family: monospace;">,</span> the
concurrent package does have an equivalent object, <a
style="font-family: monospace;"
href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/Future.html">Future</a>.&nbsp;
<span style="font-family: monospace;">Future </span>has methods that
allows the client to call the <span style="font-family: monospace;">get()</span>
method, and block while waiting for a result, and for this reason it
cannot
be used from the dispatch thread.&nbsp; But it can be used, in a
limited way, by clients which are running on background thread that
still
need to retrieve data from <span style="text-decoration: underline;">synchronous</span>
DSF methods.&nbsp; In this case the code might look like the
following:<br>
<pre>Service:<br> public class Service {<br> int syncMethod() {<br> // perform calculation<br> ...<br> return result;<br> }<br> }<br><br>Client:<br> ...<br> DsfExecutor executor = new DsfExecutor();<br> final Service service = new Service(executor);<br> Future&lt;Integer&gt; future = executor.submit(new Callable&lt;Integer&gt;() {<br> Integer call() {<br> return service.syncMethod();<br> }<br> });<br> int result = future.get();<br></pre>
The biggest drawback to using <span style="font-family: monospace;">Future
</span>with DSF services, is that it does not work with
asynchronous methods.&nbsp; This is because the <a
href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/Callable.html#call%28%29"><span
style="font-family: monospace;">Callable.call()</span></a>
implementation
has to return a value within a single dispatch cycle.&nbsp; To get
around this, DSF has an additional object called <span
style="font-family: monospace;">DsfQuery</span>, which works like a <span
style="font-family: monospace;">Future </span>combined with a <span
style="font-family: monospace;">Callable</span>, but allows the
implementation to make multiple dispatches before setting the return
value to the client.&nbsp; The <span style="font-family: monospace;">DsfQuery<span
style="font-family: monospace;"> object works as follows:<br>
</span></span>
<ol>
<li>Client creates the query object with its own implementation of <span
style="font-family: monospace;">DsfQuery.execute()</span>.<br>
</li>
<li>Client calls the <span style="font-family: monospace;">DsfQuery.get()</span>
method on non-dispatch thread, and blocks.</li>
<li>The query is queued with the executor, and eventually the <span
style="font-family: monospace;">DsfQuery.execute()</span> method is
called on the dispatch thread.</li>
<li>The query <span style="font-family: monospace;">DsfQuery.execute()</span>
calls synchronous and asynchronous methods that are needed to do its
job.</li>
<li>The query code calls <span style="font-family: monospace;">DsfQuery.done()</span>
method with the result.</li>
<li>The <span style="font-family: monospace;">DsfQuery.get()</span>
method un-blocks and returns the result to the client.<br>
</li>
</ol>
<h3><a
href="http://dsdp.eclipse.org/help/latest/topic/org.eclipse.dd.dsf.doc/reference/api/org/eclipse/dd/dsf/examples/concurrent/package-summary.html">Slow
Data Provider Example</a></h3>
The point of DSF concurrency can be most easily explained through
a practical example.&nbsp; Suppose there is a viewer which needs to
show data that originates from a remote "provider".&nbsp; There is a
considerable delay in transmitting the data to and from the provider,
and some delay in processing the data.&nbsp; 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.&nbsp; The diagram below illustrates
the
logical relationship between components:<br>
<br>
.<img alt="" title="Slow Data Provider Diagram"
src="dsf_concurrency_model-1.png" style="width: 636px; height: 128px;"><br>
<p>In detail, these components look like this:<span
style="text-decoration: underline;"></span></p>
<p><span style="text-decoration: underline;"></span></p>
<span style="text-decoration: underline;">Table Viewer</span><br>
<p>The table viewer is the standard
<span style="font-family: monospace;">org.eclipse.jface.viewers.TableViewer</span>,
created with <span style="font-family: monospace;">SWT.VIRTUAL</span>
flag.&nbsp; It has an associated content
provider, SlowDataProviderContentProvider) which handles all the
interactions with the data provider.&nbsp; The lazy content provider
operates in a very simple cycle:</p>
<ol>
<li>Table viewer tells content provider that the input has changed by
calling <span style="font-family: monospace;">IContentProvider.inputChanged()</span>.&nbsp;
This means that the content provider has to query initial state of the
data.</li>
<li>Next the content provider tells the viewer how many elements
there are, by calling <span style="font-family: monospace;">TableViewer.setItemCount()</span>.</li>
<li>At this point, the table resizes, and it requests data values for
items that are visible.&nbsp; So for each visible item it calls: <span
style="font-family: monospace;">ILazyContentProvider.updateElement()</span>.</li>
<li>After calculating the value, the content provider tells the table
what the value is, by calling <span style="font-family: monospace;">TableViewer.replace().</span></li>
<li>If the data ever changes, the content provider tells the table to
rerequest the data, by calling <span style="font-family: monospace;">TableViewer.clear()</span>.</li>
</ol>
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:<br>
<pre> public void updateElement(final int index) {<br> assert fTableViewer != null;<br> if (fDataProvider == null) return;<br><br> fDataProvider.getExecutor().execute(<br> new Runnable() { public void run() {<br> // Must check again, in case disposed while redispatching.<br> if (fDataProvider == null) return;<br> <br> queryItemData(index);<br> }});<br> }<br></pre>
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.<br>
<pre> public void dataChanged(final Set&lt;Integer&gt; indexes) {<br> // Check for dispose.<br> if (fDataProvider == null) return;<br><br> // Clear changed items in table viewer.<br> if (fTableViewer != null) {<br> final TableViewer tableViewer = fTableViewer;<br> tableViewer.getTable().getDisplay().asyncExec(<br> new Runnable() { public void run() {<br> // Check again if table wasn't disposed when <br> // switching to the display thread.<br> if (tableViewer.getTable().isDisposed()) return; // disposed<br> for (Integer index : indexes) {<br> tableViewer.clear(index);<br> }<br> }});<br> }<br> }<br></pre>
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.<br>
<p><span style="text-decoration: underline;">Data Provider Service</span></p>
<p>The data provider service interface, <span
style="font-family: monospace;">DataProvider</span>, is very similar
to that of the lazy content provider.&nbsp; It has methods to: </p>
<ul>
<li>get item count</li>
<li>get a value for given item</li>
<li>register as listener for changes in data count and data values</li>
</ul>
But this is a DSF interface, and all methods must be called on the
service's dispatch thread.&nbsp; For this reason, the <span
style="font-family: monospace;">DataProvider </span>interface returns
an instance of <span style="font-family: monospace;">DsfExecutor</span>,
which must be used with the interface.<br>
<p><span style="text-decoration: underline;">Slow Data Provider</span></p>
<p>The data provider is actually implemented as a thread which is an
inner class of <span style="font-family: monospace;">SlowDataProvider</span>
service.&nbsp; The provider thread
communicates with the service by reading Request objects from a shared
queue, and by posting Runnable objects directly to the <span
style="font-family: monospace;">DsfExecutor</span> but
with a simulated transmission delay.&nbsp; Separately, an additional
flag is also used to control the shutdown of the provider thread.</p>
To simulate a real back end, the data provider randomly invalidates a
set of items and notifies the listeners to update themselves.&nbsp; It
also periodically invalidates the whole table and forces the clients to
requery all items.<br>
<h4>Data and Control Flow<br>
</h4>
This can be described in following steps:<br>
<ol>
<li>The table viewer requests data for an item at a given index (<span
style="font-family: monospace;">SlowDataProviderContentProvider.updateElement</span>).<br>
</li>
<li>The table viewer's content provider executes a <span
style="font-family: monospace;">Runnable </span>in the DSF
dispatch thread and calls the data provider interface (<span
style="font-family: monospace;">SlowDataProviderContentProvider.queryItemData</span>).</li>
<li>Data provider service creates a Request object, and files it in a
queue (<span style="font-family: monospace;">SlowDataProvider.getItem</span>).</li>
<li>Data provider thread de-queues the Request object and acts on it,
calculating the value (<span style="font-family: monospace;">ProviderThread.processItemRequest</span>).</li>
<li>Data provider thread schedules the calculation result to be
posted with DSF executor (<span style="font-family: monospace;">SlowDataProvider.java:185</span>).</li>
<li>The Done callback sets the result data in the table viewer (<span
style="font-family: monospace;">SlowDataProviderContentProvider.java:167</span>).<br>
</li>
</ol>
<h4>Running the example and full sources</h4>
This example is implemented in the <span
style="font-family: monospace;">org.eclipse.dd.dsf.examples</span>
plugin, in the <span style="font-family: monospace;">org.eclipse.dd.dsf.examples.concurrent</span>
package.&nbsp; <br>
<br>
To run the example:<br>
<ol>
<li>Build the test plugin (along with the <span
style="font-family: monospace;">org.eclipse.dsdp.DSF plugin</span>)
and launch the PDE.&nbsp; <br>
</li>
<li>Make sure to add the <span style="font-style: italic;">DSF
Tests</span> action set to your current perspective.</li>
<li>From the main menu, select <span style="font-style: italic;">DSF
Tests -&gt; Slow Data Provider</span>.</li>
<li>A dialog will open and after a delay it will populate with data.</li>
<li>Scroll and resize dialog and observe the update behavior.</li>
</ol>
<h4>Initial Notes<br>
</h4>
This example is supposed to be representative of a typical embedded
debugger design problem.&nbsp; 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.&nbsp; But as such, this basic example presents a couple
of major usability problems<br>
<ol>
<li>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.&nbsp; The result of this is visible to the user as
lines of data are filled in one-by-one in the table.&nbsp; 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.</li>
<li>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.&nbsp; 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.&nbsp; <br>
</li>
</ol>
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. <br>
<h3>Coalescing</h3>
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.&nbsp; There are
two basic patterns in which coalescing is achieved:<br>
<ol>
<li>The back end provides an interface for retrieving data in large
chunks.&nbsp; 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.</li>
<li>The back end providers an interface for retrieving data in
variable size chunks.&nbsp; When the service implementation receives a
request for a single item, it buffers the request, and waits for other
requests to come in.&nbsp; After a delay, the service clears the buffer
and submits a request for the combined items to the data provider.</li>
</ol>
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" (<span
style="font-family: monospace;">InputCoalescingSlowDataProvider.java</span>).&nbsp;
<br>
<p><span style="text-decoration: underline;">Input Buffer</span></p>
<p>The main feature of this pattern is a buffer for holding the
requests before sending them to the data provider.&nbsp; In this
example the user requests are buffered in two arrays: <span
style="font-family: monospace;">fGetItemIndexesBuffer</span> and <span
style="font-family: monospace;">fGetItemDonesBuffer</span>.&nbsp; The
<span style="font-family: monospace;">DataProvider.getItem()</span>
implementation is changed as follows:</p>
<pre> public void getItem(final int index, final GetDataDone&lt;String&gt; done) {<br> // Schedule a buffer-servicing call, if one is needed.<br> if (fGetItemIndexesBuffer.isEmpty()) {<br> fExecutor.schedule(<br> new Runnable() { public void run() {<br> fileBufferedRequests();<br> }},<br> COALESCING_DELAY_TIME, <br> TimeUnit.MILLISECONDS);<br> }<br> <br> // Add the call data to the buffer. <br> // Note: it doesn't matter that the items were added to the buffer <br> // after the buffer-servicing request was scheduled. This is because<br> // the buffers are guaranteed not to be modified until this dispatch<br> // cycle is over.<br> fGetItemIndexesBuffer.add(index);<br> fGetItemDonesBuffer.add(done);<br> } <br><br></pre>
And method that services the buffer looks like this:<br>
<pre> public void fileBufferedRequests() { <br> // Remove a number of getItem() calls from the buffer, and combine them<br> // into a request.<br> int numToCoalesce = Math.min(fGetItemIndexesBuffer.size(), COALESCING_COUNT_LIMIT);<br> final ItemRequest request = new ItemRequest(new Integer[numToCoalesce], new GetDataDone[numToCoalesce]); <br> for (int i = 0; i &lt; numToCoalesce; i++) {<br> request.fIndexes[i] = fGetItemIndexesBuffer.remove(0);<br> request.fDones[i] = fGetItemDonesBuffer.remove(0);<br> }<br><br> // Queue the coalesced request, with the appropriate transmission delay.<br> fQueue.add(request);<br> <br> // If there are still calls left in the buffer, execute another <br> // buffer-servicing call, but without any delay.<br> if (!fGetItemIndexesBuffer.isEmpty()) {<br> fExecutor.execute(new Runnable() { public void run() {<br> fileBufferedRequests();<br> }});<br> }<br> }<br></pre>
The most interesting feature of this implementation is the fact that
there are no semaphores anywhere to control access to the input
buffers.&nbsp; Even though the buffers are serviced with a delay and
multiple clients can call the <span style="font-family: monospace;">getItem()</span>
method, the use of a single
dispatch thread prevents any race conditions that could corrupt the
buffer data.&nbsp; 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.<br>
<h3>Cancellability</h3>
<p><span style="text-decoration: underline;">Table Viewer</span></p>
<p><span style="text-decoration: underline;"></span></p>
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.&nbsp; For the table viewer
content provider, this means that additional features have to be
added.&nbsp; In <span style="font-family: monospace;">CancellingSlowDataProviderContentProvider.java</span>
<span style="font-family: monospace;">ILazyContentProvider.updateElement()</span>
was changes as follows:<br>
<pre> public void updateElement(final int index) {<br> assert fTableViewer != null;<br> if (fDataProvider == null) return;<br> <br> // Calculate the visible index range.<br> final int topIdx = fTableViewer.getTable().getTopIndex();<br> final int botIdx = topIdx + getVisibleItemCount(topIdx);<br> <br> fCancelCallsPending.incrementAndGet();<br> fDataProvider.getExecutor().execute(<br> new Runnable() { public void run() {<br> // Must check again, in case disposed while redispatching.<br> if (fDataProvider == null || fTableViewer.getTable().isDisposed()) return;<br> if (index &gt;= topIdx &amp;&amp; index &lt;= botIdx) {<br> queryItemData(index);<br> }<br> cancelStaleRequests(topIdx, botIdx);<br> }});<br> }<br></pre>
Now the client keeps track of the requests it made to the service in <span
style="font-family: monospace;">fItemDataDones</span>, and above, <span
style="font-family: monospace;">cancelStaleRequests()</span> iterates
through all the outstanding requests and cancels the ones that are no
longer in the visible range.<br>
<p><span style="text-decoration: underline;">Data Provider Service<span
style="text-decoration: underline;"></span></span></p>
<p><span style="text-decoration: underline;"><span
style="text-decoration: underline;"></span></span></p>
<p>The data provider implementation
(<span style="font-family: monospace;">CancellableInputCoalescingSlowDataProvider.java</span>),
builds on top of the
coalescing data provider.&nbsp; To make the canceling feature useful,
the data provider service has to limit the size of the request
queue.&nbsp; 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.&nbsp; So instead, if a flood
of <span style="font-family: monospace;">getItem()</span>
calls comes in, the service has to hold most of them in the coalescing
buffer in case the client decides to cancel them.&nbsp; Therefore the
<span style="font-family: monospace;">fileBufferedRequests()</span>
method includes a simple check before servicing
the buffer, and if the request queue is full, the buffer servicing call
is delayed.</p>
<pre> if (fQueue.size() &gt;= REQUEST_QUEUE_SIZE_LIMIT) {<br> if (fGetItemIndexesBuffer.isEmpty()) {<br> fExecutor.schedule(<br> new Runnable() { public void run() {<br> fileBufferedRequests();<br> }},<br> REQUEST_BUFFER_FULL_RETRY_DELAY, <br> TimeUnit.MILLISECONDS);<br> }<br> return;<br> } <br></pre>
Beyond this change, the only other significant change is that before
the requests are queued, they are checked for cancellation.<br>
<h3>Final Notes<br>
</h3>
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.&nbsp; 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.&nbsp; 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.<br>
<p>Coalescing and Cancellability are both optimizations.&nbsp; Neither
of these optimizations affected the original interface of the service,
and one of them only needed a service-side modification.&nbsp; 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.&nbsp; </p>
<p>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.&nbsp; 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.&nbsp; The perfect example of this is a Variables
service, which is responsible for calculating the value of expressions
shown in the Variables view.&nbsp; 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.&nbsp; But as
soon as a resumed event is issued by Run Control, the Variables service
needs to cancel&nbsp; the pending evaluation requests.<br>
</p>
<br>
<br>
</body>
</html>

View file

@ -1,286 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title>DSF Data Model</title>
</head>
<body>
<h2>DSF Data Model</h2>
<b><span style="font-size: 12pt; font-family: &quot;Times New Roman&quot;;">Version
1.0<br>
Pawel Piech<br>
&copy; 2006, Wind River Systems.<span style="">&nbsp; </span>Release
under EPL version 1.0.</span></b>
<h3>Overview</h3>
<p>The data model aspect of DSF is only partially complete as compared
to the Concurrency and Services Models.&nbsp; The goals for its design
are:<br>
</p>
<ol>
<li><span style="font-style: italic;">Separate the structure of the
data in the services from the model used for presentation in views.</span>&nbsp;
This seems like a basic model-viewer separation, which is something
that we theoretically have already.&nbsp; But in reality the current
platform debug model APIs closely correspond to how the data is
laid out in debug views, and even with the flexible hierarchy views it
is
difficult to provide alternative layouts.</li>
<li><span style="font-style: italic;">Allow for a modular
implementation of services that contribute to the data model.</span>&nbsp;&nbsp;
<br>
</li>
<li><span style="font-style: italic;">Perform well with large
data sets.</span></li>
<li><span style="font-style: italic;">Make the data model interfaces
convenient to use by other services as well as by views.</span>&nbsp;
Some interim designs of DSF data model APIs were very well suited for
populating views (though asynchronous) content and label provider, but
were very difficult to use for other purposes, such as by another
service, or a client that creates a dialog.&nbsp; This led to services
implementing two sets of interfaces for the same data, which was more
expensive to develop and maintain.<br>
</li>
<li><span style="font-style: italic;">Allow for easy changes to the
layout of data in views.</span>&nbsp; This is from the point of view of
a debugger implementer that would like to modify the standard layout of
debugger data. <br>
</li>
<li><span style="font-style: italic;">Allow the users to modify the
layout of data in views.</span>&nbsp; And this is a logical extension
of the previous goal.</li>
</ol>
<p> </p>
That's a pretty ambitious set of goals to keep in mind, which partly
explains why the design is not fully complete yet.&nbsp; In particular,
the last goal doesn't have any implementation at this point.&nbsp; But
other than that the, we believe that our current design mostly
meets the other goals.&nbsp; It remains to be seen how well it will
hold up
beyond a prototype implementation.<br>
<p>The DSF data model is divided into two parts: a non-UI part that
helps services expose data in a consistent form, and a UI part that
helps viewers present the data.&nbsp; They are described separately in
the two sections below.<br>
</p>
<h3>Timers Example</h3>
<p>A <a
href="http://dsdp.eclipse.org/help/latest/topic/org.eclipse.dd.dsf.doc/reference/api/org/eclipse/dd/dsf/examples/timers/package-summary.html">"timers
example"</a> is included with the DSF plugins which
demonstrates the use of data model and view model
APIs.&nbsp;&nbsp; It is probably much easier to digest this document
when referring to this example for usage.<br>
</p>
<h3>Data Model API (<a
href="http://dsdp.eclipse.org/help/latest/topic/org.eclipse.dd.dsf.doc/reference/api/org/eclipse/dd/dsf/model/package-summary.html"><span
style="font-family: monospace;">org.eclipse.dd.dsf.model</span></a>)<br>
</h3>
As stated before, the aim of this API is to allow services to provide
data with just enough common information, so that it can be easily
presented in the view, but with a simple enough design, so that the
data can be accessed by non-viewer clients.&nbsp; The type of data in
services can vary greatly from service to service, some data for
example:<br>
<ul>
<li>service data might be extremely large and thus may only be
retrieved from a back end process in small chunks, while some service
data might be always stored locally in the service<br>
</li>
<li>data might take a very long time to retrieve, or it could be
instantaneous<br>
</li>
<li>some services might support canceling of the request while it is
being processed, while other services might not<br>
</li>
<li>some data may change very frequently, other data may not change
at all<br>
</li>
</ul>
The data model API tries to find a common denominator for these
divergent properties and imposes the following restrictions:<br>
<ol>
<li>Each "chunk" of data that comes from a service has a
corresponding <span style="font-family: monospace;">IDataModelContext </span>(Data Model Context)
object.<br>
</li>
<li>The DM-Context objects are to be generated by the data model services (<span
style="font-family: monospace;">IDataModelService</span>) with either
synchronous or asynchronous methods, and taking whatever arguments are
needed.&nbsp; Put differently, how DM-Contexts are created is up to the
service.</li>
<li>The service will provide a method for retrieving each "chunk" of
model data (<span style="font-family: monospace;">IDataModelData</span>)
using a method that requires no other arguments besides the DM-Contexts.</li>
</ol>
<h4>DM-Context (<span style="font-family: monospace;">IMContext</span>)<br>
</h4>
The DM-Contexts are the most
important part of this design, so they warrant a closer look.&nbsp; The
interface is listed below:<br>
<pre> public interface IDataModelContext&lt;V extends IDataModelData&gt; extends IAdaptable {<br> public String getSessionId();<br> public String getServiceFilter();<br> public IDataModelContext[] getParents();<br> }<br></pre>
First of all the object extends <span style="font-family: monospace;">IAdaptable</span>,
which allows clients to use these objects as handles that are stored
with UI components.&nbsp; However the implementation of <span
style="font-family: monospace;">IDataModelData.getAdapter()</span>&nbsp;
presents a particular challenge.&nbsp; If the standard platform method
of retrieving an adapter is used (<span style="font-family: monospace;">PlatformObject.getAdapter()</span>),
then there can only be one adapter registered for a given DM-Context class,
which has to be shared by all the DSF sessions that are running
concurrently.&nbsp; Thus one debugger that implements a <span
style="font-family: monospace;">IStack.IFrameDMContext</span>, would have to
have the same instance of<span style="font-family: monospace;">
IAsynchronousLabelAdapter</span> as another debugger implementation
that is running at the same time.&nbsp; To overcome this problem, DSF
provides a method for registering adapters with a session using <span
style="font-family: monospace;">DsfSession.registerModelAdapter()</span>,
instead of with the platform (<span style="font-family: monospace;">Platform.getAdapterManager().registerAdapters()</span>).&nbsp;
<br>
<p>The <span style="font-family: monospace; font-weight: bold;">getSessionId()</span>
method serves two purposes.&nbsp; First, it allows the
<span style="font-family: monospace;">IAdapter.getAdapter()</span>
implementation to work as described above. Second, it allows clients to
access the correct dispatch thread (<span
style="font-family: monospace;">DsfSession.getSession(id).getExecutor()</span>)
for calling the service that the DM-Context originated from.&nbsp; <br>
</p>
<p>The <span style="font-family: monospace; font-weight: bold;">getServiceFilter()</span>
method is actually included to allow future development.&nbsp; It is
intended to allow the client to precisely identify the service that
the DM-Context originated from, without having to examine the exact class type
of the DM-Context.&nbsp; But this functionality will not really be needed
until we start writing generic/data-driven clients.<br>
</p>
<p>The <span style="font-family: monospace; font-weight: bold;">getParents()</span>
method allows the DM-Context to be connected together into something that can
be considered a "model".&nbsp; Of course, most debugger data objects,
require the context of other objects in order to make sense: stack
frame is meaningless without the thread, debug symbols belong to a
module, which belongs to a process, etc.&nbsp; In other words, there is
some natural hierarchy to the data in debug services which needs to be
accessible through the data model APIs.&nbsp; This hierarchy may be the
same hierarchy that is to be shown in some debug views, but it doesn't
have to be.&nbsp; More importantly, this hierarchy should allow for a
clean separation of debug services, and for a clear dependency graph
between these services. </p>
<h3>View Model API (<a
href="http://dsdp.eclipse.org/help/latest/topic/org.eclipse.dd.dsf.doc/reference/api/org/eclipse/dd/dsf/ui/model/package-summary.html"><span
style="font-family: monospace;">org.eclipse.dd.dsf.ui.model</span></a>)<br>
</h3>
This is the component which allows the DSF data model to be presented
in
the views with different/configurable layouts.&nbsp; It is tightly
integrated with the recently added (and still provisional)
flexible-hierarchy viewers in the <span style="font-family: monospace;">org.eclipse.debug.ui</span>
plugin (see EclipseCon 2006 <a
href="http://www.eclipsecon.org/2006/Sub.do?id=31">presentation</a>
for more details).&nbsp; Actually, the platform flexible hierarchy
framework already provides all the adapter interfaces needed to present
the DSF data model in the viewers, and it is possible to do
that.&nbsp; However the flexible hierarchy views were not specifically
designed for DSF, and there are a few ugly patterns that emerge when
using them with DSF data model interfaces directly:<br>
<ul>
<li>Because of the nature of IAdaptable pattern, the flexible
hierarchy label and content adapters have to have a single instance
that works for all views that the objects appear in.&nbsp; This leads
to a lot of if-else statements, which make the implementation difficult
to follow.<br>
</li>
<li>There is a single adapter for all DSF data model elements in the
tree (from the same session), so the adapters have even more if-else
statements to handle the different elements in the viewer.</li>
<li>Most of DSF adapter work needs to be performed in the dispatch
thread, so each handler starts with a re-dispatch call.</li>
<li>In all of this, the logic which determines the hierarchy of
elements in the viewer is very hard to follow.</li>
</ul>
The view model API tries to address these issues in the following way:<br>
<ol>
<li>It divides the adapter work for different views in separate <span
style="font-family: monospace;">ViewModelProvider</span> objects.</li>
<li>It defines the view layout in an object-oriented manner using the
<span style="font-family: monospace;">IViewModelLayoutNode</span>
objects.</li>
<li>It consolidates the logic of switching to dispatch thread in one
place, and allows the <span style="font-family: monospace;">ViewModelProvider</span>
objects to work only in dispatch thread.<br>
</li>
</ol>
<h4><span style="font-family: monospace;">IViewModelLayoutNode</span></h4>
The core of the logic in this design lies in the implementation of the <span
style="font-family: monospace;">IViewModelLayoutNode</span> objects.
This interface is listed below:<br>
<pre>public interface IViewModelLayoutNode {<br> public IViewModelLayoutNode[] getChildNodes();<br> public void hasElements(IViewModelContext parentVmc, GetDataDone&lt;Boolean&gt; done);<br> public void getElements(final IViewModelContext parentVmc, GetDataDone&lt;IViewModelContext[]&gt; done);<br> public void retrieveLabel(IViewModelContext vmc, final ILabelRequestMonitor result);<br> public boolean hasDeltaFlags(IDataModelEvent e);<br> public void buildDelta(IDataModelEvent e, ViewModelDelta parent, Done done);<br> public void sessionDispose();<br>}<br></pre>
The <span style="font-family: monospace; font-weight: bold;">getChildNodes()</span>
method allows these layout nodes to be combined into a tree structure,
which mimics the layout of elements in the view.&nbsp; What the
children are depends on the implementation, some may be configurable
and
some may be fixed.<br>
<br>
The <span style="font-family: monospace; font-weight: bold;">hasElements()</span>
and <span style="font-family: monospace; font-weight: bold;">getElements()</span>
methods generate the actual elements that will appear in the
view.&nbsp; The methods are analogous to the flexible hierarchy API
methods: <span style="font-family: monospace;">IAsynchronousContentAdapter.isContainer()</span>
and <span style="font-family: monospace;">IAsynchronousContentAdapter.retrieveChildren()</span>
and are pretty straightforward to implement. Also <span
style="font-weight: bold; font-family: monospace;">retrieveLabel()</span>
is directly analogous to
IAsynchronousLabelAdapter.retrieveLabel().&nbsp; <br>
<br>
The <span style="font-family: monospace; font-weight: bold;">hasDeltaFlags()</span>
and <span style="font-weight: bold; font-family: monospace;">buildDelta()</span>
are used to generate model deltas in response to service events. These
are discussed in the next section.<br>
<br>
Finally, in most cases the elements in the views correspond
directly to an <span style="font-family: monospace;">IDataModelContext</span>
(DM-Context) objects of a specific type.&nbsp; In those cases, the <span
style="font-family: monospace;">DMContextVMLayoutNode</span>
abstract class implements the common functionality in that pattern.<br>
<h4>Model deltas</h4>
The <span style="font-family: monospace;">hasDeltaFlags()</span> and <span
style="font-family: monospace;">buildDelta()</span> methods are used
to implement the <span style="font-family: monospace;">IModelProxy </span>adapter,
and are the most tricky aspect of this design.&nbsp; The difficulty is
that the flexible hierarchy views require that the <span
style="font-family: monospace;">IModelProxy </span>translate data
model-specific events, into generic model deltas that can be
interpreted by the viewer.&nbsp; The deltas (<span
style="font-family: monospace;">IModelDelta</span>) are tree
structures which are supposed to mirror the structure of nodes in the
tree, and which contain flags that tell the viewer what has changed in
the view and how.<a href="#Asterix">*</a>&nbsp; This means that if the
model proxy receives an event for some <span
style="font-family: monospace;">IDataModelContext</span> (DM-Context) object,
it needs to know if this object is in the viewer's tree, and what is
the full path (or paths) that leads to this object.&nbsp; <br>
<p>The model delta is generated by first calling the top layout node's <span
style="font-family: monospace;">hasDeltaFlags()</span> with the
received event, which then can either return <span
style="font-family: monospace;">true </span>or ask any of its
children if they have deltas (which in turn returns true or calls its
children, etc).&nbsp; If a node returns <span
style="font-family: monospace;">true </span>for <span
style="font-family: monospace;">hasDeltaFlags()</span>, then the
asynchronous <span style="font-family: monospace;">buildDelta()</span>
is called with the event and a parent delta node, to generate the delta
elements and flags for its node.&nbsp; Once the layout node generates
its delta objects, it still needs to call its children, which in turn
add their delta information, and so on.<br>
</p>
<p><a name="Asterix"></a>* It's not strictly true that a full path to
an element always has to be present for model delta's to work.&nbsp; If
the full path is not present, the viewer will try to find the element
using an internal map that it keeps of all of the elements it
knows.&nbsp;
But since the viewer is lazy loading, it is possible (and likely) that
the element affected by an event is not even known to the viewer at
time of the event, and for some delta actions, <span
style="font-family: monospace;">IModelDelta.SELECT</span> and <span
style="font-family: monospace;">IModelDelta.EXPAND</span>, this is not
acceptable.<br>
</p>
</body>
</html>

View file

@ -1,135 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title>GDB/MI Debugger on top of DSF - Instructions</title>
</head>
<body>
<h2>GDB/MI Debugger implementation based on DSF</h2>
<br>
<hr style="width: 100%; height: 2px;">
<h3>Buiding and Running Instructions<br>
</h3>
<h4>To build:</h4>
<ol>
<li>Install the latest milestone of Eclipse 3.3 SDK</li>
<li>Install the latest milestone of CDT 4.0</li>
<li>Install and configure gdb (cygwin on windows)</li>
<li>Check out following projects from<a
href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.dd.dsf/plugins/?cvsroot=DSDP_Project">
<span class="moz-txt-slash"><span class="moz-txt-tag">/</span>cvsroot/dsdp/org.eclipse.dd.dsf/plugins
</span></a></li>
<ul>
<li>org.eclipse.dd.dsf </li>
<li>org.eclipse.dd.dsf.ui </li>
<li>org.eclipse.dd.dsf.debug </li>
<li>org.eclipse.dd.dsf.debug.ui </li>
<li>org.eclipse.dd.dsf.mi.core </li>
<li>org.eclipse.dd.dsf.mi.ui.</li>
</ul>
</ol>
<h4>To run:</h4>
<ol>
<li>Create a new "Managed make build project" called "hello".</li>
<li>Create a simple hello.c source file:</li>
</ol>
<div style="margin-left: 40px;">
<pre>#include &lt;stdio.h&gt;<br>int main(void) {<br> printf("Hello world");<br>}<br></pre>
</div>
<ol start="3">
<li>Build the project.<br>
</li>
<li>Create a new "DSF C/C++ Local Application"&nbsp; launch
configuration (one with the pink icon) and set the executable and entry
point to "main"<br>
</li>
<li>Launch and step through.</li>
<li>If the "source not found" page appears, the a path mapping needs
to be created.&nbsp; This is an issue with latest cygwin gdb.<br>
</li>
<ol>
<li>Click on the "Edit source lookup" button in the editor, or
right click on the launch node in Debug View and select "Edit source
lookup"</li>
<li>Click on the "Add..." button</li>
<li>Select "Path Mapping" and click OK.<br>
</li>
<li>Select the new "Path Mapping" source container and click the
"Edit..." button.</li>
<li>Once again, click the "Add..." button to create a mapping.</li>
<li>Enter the path to map from.&nbsp; Look at the stack frame label
in Debug view, if the filename is something like
"/cygdrive/c/workspace/hello/hello.c", enter the path to the first real
directory "/cygdrive/c/workspace".</li>
<li>Enter the correct path to the directory entered above, in the
file system.&nbsp; In example above, it would be "C:\workspace".</li>
<li>Click OK three times and you'll be back in Kansas.... ehm Debug
view that is.</li>
<li>If the source doesn't show up right away, try stepping once.</li>
</ol>
</ol>
<hr style="width: 100%; height: 2px;">
<h3>Supported Platforms<br>
</h3>
Currently only Windows with <a
href="http://www.cygwin.com/mirrors.html">cygwin</a> GDB is supported.
<br>
<br>
<hr style="width: 100%; height: 2px;">
<h3>Current Features<br>
</h3>
<ul>
<li>Launching</li>
<ul>
<li>The "DSF C/C++Local Application" is the standard CDT launch
configuration minus some of the features.&nbsp; <br>
</li>
<li>What is NOT working here is <br>
</li>
<ul>
<li>Debugger tab: the selection of debugger back ends (gdb/mi,
Cygwin gdb debugger, etc.), tThe implementation is currently hard-wired
for Cygwin,</li>
<li>Debugger tab: Debugger Options section</li>
</ul>
</ul>
<li>Debug view</li>
<ul>
<li>Single thread debugging only.</li>
<li>Terminating<br>
</li>
<li>Stepping <br>
</li>
<li>Resume/Suspend<br>
</li>
</ul>
<li>Console support<br>
</li>
<ul>
<li>GDB process output</li>
<li>NO user process console support<br>
</li>
</ul>
<li>Breakpoints</li>
<ul>
<li>Basic CDT breakpoint support implemented</li>
<ul>
<li>no filtering support, <br>
</li>
<li>no advanced options (hardware, temporary, etc)</li>
<li>no watchpoints <br>
</li>
</ul>
</ul>
<li>Variables</li>
<ul>
<li>not yet<br>
</li>
</ul>
</ul>
<br>
<hr style="width: 100%; height: 2px;">Updated Aug 25th, 2006<br>
<br>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -1,363 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title>DSF Services Model</title>
</head>
<body>
<h2>DSF Services Model</h2>
<br>
<b><span style="font-size: 12pt; font-family: &quot;Times New Roman&quot;;">Version
1.0<br>
Pawel Piech <br>
</span></b><b><span
style="font-size: 12pt; font-family: &quot;Times New Roman&quot;;">&copy; 2006,
Wind River Systems.<span style="">&nbsp; </span>Release under EPL
version 1.0.</span></b><b><span
style="font-size: 12pt; font-family: &quot;Times New Roman&quot;;"><br style="">
</span></b>
<p>Debugger Services Framework (DSF) is primarily a service framework
defining rules for how
services should be registered, discovered, organized into functional
groups, communicated with, and started/ended.&nbsp; These rules help to
organize the services into a functional system that efficiently
abstracts various debugger back end capabilities.&nbsp; </p>
<p>DSF services build on top of the OSGI services framework, so
it's important to understand OSGI services before looking at DSF
itself.&nbsp; For an overview of OSGI including services, see the <a
href="http://eclipsezilla.eclipsecon.org/php/attachment.php?bugid=185">presentation
on OSGI from EclipseCon 2006</a>.&nbsp; For detailed information, see
OSGI javadocs, primarily: <a style="font-family: monospace;"
href="http://help.eclipse.org/help31/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/osgi/org/osgi/framework/package-summary.html">org.osgi.framework</a><a
style="font-family: monospace;"
href="http://help.eclipse.org/help31/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/osgi/org/osgi/framework/ServiceRegistration.html">ServiceRegistration</a>,
<a
href="http://help.eclipse.org/help31/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/osgi/org/osgi/framework/BundleContext.html"><span
style="font-family: monospace;">BundleContext</span></a>, <a
href="http://help.eclipse.org/help31/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/osgi/org/osgi/framework/BundleContext.html"><span
style="font-family: monospace;">ServiceReference</span></a>, <a
href="http://help.eclipse.org/help31/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/osgi/org/osgi/framework/Filter.html"><span
style="font-family: monospace;">Filter</span></a>, and <a
style="font-family: monospace;"
href="http://help.eclipse.org/help31/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/osgi/org/osgi/util/tracker/ServiceTracker.html">ServiceTracker</a>.
</p>
<h3>Services<br>
</h3>
In OSGI any class can be registered as a service.&nbsp; In DSF,
Services must implement the <a
href="http://dsdp.eclipse.org/help/latest/topic/org.eclipse.dd.dsf.doc/reference/api/org/eclipse/dd/dsf/service/IDsfService.html"><span
style="font-family: monospace;">IDsfService</span></a>&nbsp;
interface, which requires that the service
provide:<br>
<ol>
<li>Access to the <span style="font-family: monospace;">DsfExecutor </span>that
has to be used to access service methods.</li>
<li>Full list of properties used to uniquely identify the service in
OSGI.</li>
<li>Startup and shutdown methods.</li>
</ol>
For the first two items, a service must use the data it received from
its constructor.&nbsp; For the third item, a service must register and
unregister itself with OSGI.&nbsp; But beyond that, this is all that
services have in common, everything else is up to the specific service
interface.<br>
<h3>Sessions (<a
href="http://dsdp.eclipse.org/help/latest/topic/org.eclipse.dd.dsf.doc/reference/api/org/eclipse/dd/dsf/service/DsfSession.html"><span
style="font-family: monospace;">org.eclipse.dd.dsf.service.DsfSession</span></a>)<br>
</h3>
DSF services are organized into logical groups, called
sessions.&nbsp; Sessions are only necessary because we want multiple
instances of systems built with DSF services to run at the same
time&nbsp; This is because there is only a single OSGI service
registry, so if multiple services are registered with a given class
name, OSGI will not be able to distinguish between the two based on the
class name alone.&nbsp; So there is an additional property which is
used by every DSF service when registering with OSGI, <span
style="font-family: monospace;">IDsfService.PROP_SESSION_ID</span>.&nbsp;
<br>
<p>A <span style="font-family: monospace;">Session</span> object
(TODO: link javadoc) has the following data associated with it:<br>
</p>
<ul>
<li><span style="text-decoration: underline;">Session ID</span> - A <span
style="font-family: monospace;">String</span> object that is unique
among all other sessions.&nbsp; Its ID is used by services as the <span
style="font-family: monospace;">IDsfService.PROP_SESSION_ID</span>
property, and it is used by the client to obtain the <span
style="font-family: monospace;">Session</span> object instance.</li>
<li><span style="font-family: monospace; text-decoration: underline;">DsfExecutor</span>
- Each session has a single executor.&nbsp; This means that all the
services in a single session share the same executor and dispatch
thread, and conversely it means that when operating in the dispatch
thread, the state of all the services in a session will remain the same
until the end of a dispatch.&nbsp; Note: multiple sessions <span
style="font-style: italic;">could</span> share the same <span
style="font-family: monospace;">DsfExecutor</span>.</li>
<li><span style="text-decoration: underline;">Service startup counter</span>
- An integer counter which is read and incremented by every service
that is started in a session.&nbsp; This counter is used to determine
the dependency order among services, which is used by events.</li>
<li><span style="text-decoration: underline;">Event listener list</span>
- This will be covered in the "Events" section.</li>
<li><span style="text-decoration: underline;">Adapter list</span> - A
list of adapters, providing functionality analogous to runtime's <span
style="font-family: monospace;">org.eclipse.core.internal.runtime.AdapterManager</span>.&nbsp;
Sessions need to manage their own lists of adapters, so that <span
style="font-family: monospace;">IAdapter</span> objects which
originate from DSF services can provider different adapters, based
on the session that they originate from.&nbsp; This feature is covered
in detail in the "DSF Data Model" document.<br>
</li>
</ul>
<p>The Session class also has a number of static features used to
manage Session objects:</p>
<ul>
<li><span style="text-decoration: underline;">Session ID counter</span>
- Used to generate new session IDs.</li>
<li><span style="text-decoration: underline;">Methods for starting
and ending sessions</span> <br>
</li>
<li><span style="text-decoration: underline;">Session started/ended
event listener list</span> - This allows clients to be notified when
sessions are created or terminated, which is used mostly for clean-up
purposes.<br>
</li>
</ul>
<h3>Startup/Shutdown</h3>
Managing the startup and shutdown process is often the most complicated
aspect of modular systems.&nbsp; The details of how the startup and
shutdown processes should be performed are also highly dependent on the
specifics of the system and service implementations.&nbsp; To help
with this, DSF provides two simple guidelines:<br>
<ol>
<li><span style="text-decoration: underline;">There should be a clear
dependency tree of all services within a session</span> - When the
dependencies between services are clearly defined, it is possible to
bring-up and bring-down the services in an order that guarantees each
running service can access all of the services that it depends on.</li>
<li><span style="text-decoration: underline;">There needs to be a
single point of control, which brings up and shuts down all the
services.</span> - In other words, services should not initialize or
shut-down themselves, based on some global event that they are all
listening to.&nbsp; But rather an external piece of logic needs to be
in charge of performing this operation.</li>
</ol>
The main implication of the first guideline, is that each service can
get and hold onto references to other services, without having to
repeatedly check, whether the service references are still valid.&nbsp;
This is because if a given service is to be shut-down, all services
that depend on this service will already have been shut down.&nbsp; The
second guideline, simply ensures that startup and shutdown procedures
are clear and easy to follow.<br>
<h3><a
href="http://dsdp.eclipse.org/help/latest/topic/org.eclipse.dd.dsf.doc/reference/api/org/eclipse/dd/dsf/service/DsfServicesTracker.html"><span
style="font-family: monospace;">org.eclipse.dd.dsf.service.DsfServicesTracker</span></a>
vs <a
href="http://help.eclipse.org/help31/topic/org.eclipse.platform.doc.isv/reference/osgi/org/osgi/util/tracker/ServiceTracker.html"><span
style="font-family: monospace;">org.osgi.util.tracker.ServiceTracker</span></a></h3>
OSGI methods for obtaining and tracking services can be rather
complicated.&nbsp; To obtain a reference to a service, the client has
to:<br>
<ol>
<li>Get a reference to a <span style="font-family: monospace;">BundleContext
</span>object, which can be retrieved from the plugin class.</li>
<li>Obtain a service reference object by calling <span
style="font-family: monospace;">BundleContext.getServiceReference()</span>;</li>
<li>Obtain an instance of the service by calling <span
style="font-family: monospace;">BundleContext.getService(ServiceReference)</span>.</li>
</ol>
But worst of all, when the client is finished using the service, it has
to call <span style="font-family: monospace;">BundleContext.ungetService(ServiceReference)</span>,
because the bundle context counts the used references to a given
service.&nbsp; All this paperwork is useful for services which manage
their own life-cycle, and could be un-registered at any time.&nbsp; To
make managing references to these kinds of services, OSGI provides a
utility class, called <span style="font-family: monospace;">ServiceTracker</span>.&nbsp;
<br>
<p>For DSF services, the life cycle of the services is much more
predictable, but the process of obtaining a reference to a service is
just as onerous.&nbsp; DSF provides its own utility, which is
separate from the <span style="font-family: monospace;">ServiceTracker</span>,
named <span style="font-family: monospace;">DsfServicesTracker</span>.&nbsp;
The differences between the two are listed in table below:<br>
</p>
<table style="width: 100%; text-align: left;" border="1" cellpadding="3"
cellspacing="0">
<tbody>
<tr>
<th
style="vertical-align: top; background-color: rgb(204, 204, 204); font-family: helvetica,arial,sans-serif;">Property<br>
</th>
<th
style="vertical-align: top; background-color: rgb(204, 204, 204); font-family: helvetica,arial,sans-serif;">OSGI
<span style="font-family: monospace;">ServiceTracker</span><br
style="font-family: monospace;">
</th>
<th
style="vertical-align: top; background-color: rgb(204, 204, 204); font-family: helvetica,arial,sans-serif;">DSF
<span style="font-family: monospace;">DsfServicesTracker</span><br>
</th>
</tr>
<tr>
<td
style="vertical-align: top; font-family: helvetica,arial,sans-serif;">Number
of services tracked<br>
</td>
<td
style="vertical-align: top; font-family: helvetica,arial,sans-serif;">While
not strictly limited, it is optimized for tracking services of a single
class type, or more typically to track a single service reference.<br>
</td>
<td
style="vertical-align: top; font-family: helvetica,arial,sans-serif;">Designed
to track services within a single DSF session.&nbsp; <br>
</td>
</tr>
<tr>
<td
style="vertical-align: top; font-family: helvetica,arial,sans-serif;">When
are service references obtained<br>
</td>
<td
style="vertical-align: top; font-family: helvetica,arial,sans-serif;">Obtain
references automatically as the services register themselves.<br>
</td>
<td
style="vertical-align: top; font-family: helvetica,arial,sans-serif;">Service
references are obtained as requested by the client, and cached.</td>
</tr>
<tr>
<td
style="vertical-align: top; font-family: helvetica,arial,sans-serif;">Synchronization<br>
</td>
<td
style="vertical-align: top; font-family: helvetica,arial,sans-serif;">Multi-thread
accessible.&nbsp; <br>
</td>
<td
style="vertical-align: top; font-family: helvetica,arial,sans-serif;">Can
be accessed only on the session's dispatch thread.<br>
</td>
</tr>
<tr>
<td
style="vertical-align: top; font-family: helvetica,arial,sans-serif;">Clean-up<br>
</td>
<td
style="vertical-align: top; font-family: helvetica,arial,sans-serif;">Automatically
un-gets references for services that are shut down.<br>
</td>
<td
style="vertical-align: top; font-family: helvetica,arial,sans-serif;">Client
must listen to session events, and clean up as needed.<br>
</td>
</tr>
</tbody>
</table>
<p>Both trackers are useful.&nbsp; Service implementations that depend
on a number of other services are most likely to use DSF <span
style="font-family: monospace;">ServicesTracker</span>, while some
clients, which use a single service may find OSGI <span
style="font-family: monospace;">ServiceTracker </span>more suitable.<br>
</p>
<h3>Events</h3>
Events are the most un-conventional component of the services package
and probably most likely to need modifications to the design by the
community.&nbsp; The design goal of
the event system is to allow a hierarchy of event classes, where a
listener could register itself for a specific event class or for all
events which derive from a base class.&nbsp; The use case for this
behavior is in the data model, where we would like to have the ability
to capture all model-related events with a generic listener while at
the same time allowing for services to fully use class types.&nbsp; <br>
<p>The event model is made up of the following components:<br>
</p>
<ul>
<li><span style="font-weight: bold;"><span
style="font-family: monospace;">DsfServiceEventHandler </span>annotation</span>
- This is the only indicator that a given method is an event
listener.&nbsp; The class with the event handler doesn't have to
implement any interfaces, but it must be public, which is a big
drawback.</li>
<li><span style="font-weight: bold;"><span
style="font-family: monospace;">Session.addServiceEventListener</span>,
<span style="font-family: monospace;">Session.removeServiceEventListener</span>
methods</span> - These methods allow clients to register for an event
based on an event class and a service filter, where the filter can be
used to uniquely identify a service in case of services with multiple
instances of same class.</li>
<li><span style="font-weight: bold;"><span
style="font-family: monospace;">Session.dispatchEvent</span> method -</span>
This is the method that actually dispatches the event to the
listeners.&nbsp;
The method must be called by a service that generates the event.</li>
</ul>
There are only a few more notes about the events mechanism:<br>
<ol>
<li>The event is always dispatched in its own <span
style="font-family: monospace;">Runnable</span> submitted to
the session's <span style="font-family: monospace;">DsfExecutor</span>.</li>
<li>There is a slight convenience for clients not to have to register
for each type of event separately.</li>
<li>There is a slight inconvenience for clients, because anonymous
classes cannot be used as listeners, due to the public class
requirement.</li>
</ol>
<h3>Debugger Services (<a
href="http://dsdp.eclipse.org/help/latest/topic/org.eclipse.dd.dsf.doc/reference/api/org/eclipse/dd/dsf/debug/package-summary.html"><span
style="font-family: monospace;">org.eclipse.dd.dsf.debug</span></a>)<br>
</h3>
DSF framework includes a set of service interfaces for a typical
debugger implementation.&nbsp; Functionally, they are pretty much
equivalent to the platform debug interfaces, but they are structured in
a way that allows a debugger to implement only some of them.&nbsp; In
order for the startup and shutdown process to work effectively, the
dependencies between services need to be clearly defined.&nbsp; The
dependencies between the main service interfaces are shown in the graph
below:<br>
<img alt="" title="Debugger Services Dependencies"
src="dsf_services_model-1.png" style="width: 720px; height: 540px;"><br>
<p>It's also important to realize that it's unlikely that a single
hierarchy of interfaces will adequately fit all the various debugger
use cases, and it is likely that some interfaces will be needed which
partially duplicate functionality found in other interfaces.&nbsp;
An example of this in the proposed interface set are the interfaces
which are used to initiate a debugging session.&nbsp; The <span
style="font-family: monospace;">INativeProcesses</span> service is
intended as the simple abstraction for native debuggers, where a
debugger only needs an existing host process ID or an executable image
name.&nbsp; Based on this a <span style="font-family: monospace;">INativeProcess</span>
debugger implementation should be able to initiate a debugging session,
and return run-control, memory, and symbol contexts that are required
to carry out debugging operations.&nbsp; By comparison, <span
style="font-family: monospace;">IOS</span> and <span
style="font-family: monospace;">ITarget</span> are generic interfaces
which allow clients to manage multiple target definitions, to
examine a wide array of OS objects, and to attach a debugger to a
process or some other debuggable entity.&nbsp; <br>
<img alt="" title="Process Managment Services"
src="dsf_services_model-2.png" style="width: 720px; height: 540px;"><br>
</p>
<h4>Disclaimer</h4>
Drafting large APIs that are intended to have many implementations and
by clients is a notoriously difficult task.&nbsp; It is
impossible to expect that a first draft of such interfaces will not
require changes, and only time and multiple successful implementation
can validate them.&nbsp; While we can draw upon many examples of
debugger
APIs in Eclipse in and our commercial debugger, this is a new API with
a
prototype that exercises only a small portion of its interfaces.<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
</body>
</html>

View file

@ -4,12 +4,14 @@
<!-- Define the top level topics --> <!-- Define the top level topics -->
<!-- ============================================================================= --> <!-- ============================================================================= -->
<toc label="DSF Design Documents"> <toc label="DSF Documents">
<topic label="DSF White Paper" href="docs/dsf_white_paper.html"/> <topic href="docs/dsf_white_paper.html" label="DSF White Paper">
<topic label="DSF Services Model" href="docs/dsf_services_model.html"/> </topic>
<topic label="DSF Concurrency Model" href="docs/dsf_concurrency_model.html"/> <topic href="docs/intro/dsf_programming_intro.html" label="Introduction to Programming with DSF">
<topic label="DSF Data Model" href="docs/dsf_data_model.html"/> </topic>
<topic label="GDB/MI Debugger implementation based on DSF" href="docs/dsf_mi_instructions.html"/> <topic href="docs/pda/dsf_debugger_howto.html" label="How to write a DSF-based debugger">
<topic label="DSF API Reference All Packages" href="reference/api/overview-summary.html"/> </topic>
<topic href="reference/api/overview-summary.html" label="DSF API Reference All Packages">
</topic>
<topic label="Legal" href="notices.html"/> <topic label="Legal" href="notices.html"/>
</toc> </toc>