Professional Documents
Culture Documents
ClockAPl
"All
my possessionsfor
amoment of time."
-Elizabeth
Tr
The ClockAPl
Java RTS implements the RTSJ Clock API, which defines real-time clock and
timer facilities. These classes should be used in place of calls to System.currentTimeMilf is, System.nanoTime, and the java.util.Date class. They represent points in time with the best possible accuracy and precision the underlying
hardware can support, and distinguish between absolute points in time, and those
relative to a starting point. Additionally, these classes allow the creation of timers
that f,re either periodically, or for one time only, deterministically. Finally, the
Java RTS implementation of these classes satisfles the POSIX real-time requirement of providing access to high-resolution timers. For instance, the code in ListingT-l checks and displays the resolution of the real-time clock the system it's
executed on.
223
224
Crupron
Listing
7-1
Trun
Rott-Tttp CtocxAPI
*;
t-
utionO ;
pri ntl n (
"Real-time clock resolution = " + t.toStringO);
C'l
System. out
To use the RTSJ Clock API (see Figure 7-1) to create timestamps or highresolution timings, begin by gaining a reference to the singleton real-time clock
object via the getReal ti meCl ock O method call on the j avax . real ti me . Cl ock
object, as seen in the previous example. To calculate a timestamp with nanosecond
resolution (on hardware that supports it), use code similar to that in Listing 7-2.
Listing
7-2
//
begi nTim
//
//
do process'ing here..
Tttr CtocxAPI
In addition to the
22s
combination of the Clock and Absol uteTime classes, you can perform date and
time operations. This includes gaining access to java.util.Date objects. The
complete class diagram for the RTSJ time operations is shown inFigweT-2.
<singleton>
javax. realtime.Clock
+getEpochOffset0
+getRealtimeClock0
+getResolution0
+getTime0
+setResolution0
ir--------il
;
i
i
i
i
I
HignResolutionTime
------l
+absolute$ i
^r^-^/\
+ctoneQ
+compareTo$ i
+equatsQ i
+gerCtockQ i
I
,
+getMittisecondsQ
+getNanosecondsfl
\,
+hashCode
lI
I
+relative0
+set$
+waitFor0bject0
+add0
+relative0
+add0
+relative0
+subtract0
+set$
+tostring0
+getDate0
+subtractfl
+tostring0
Figure
7-2
226
+destroyO
+disableQ
+enableO
+fireO
+getClockQ
+getFireTime$
+handledBY$
+ isRunningQ
+removeHanter1
+reschedulefl
+setHandleO
i
I
i
i
i
i
I
I
,
i
i
i
+getlnterval0
+setlnterval0
Figure
7-3
We'll take a look at how to use these classes shortly. As mentioned earlier, the
Cl ock API provides access to timers that can be set to expire periodically, or one
time only. These classes are Peri odi cTi mer and OneShotTi mer, respectively (see
Figure 7-3). Since these classes extend AsynchEvent, we'11 examine these classes
in detail in Chapter 8.
Ttrun, O pnntnoN s
periodic Real ti
The Absol uteTi me class represents a specific point in time. It's specif,ed and
measured in a combination of milliseconds and nanoseconds. Here are the AbsoI uteTi me class constructors:
Absol uteTimeO---{reates a new object representing a time of 0 milliseconds
and nanoseconds.
The Rel ati veTi me class represents a time interval, such as a period of time relative to the current time. It's frequently used when specifying a periodic real-time
thread's period, a parameter to a Real ti meTh read . si eep call, or a call to a form
of the wai t method when synchronizing on shared objects. The constructors for
the Rel ati meTi me class are:
int
sents the interval based on the provided milliseconds parameter, plus the pro-
227
Cruprnn7
228
the provided nanosecond parameter. In this case, the provided clock is used;
null, then the real-time clock is used by default.
if
ati veTi
Rel
ativeTime(ReI ativeTime)-creates
(Cl ock)--creates a new object with zeros for both the milliseconds and nanoseconds parameter. In this case, the provided clock is used; if
null, then the real-time clock is used by default.
me
a new object
7-3
ti me. * ;
public class Main {
public static void main(String[] args) {
// Create the objects beforehand; eliminates
jitter
AbsoluteTime before;
AbsoluteTime after;
RelativeTime elapsed;
" + elapsed)
As the comment at the beginning of the code says, we declare the time objects
before the timed operation so that the object creating time doesn't get factored into
the measurement. Since Cl ock operations are bounded and deterministic in Java
RTS, the calls to getTi me are efflcient, and consistent, in terms of execution time.
In fact, due to inherent jitter in the j ava. uti I . Date class, you should use Cl ock .
getRealtimeClockO.getTime and the AbsoluteTime it returns for all timestamping operations, such as that shown in Listing 7-4.
7-4
Listing
229
look at more involved example of how to use both the Absol uteTime
ati veTi
me
classes.
In the examples shown in this flgure, the stock price starts at a value of 10.00.
Before the first ten-millisecond conflation period (where an update to clients will
be sent) four individual price updates are received from the external data feed for
the stock being watched. Each price update is applied to the stock price, but the
result is not sent until the conflation period expires. At that time, the stock price is
9.48, which is the value sent in the update to the client applications.
Stock start
price:
10.00
Notify
Notify
Clients
Clients
Stock:9.48
Stock: 9.37
.'i'Il
9.98
il
9.48
0 *--ro-r;poateliola-t.ro--'
9.37
}0,
*-----;upout-,,;------'
i:
Figure
7-4
230
However, during the next conflation period (another ten milliseconds) no price
updates are received. Therefore, no conflated price update needs to be sent since
the value hasn't changed. However, as soon as the next price update does occur, the
resulting price will be sent out immediately (which is 9.37 in this example). At this
point, the conflation timer is reset, and updates will be conflated for the next ten
milliseconds.
The description and diagram above lay the groundwork for our sample application. There's no need to create a periodic RTT; we'll simply use a starting time (an
Absol uteTi me object) and wait on an object that will be signaled when an update
occurs. However, we'Il use the H'i ghResol uti onTi me . wai tFo rObj ect method,
which allows us to provide a maximum amount of time to wait. Therefore, with
each update, the code will check the time elapsed, and if it's still within the conflation time period, the code will again wait but for the time remaining in the period.
Implementing it this way allows us to demonstrate the use of Absol uteTi me and
Rel ati veTi me objects, as well as Hi ghResol uti onTi me arithmetic in one meaningful example. In Chapter 8, we'11 modify this application to use a Ti mer object
instead. For now, let's begin looking at Listing 7-5.
Listing
7-5
ti
me.
*;
ic class MyApp {
Object lock = new ObjectO;
double update = 0.00;
final RelativeTime PERI0D = nw RelativeTime(L0,0);
pubf
// ...
// ...
//
L0ms
231
// ...
// ...
pub'lic MyAppO {
Conflater conflater =
DataFeed datafeed =
conflater. startO
datafeed. startO
w ConflaterO;
hw DataFeedo;
= new MyAppO;
From this code, you can see that there are two RealtimeThread classes: Conflater, and DataFeed. The Conflater class object listens for updates from the
DataFeed class object, and sends conflated updates to its clients at least every
PERIOD amount of time. Both RTTs share the object 1ock, and each change to the
stock is communicated through update.
Listing
7-6
try
//
]
lock.waitO;
Ltinued
CrutprnnT
232
Tae
Rott-Tmr CucxAPI
//
AbsoluteTime current
rtClock.getTimeO;
RelativeTime elapsed =
cu r rent . subt ract (startTi
me)
updateoccured = true;
price += updti
System. out . pri ntl n (
"Conflater: update " + update +
", El apsed=" + el apsed) ;
timeout = PERIOD.subtract( elapsed
);
//
Send
updateCl i ents(pri
i secondsO )
ce, e1 apsed) ;
else {
//
Send
ce, e1 apsed) ;
startTifi = FtClock. getT'imeo ;
updateCl i ents(pri
Dm
F nno E xrup to
]
catch(Exceptione){
e . pri
ntstackTrace O ;
astUpdate)
Send update
//
timeout = PERIOD;
updateOccured = false;
The first update received is the starting price. From that point on, the thread enters
an inf,nite loop waiting for an update, or the period to expire-whichever comes
first. In the whi 1e loop, wai tForobject is called with a timeout value, which is
initially set to the full ten-millisecond period. Therefore, one of two things can
occur: an update arrives, which results in the wait call returning when the lock
object is signaled, or the timeout period expires.
When execution continues after wai tFor0bject, the elapsed time since the start
of the conflation period is determined, and a check is made to see if a data feed
update has arrived. This is simple-if the update price is non-zero, an update
occurred. Otherwise the timeout value has expired. In the case of a price update, a
flag is set (to be checked later, when the period expires), the price is adjusted
accordingly, and the timeout value is set to the time remaining in the current
period. However, if the elapsed time since the beginning of the period is greater
than the period itself, then we know that a data feed update hasn't been received in
the current period and this update needs to be sent to clients right away. The
updateCl i ents method is called to perform this.
In the case of a period timeout, a check is made to see if any data feed updates
have occurred in the current period. If not, then nothing special is done, and the
233
Cru*rr,n7 Tnr
234
Rn.r-Ttur CtocxAPI
7-7
//
send(1
send (1-
, -.0L);
,
.0L)
send(2, -.50) ;
send(4, O.0O);
// tust
sleep for
4ms
//
after period
]
private void send(int interval, double change) {
if(interval>0){
// Wait for elapsed time
try {
RelativeTime elaPsed
ativeTime(interval, 0) ;
synchron'i zed ( privlock ) {
Hi ghResol uti onTi me . wai tFor0bj ect (
new Rel
privlock, elapsed);
catch(Exceptione){}
]
if ( change != O.OO )
update = change;
235
synchronized(1ock){
lock.notifyO;
This contrived code simulates sending the updates, perperiod, as shown in Figure
7-4.The first parameter in the send method is the amount of time in milliseconds
the code waits before sending the update. The second parameter is the amount the
stock price has moved, up or down (a delta value). If the update value passed is
zeto,the method effectively acts as a high-resolution call to s'leep. If the interval
is set to zeo, the price update is sent immediately.
Sending a price update is a simple operation; the global update delta value is set,
and the shared lock object is signaled (informing the Conflater class that the
update is available). The succession of calls to send is meant to simulate a random
pattern of updates with varying amounts of time between them, such that they
recreate the scenario in Figure 7-4.
Applicotion Output
When executed with Java RTS on a uniprocessor, the following output
will
be
observed:
Conflater started...
Conflate r recei ved start'i ng pr.i ce
Conflater: update -0.02, Elapsed=(2 ms, 2484 ns)
Conflater: update -0.0L, Elapsed=(3 ms, 5276 ns)
Conflater: update 0.01-, Elapsed=(5 ms, 3099 ns)
Conflater: update -0.5, E'lapsed=(7 ms, 3104 ns)
Conflater: Updati ng c'li ents:
Conflater: pri c=9.48
Conflater: Time since last update:(10 ms, 0 ns)
Conflater: update -0.LL, E'lapsed=(13 ms, 5331 ns)
Conflater: update after period
Conflater: Updating clients:
Conflater: pri ce=9. 37
Conflater: Time since last update:(13 ms, 5331 ns)
Atthe verybeginning (r= 0), the stock startprice
each update is received is relative to the start of the current period (not to each
236
Cu.prnn7
Trun
Rn,uTt*t CtocxAPI
individual update). Therefore, the first update to clients is triggered by the period
timeout att+ l0 milliseconds.
However, during the second ten-millisecond period, no data feed updates occur.
Therefore, when the period expires, nothing is done. When an update finally does
arrive 13 milliseconds after the beginning of the second period (t + 23 milliseconds), the new price is sent to clients immediately, and the cycle repeats.