RealtimeThread
and its subclasses and AsyncEventHandler
and its subclasses.Timely execution of schedulable objects means that the programmer can determine by analysis of the program, testing the program on particular implementations, or both whether particular threads will always complete execution before a given timeliness constraint. This is the essence of real-time programming: the addition of temporal constraints to the correctness conditions for computation. For example, for a program to compute the sum of two numbers it may no longer be acceptable to compute only the correct arithmetic answer but the answer must be computed before a particular time. Typically, temporal constraints are deadlines expressed in either relative or absolute time.
We use the term scheduling (or scheduling algorithm) to refer to the production of a sequence (or ordering) for the execution of a set of schedulable objects (a schedule). This schedule attempts to optimize a particular metric (a metric that measures how well the system is meeting the temporal constraints). A feasibility analysis determines if a schedule has an acceptable value for the metric. For example, in hard real-time systems the typical metric is "number of missed deadlines" and the only acceptable value for that metric is zero. So-called soft real-time systems use other metrics (such as mean tardiness) and may accept various values for the metric in use.
Many systems use thread priority in an attempt to determine a schedule. Priority is typically an integer associated with a schedulable object; these integers convey to the system the order in which the threads should execute. The generalization of the concept of priority is execution eligibility. We use the term dispatching to refer to that portion of the system which selects the thread with the highest execution eligibility from the pool of threads that are ready to run. In current real-time system practice, the assignment of priorities is typically under programmer control as opposed to under system control. The RTSJ's base scheduler also leaves the assignment of priorities under programmer control. However, the base scheduler also inherits methods from its superclass that may help determine feasibility.
For the base scheduler the feasibility methods may assume a sufficiently fast processor to complete any proposed load on schedule. The RTSJ expects that the base scheduler may be subclassed in particular implementations (e.g., an EDF scheduler) and for those implementations the feasibility methods may correctly indicate the actual feasibility of the system under the given scheduler. Note that for the base scheduler the RTSJ is no different than most real-time operating systems in current use.
The RTSJ requires a number of classes with names of the format <string>Parameters (such as SchedulingParameters). An instance of one of these parameter classes holds a particular resource-demand characteristic for one or more schedulable objects. For example, the PriorityParameters subclass of SchedulingParameters contains the execution eligibility metric of the base scheduler, i.e., priority. At some time (construction-time or later when the parameters are replaced using setter methods), instances of parameter classes are bound to a schedulable object. The schedulable object then assumes the characteristics of the values in the parameter object. For example, if a PriorityParameters instance that had in its priority field the value representing the highest priority available is bound to a schedulable object, then that object will assume the characteristic that it will execute whenever it is ready in preference to all other schedulable objects (except, of course, those also with the highest priority).
The RTSJ is written so as to allow implementers the flexibility to install arbitrary scheduling algorithms and feasibility analysis algorithms in an implementation of the specification. We do this because the RTJEG understands that the real-time systems industry has widely varying requirements with respect to scheduling. Use of the Java platform may help produce code written once but able to execute on many different computing platforms (known as Write Once, Run Anywhere.) The RTSJ both contributes to this goal and detracts from it. The RTSJ's rigorous specification of the required priority scheduler is critical for portability of time-critical code, but the RTSJ permits and supports platform-specific schedulers which are not portable.
There are four basic types of memory areas:
The contents of a scoped memory are discarded when no object in the scope can be referenced. This is done by a technique similar to reference counting the scope. A conformant implementation might maintain a count of the number of external references to each memory area. The reference count for a ScopedMemory area would be increased by entering a new scope through the enter() method of MemoryArea, by the creation of a schedulable object using the particular ScopedMemory area, or by the opening of an inner scope. The reference count for a ScopedMemory area would be decreased when returning from the enter() method, when the schedulable object using the ScopedMemory terminates, or when an inner scope returns from its enter() method. When the count drops to zero, the finalize method for each object in the memory would be executed to completion. Reuse of the scope is blocked until finalization is complete.
Scopes may be nested. When a nested scope is entered, all subsequent allocations are taken from the memory associated with the new scope. When the nested scope is exited, the previous scope is restored and subsequent allocations are again taken from that scope.
Because of the lifetimes of scoped objects, it is necessary to limit the references to scoped objects, by means of a restricted set of assignment rules. A reference to a scoped object cannot be assigned to a variable from an outer scope, or to a field of an object in either the heap or the immortal area. A reference to a scoped object may only be assigned into the same scope or into an inner scope. The virtual machine must detect illegal assignment attempts and must throw an appropriate exception when they occur.
The flexibility provided in choice of scoped memory types allows the application to use a memory area that has characteristics that are appropriate to a particular syntactically defined region of the code.
The specification also provides a mechanism by which the programmer can override the default system-wide policy, or control the policy to be used for a particular monitor, provided that policy is supported by the implementation. The monitor control policy specification is extensible so that new mechanisms can be added by future implementations.
A second policy, priority ceiling emulation protocol (or highest locker protocol), is also specified for systems that support it. This protocol is also a well-known algorithm in the literature; somewhat simplified, its effect is as follows:
Note that while the RTSJ requires that the execution of non-heap schedulable objects must not be delayed by garbage collection on behalf of lower-priority schedulable objects, an application can cause a no-heap schedulable object to wait for garbage collection by synchronizing using an object between an heap-using thread or schedulable object and a non-heap schedulable object. The RTSJ provides wait-free queue classes to provide protected, non-blocking, shared access to objects accessed by both regular Java threads and no-heap real-time threads. These classes are provided explicitly to enable communication between the real-time execution of non-heap schedulable objects and regular Java threads or heap-using schedulable objects.
fire()
method being called, the associated instances of AsyncEventHandler
are scheduled and the handleAsyncEvent()
methods are invoked, thus the required logic is performed. Also, methods on AsyncEvent
are provided to manage the set of instances of AsyncEventHandler
associated with the instance of AsyncEvent
.
An instance of AsyncEventHandler can be thought of as something similar to a thread. It is a Runnable object: when the event fires, the associated handlers are scheduled and the handleAsyncEvent()
methods are invoked. What distinguishes an AsyncEventHandler from a simple Runnable is that an AsyncEventHandler has associated instances of ReleaseParameters, SchedulingParameters and MemoryParameters that control the actual execution of the handler once the associated AsyncEvent is fired. When an event is fired, the handlers are executed asynchronously, scheduled according to the associated ReleaseParameters and SchedulingParameters objects, in a manner that looks like the handler has just been assigned to its own thread. It is intended that the system can cope well with situations where there are large numbers of instances of AsyncEvent and AsyncEventHandler (tens of thousands). The number of fired (in process) handlers is expected to be smaller.
A specialized form of an AsyncEvent is the Timer class, which represents an event whose occurrence is driven by time. There are two forms of Timers: the OneShotTimer and the PeriodicTimer. Instances of OneShotTimer fire once, at the specified time. Periodic timers fire initially at the specified time, and then periodically according to a specified interval.
Timers are driven by Clock objects. There is a special Clock object, Clock.getRealtimeClock(), that represents the real-time clock. The Clock class may be extended to represent other clocks the underlying system might make available (such as a execution time clock of some granularity).
The RTSJ's approach to ATC is based on several guiding principles, informally outlined in the following lists.
Earlier versions of the Java language supplied mechanisms for achieving these effects: in particular the methods stop() and destroy() in class Thread. However, since stop() could leave shared objects in an inconsistent state, stop() has been deprecated. The use of destroy() can lead to deadlock (if a thread is destroyed while it is holding a lock) and although it was not deprecated until version 1.5 of the Java specification, its usage has long been discouraged. A goal of the RTSJ was to meet the requirements of asynchronous thread termination without introducing the dangers of the stop() or destroy() methods.
The RTSJ accommodates safe asynchronous real-time thread termination through a combination of the asynchronous event handling and the asynchronous transfer of control mechanisms. To create such a set of real-time threads consider the following steps:
interrupt()
on each of the real-time threads affected by the changeinterrupt()
have them create a new set of real-time threads appropriate to the current state of the external worldrun()
method of the real-time thread will complete normally.
This idiom provides a quick (if coded to be so) but orderly clean up and termination of the real-time thread. Note that the oracle can comprise as many or as few asynchronous event handlers as appropriate.
RawMemoryAccess
defines methods that allow the programmer to construct an object that represents a range of physical addresses. Access to the physical memory is then accomplished through get[type]()
and set[type]()
methods of that object where the type represents a word size, i.e., byte, short, int, long, float, and double. No semantics other than the set[type]()
and get[type]()
methods are implied. The VTPhysicalMemory, LTPhysicalMemory,
and ImmortalPhysicalMemory
classes allow programmers to construct an object that represents a range of physical memory addresses. When this object is used as a MemoryArea
other objects can be constructed in the physical memory using the new keyword as appropriate.
The PhysicalMemoryManager
is available for use by the various physical memory accessor objects (VTPhysicalMemory, LTPhysicalMemory, ImmortalPhysicalMemory, RawMemoryAccess,
and RawMemoryFloatAccess)
to create objects of the correct type that are bound to areas of physical memory with the appropriate characteristics - or with appropriate accessor behavior. Examples of characteristics that might be specified are: DMA memory, accessors with byte swapping, etc. OEMs may provide PhysicalMemoryTypeFilter
classes that allow additional characteristics of memory devices to be specified.
RawMemoryAccess
models a range of physical memory as a fixed sequence of bytes. A full complement of accessor methods allow the contents of the physical area to be accessed through offsets from the base, interpreted as byte, short, int, or long data values or as arrays of these types.
Whether the offset specifies the most-significant or least-significant byte of a multibyte value is affected by the BYTE_ORDER static variable in class RealtimeSystem
, possibly amended by a byte swapping attribute associated with the underlying physical memory type.
The RawMemoryAccess
class allows a real-time program to implement device drivers, memory-mapped I/O, flash memory, battery-backed RAM, and similar low-level software.
A raw memory area cannot contain references to Java objects. Such a capability would be unsafe (since it could be used to defeat Java's type checking) and error prone (since it is sensitive to the specific representational choices made by the Java compiler).
VTPhysicalMemory, LTPhysicalMemory
, and ImmortalPhysicalMemory
classes allow the programmer this flexibility. The programmer would construct a physical memory object on the memory addresses occupied by the fast RAM.