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:
parent
0b3ad17747
commit
7158eeed07
8 changed files with 9 additions and 1223 deletions
Binary file not shown.
Before Width: | Height: | Size: 6.1 KiB |
|
@ -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: "Times New Roman";">Version
|
||||
1.0<br>
|
||||
Pawel Piech<br>
|
||||
© 2006, Wind River Systems.<span style=""> </span>Release
|
||||
under EPL version 1.0.</span></b><b><span
|
||||
style="font-size: 18pt; font-family: "Times New Roman";"><o:p></o:p></span></b></p>
|
||||
<h3>Introduction</h3>
|
||||
Providing a solution to concurrency problems is the primary design goal
|
||||
of DSF. 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). </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. 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. 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. 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. This means that
|
||||
thread-defensive programming techniques, such as making duplicates of
|
||||
lists before iterating over them, are not necessary. 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. 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. <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. 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. 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. 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. 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. 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. 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. 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. 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>.
|
||||
<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. 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. 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<Integer> future = executor.submit(new Callable<Integer>() {<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. 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. 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. 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. Suppose there is a viewer which needs to
|
||||
show data that originates from a remote "provider". There is a
|
||||
considerable delay in transmitting the data to and from the provider,
|
||||
and some delay in processing the data. The viewer is a
|
||||
lazy-loading table, which means that it request information only about
|
||||
items that are visible on the screen, and as the table is scrolled, new
|
||||
requests for data are generated. The diagram below illustrates
|
||||
the
|
||||
logical relationship between components:<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. It has an associated content
|
||||
provider, SlowDataProviderContentProvider) which handles all the
|
||||
interactions with the data provider. The lazy content provider
|
||||
operates in a very simple cycle:</p>
|
||||
<ol>
|
||||
<li>Table viewer tells content provider that the input has changed by
|
||||
calling <span style="font-family: monospace;">IContentProvider.inputChanged()</span>.
|
||||
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. 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<Integer> 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. 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. 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. 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. 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. 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. <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. <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 -> 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. Embedded debuggers are often slow in
|
||||
retrieving and processing data, and can sometimes be accessed through a
|
||||
relatively slow data channel, such as serial port or JTAG
|
||||
connection. But as such, this basic example presents a couple
|
||||
of major usability problems<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. The result of this is visible to the user as
|
||||
lines of data are filled in one-by-one in the table. However,
|
||||
most debugger back ends are in fact capable of retrieving data in
|
||||
batches and are much more efficient at it than retrieving data items
|
||||
one-by-one.</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. 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. <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. 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. 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. When the service implementation receives a
|
||||
request for a single item, it buffers the request, and waits for other
|
||||
requests to come in. After a delay, the service clears the buffer
|
||||
and submits a request for the combined items to the data provider.</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>).
|
||||
<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. 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>. The
|
||||
<span style="font-family: monospace;">DataProvider.getItem()</span>
|
||||
implementation is changed as follows:</p>
|
||||
<pre> public void getItem(final int index, final GetDataDone<String> 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 < 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. 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. 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. For the table viewer
|
||||
content provider, this means that additional features have to be
|
||||
added. 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 >= topIdx && index <= 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. To make the canceling feature useful,
|
||||
the data provider service has to limit the size of the request
|
||||
queue. This is because in this example which simulates
|
||||
communication with a target and once requests are filed into the
|
||||
request
|
||||
queue, they cannot be canceled, just like a client can't cancel
|
||||
request once it sends them over a socket. So instead, if a flood
|
||||
of <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. 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() >= 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. But what we have found is that
|
||||
as the problem gets bigger, the amount of
|
||||
features in the data provider increases, the state of the
|
||||
communication protocol gets more complicated, and the number of modules
|
||||
needed in the service layer increases, using free threading and
|
||||
semaphores does not safely scale. Using a dispatch thread for
|
||||
synchronization certainly doesn't make the inherent problems of the
|
||||
system less complicated, but it does help eliminate the race conditions
|
||||
and deadlocks from the overall system.<br>
|
||||
<p>Coalescing and Cancellability are both optimizations. Neither
|
||||
of these optimizations affected the original interface of the service,
|
||||
and one of them only needed a service-side modification. But as
|
||||
with all optimizations, it is often better to first make sure that the
|
||||
whole system is working correctly and then add optimizations where they
|
||||
can make the biggest difference in user experience. </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. For
|
||||
cancellation, many services in DSF build on top of other services,
|
||||
which means that even a low-level service can cause a higher
|
||||
level service to retrieve data, while another event might cause it to
|
||||
cancel those requests. The perfect example of this is a Variables
|
||||
service, which is responsible for calculating the value of expressions
|
||||
shown in the Variables view. The Variables service reacts to the
|
||||
Run Control service, which issues a suspended event and then requests a
|
||||
set of variables to be evaluated by the debugger back end. But as
|
||||
soon as a resumed event is issued by Run Control, the Variables service
|
||||
needs to cancel the pending evaluation requests.<br>
|
||||
</p>
|
||||
<br>
|
||||
<br>
|
||||
</body>
|
||||
</html>
|
|
@ -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: "Times New Roman";">Version
|
||||
1.0<br>
|
||||
Pawel Piech<br>
|
||||
© 2006, Wind River Systems.<span style=""> </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. 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>
|
||||
This seems like a basic model-viewer separation, which is something
|
||||
that we theoretically have already. 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>
|
||||
<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>
|
||||
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. 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> 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> 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. In particular,
|
||||
the last goal doesn't have any implementation at this point. But
|
||||
other than that the, we believe that our current design mostly
|
||||
meets the other goals. 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. 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. 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. 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. 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. The
|
||||
interface is listed below:<br>
|
||||
<pre> public interface IDataModelContext<V extends IDataModelData> 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. However the implementation of <span
|
||||
style="font-family: monospace;">IDataModelData.getAdapter()</span>
|
||||
presents a particular challenge. 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. 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. 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>).
|
||||
<br>
|
||||
<p>The <span style="font-family: monospace; font-weight: bold;">getSessionId()</span>
|
||||
method serves two purposes. 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. <br>
|
||||
</p>
|
||||
<p>The <span style="font-family: monospace; font-weight: bold;">getServiceFilter()</span>
|
||||
method is actually included to allow future development. 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. 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". 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. In other words, there is
|
||||
some natural hierarchy to the data in debug services which needs to be
|
||||
accessible through the data model APIs. This hierarchy may be the
|
||||
same hierarchy that is to be shown in some debug views, but it doesn't
|
||||
have to be. 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. 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). 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. 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. 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<Boolean> done);<br> public void getElements(final IViewModelContext parentVmc, GetDataDone<IViewModelContext[]> 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. 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. 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(). <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. 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. 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. 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> 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. <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). 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. 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. 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.
|
||||
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>
|
|
@ -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 <stdio.h><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" 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. 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. 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. 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. <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 |
|
@ -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: "Times New Roman";">Version
|
||||
1.0<br>
|
||||
Pawel Piech <br>
|
||||
</span></b><b><span
|
||||
style="font-size: 12pt; font-family: "Times New Roman";">© 2006,
|
||||
Wind River Systems.<span style=""> </span>Release under EPL
|
||||
version 1.0.</span></b><b><span
|
||||
style="font-size: 12pt; font-family: "Times New Roman";"><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. These rules help to
|
||||
organize the services into a functional system that efficiently
|
||||
abstracts various debugger back end capabilities. </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. 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>. 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. 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>
|
||||
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. For the third item, a service must register and
|
||||
unregister itself with OSGI. 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. Sessions are only necessary because we want multiple
|
||||
instances of systems built with DSF services to run at the same
|
||||
time 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. 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>.
|
||||
<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. 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. 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. 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. 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>.
|
||||
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. 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. 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. 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. 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.
|
||||
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. 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. 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. All this paperwork is useful for services which manage
|
||||
their own life-cycle, and could be un-registered at any time. To
|
||||
make managing references to these kinds of services, OSGI provides a
|
||||
utility class, called <span style="font-family: monospace;">ServiceTracker</span>.
|
||||
<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. 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>.
|
||||
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. <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. <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. 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. 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. 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. <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. 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.
|
||||
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. 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. In
|
||||
order for the startup and shutdown process to work effectively, the
|
||||
dependencies between services need to be clearly defined. 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.
|
||||
An example of this in the proposed interface set are the interfaces
|
||||
which are used to initiate a debugging session. 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. 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. 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. <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. 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. 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>
|
|
@ -4,12 +4,14 @@
|
|||
<!-- Define the top level topics -->
|
||||
<!-- ============================================================================= -->
|
||||
|
||||
<toc label="DSF Design Documents">
|
||||
<topic label="DSF White Paper" href="docs/dsf_white_paper.html"/>
|
||||
<topic label="DSF Services Model" href="docs/dsf_services_model.html"/>
|
||||
<topic label="DSF Concurrency Model" href="docs/dsf_concurrency_model.html"/>
|
||||
<topic label="DSF Data Model" href="docs/dsf_data_model.html"/>
|
||||
<topic label="GDB/MI Debugger implementation based on DSF" href="docs/dsf_mi_instructions.html"/>
|
||||
<topic label="DSF API Reference All Packages" href="reference/api/overview-summary.html"/>
|
||||
<toc label="DSF Documents">
|
||||
<topic href="docs/dsf_white_paper.html" label="DSF White Paper">
|
||||
</topic>
|
||||
<topic href="docs/intro/dsf_programming_intro.html" label="Introduction to Programming with DSF">
|
||||
</topic>
|
||||
<topic href="docs/pda/dsf_debugger_howto.html" label="How to write a DSF-based debugger">
|
||||
</topic>
|
||||
<topic href="reference/api/overview-summary.html" label="DSF API Reference All Packages">
|
||||
</topic>
|
||||
<topic label="Legal" href="notices.html"/>
|
||||
</toc>
|
||||
|
|
Loading…
Add table
Reference in a new issue