You are on page 1of 14

The Reol-Tme

ClockAPl
"All

my possessionsfor

amoment of time."

-Elizabeth

Tr

chapter explores the implementation of the RTSJ def,nition of clocks and


timers within Java RTS. The classes explored in detail provide jitter-free time
operations, and access to a high-resolution clock for timer objects for different
types of timers. For instance, you can create timer objects that cause time-related
events to fire either periodically, or for one-time use.

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

Output the RT clock accuracy

i mport javax. real ti me.


publ i c cl ass Mai n {

*;

public static void main(String[] args) {


RelativeTime

t-

utionO ;
pri ntl n (
"Real-time clock resolution = " + t.toStringO);
C'l

ock. getRea'l timeCl ockO . getResol

System. out

Since timer resolution is hardware-dependent, you're likely to get different results


on different systems you run Java RTS on. When the code in Listing 7-1 is executed on SunFire v240, the accuracy output is: a

Real-time clock resolution = (0 ms, 200 ns)


Clearly, the timer resolution for Java RTS applications on this machine is 200
nanoseconds. Keep in mind that the system you run Java RTS on must provide
access to high-resolution timers, and the user account you execute your application with must have privilege to access the high-resolution timer implementation.

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

Real-time clock timestamp code

javax. realtime.Clock rtClock =


javax. realtime.Clock. getRealtimeClockO

javax. realtime.Absol uteTime begi nTime ;


j avax . real ti me . Absol uteTi me endTi me ;

get start time and convert to nanoseconds


= rtClock. getTimeO ;
long lBegin = (beg'inTime.getMillisecondsO o 1000000L)

//

begi nTim

rtt^/al I Cl ockTi meBefore . getNanoseconds O

//
//

do process'ing here..

gel. end time and convert to nanoseconds


endTift = rtClock. getTimeO ;
long lEnd = (beginTime.getMillisecondsO o 1000000L)
rtL\,al I Cl ockTi meBefore. getNanoseconds O ;

Tttr CtocxAPI

In addition to the

high-resolution timing shown

22s

in Listing 7-2, tluotgh

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

Figure 7-1. The Java RTS Clock class.

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

The Java RTS time operations.

CrutprnnT Tnr RmuTtun CrccxAPI

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

The Java RTS timer operations.

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.

Jovo RTS High-Resolution Time


Operotions
In Java RTS, real-time time objects are instances of either the Absol uteTi me or
RelativeTime classes, and associated with a singleton real-time clock. In fact,
we've already seen these classes used with setting the release parameters for

J u, RTS H rcn- Resotuno,t

Ttrun, O pnntnoN s

periodic Real ti

meTh read objects in Chapter 5. However, there are more details


to these classes that need to be examined.

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.

Absol uteTime(Absol uteTime)----creates a new object with the same starting


time as the given object.
Absol uteTime(Clock)-creates a new object associated with the given RTSJ
Cl ock object.

AbsouteTime(AbsoluteTime, Clock)-creates a new object with the same


starting time as the given object, and associated with the given RTSJ Clock
object.

AbsoluteTime(Date)-creates a new object with a starting time of Date.


getTi me.
AbsoluteTime(Date, Clock)-creates a new object with a stafing time of
Date. getTi me, and associated with the given RTSJ Cl ock object.
Absol uteTi me (l ong mi 1 I i s , i nt nanos)-creates a new object with a starting time represented by the given millisecond and nanosecond components.

AbsoluteTime(long millis, int nanos, clock)---creates a new object


with a starting time represented by the given millisecond and nanosecond components, and associated with the given RTSJ Cl ock object.

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:

RelativeTime(Iong mil I is,

int

nanos)-creates a new object that repre-

sents the interval based on the provided milliseconds parameter, plus the pro-

vided nanosecond parameter. In this case, the real-time clock is used by


default.

Re'lativeTime(long miI'lis, int nanos, clock)---creates a new object


that represents the interval based on the provided milliseconds parameter, plus

227

Cruprnn7

228

Tnn Rnr-Ttur CrccxAPI

the provided nanosecond parameter. In this case, the provided clock is used;
null, then the real-time clock is used by default.

if

Re'lativeTimeO----creates a new object with zeros for both the milliseconds


and nanoseconds parameter. The real-time clock is used by default.
Rel

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

from the one provided.

RelativeTime(RelativeTime, Clock)----creates a new object from the one


provided, using the clock provided.
Both Hi ghResol uti onTi me classes allow you to perform time arithmetic, where
you can add and subtract different time objects to derive new time objects. For
instance, the code in Listing 7-3 is a very easy way to determine the amount of
time a particular operation takes to execute.
Listing

7-3

Timing a real-time operation

ti me. * ;
public class Main {
public static void main(String[] args) {
// Create the objects beforehand; eliminates

i mport j avax . real

jitter

AbsoluteTime before;
AbsoluteTime after;
RelativeTime elapsed;

before = Clock.getReal timeClockO .getTimeO ;


// perform operation here...
after = Clock.getReal timeClockO .getTimeO ;
elapsed = after.subtract(before) ;
System.out.println("Elapsed time:

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

A Cotptrno SrocxDnt Fsr Ex*tptn

7-4

Listing

229

Jitter-free Date code

AbsoluteTime current = clock. getRealtimeclockO . getTimeO


Date tifi = current.getDateO;
System.out.println("Current date/t'ime: " + time) ;
Let's take
and Rel

look at more involved example of how to use both the Absol uteTime

ati veTi

me

classes.

A Confloled Stock Doto Feed Exomple


Let's examine a situation where we want to send conflated stock price data to client applications. The requirement is to gather all updates for a particular stock
ticker as they occur in real-time, but send them no less than ten milliseconds apart.
This means that for all updates that occur within each ten-millisecond period, the
updates must be added together to form one overall update that is sent to the clients (see Figure 7-4). However, if more than ten milliseconds elapses between
single updates, the update data gets transmitted immediately (there's no need to
delay any longer).

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:

One update (past conflation time)

Figure

7-4

Conflated stock ticker updates.

Csprnn7 Tan Rp,r-TtusCtocxAPI

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

The conflated stock update application

i mport javax. real

ti

me.

*;

ic class MyApp {
Object lock = new ObjectO;
double update = 0.00;
final RelativeTime PERI0D = nw RelativeTime(L0,0);

pubf

class Conflater extends RealtimeThread {


boolean updateOccured = false;
RelativeTime timeout = PERIOD;
AbsoluteT'ime startTime = null;
double pri ce = 0.00i

public void runO {

// ...

private vojd updatecl i ents(

double newPrice, RelativeTime lastUpdate)

// ...

class DataFeed extends RealtimeThread {


private Object privlock = new ObjectO;
pubf ic void runO {

//

L0ms

A Conrtrt SrocxDtr Fnto Exuptz,

231

// ...

private void send(int interval, double change) {

// ...

pub'lic MyAppO {
Conflater conflater =
DataFeed datafeed =

conflater. startO
datafeed. startO

w ConflaterO;
hw DataFeedo;

public static void main(String[] args) {


MYAPP PP

= 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.

The Confloter Closs


Let's take a look at class Conflater in more detail-see Listing 7-6-as that's
where the interesting code is.

Listing

7-6

The Conflater class

class Conflater extends RealtimeThread {


boolean updateOccured = false;
RelativeTime timeout = pERIOD;
AbsoluteTime startTime = null;
double pri ce = 0.00i
Clock rtClock = Clock.getRealtimeClockO

public void runO {

try

Walt to receive start pr-ice,


synchronized(lock){

//
]

and begin conflation

lock.waitO;

Ltinued

CrutprnnT

232

Tae

Rott-Tmr CucxAPI

Ihe first update is the starting price


price = update;
startTirx = FtClock. getTimeO ;
System. out . pri ntl n (
"Conflater received starting price") ;

//

// Now wa'it for updates, apply them, and send


// updated price to clients after conflated period
while(true){
synchroni zed ( lock ) {
// wait for update or period to expire
update = 0.00
Hi ghReso'l

utionTime.waitForobject(l ock, timeout)

AbsoluteTime current

rtClock.getTimeO;
RelativeTime elapsed =
cu r rent . subt ract (startTi

me)

// Check for update or timeout


if ( update != 0.OO ) {
// Apply update, and check time

updateoccured = true;
price += updti
System. out . pri ntl n (
"Conflater: update " + update +
", El apsed=" + el apsed) ;
timeout = PERIOD.subtract( elapsed

);

// First update since previous period


if ( elapsed.getMillisecondsO >
PERIOD. getMi I I

//

Send

update, start new conflation period

updateCl i ents(pri

i secondsO )

ce, e1 apsed) ;

startTiffi = rtClock. getTimeO

else {

/ Conflati on period expi red


if ( updateOccured )

//

Send

update, start new conflation period

ce, e1 apsed) ;
startTifi = FtClock. getT'imeo ;

updateCl i ents(pri

A C o,t p trno Srocx

Dm

F nno E xrup to

]
catch(Exceptione){
e . pri

ntstackTrace O ;

private void updateclients( double newprice,


RelativeTime lastUpdate) {
System.out.println("Conflater: Updating clients: ") ;
System.out.print1n("Conflater: price=" + newprice);
System.out.println("Conflater: Time since last:,' +
1

astUpdate)

Send update

//

Reset some values

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

tFor0bject. If an update has occurred, then a call is made to


updateCf ients, where all clients are given the new conflated stock price. Also,
the period start time is reset to the current time in preparation of the new conflation period. The updatecl i ents method also resets the timeout to be equal to the
full period, and resets the flag that indicates a data feed update has occurred. The
code is now ready to call wai tForObj ect again.
code again calls wai

The DotoFeed Closs


The DataFeed class is simple, but since it uses RelativeTime to create pauses
between updates (to simulate updates arriving aperiodically) let's explore it now
(see ListingT-7).
Listing

7-7

The DataFeed class

class DataFeed extends RealtimeThread {


private Object privlock = w ObjectO;

public void runO {

begin first period


send(O, L0.00); // starting price
send(2 , -.02);

//

send(1

send (1-

, -.0L);
,

.0L)

send(2, -.50) ;
send(4, O.0O);

// tust

sleep for

4ms

//

begin second period


send(12, -.11); // update well

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;

A Co,tpttrs,o SrocxDm, Fm Ex,lturpm

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

is received andthewai tForobject


loop begins. You can see in the output above that four data feed updates are conflated
into the first price update sent out to the simulated clients. The time elapsed when

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.