It happened just like that. In the middle of a conversation, he got a kind of misty look in his eyes, like something wasn’t quite right. His breathing became more labored, he hunched forward a bit, and the next thing you knew, he was in full heart attack mode. An ambulance was quickly called for; this is where seconds count. As the ambulance was en route, efforts were made to clear the way for the EMTs so that they could get to work as quickly as possible. The main door was propped open, and an attempt was made to reserve the spot in front for the ambulance. But just as the ambulance was getting close, a garbage truck came by and blocked access. The garbage men casually jumped down from the truck and started collecting the garbage. Attempts to get them to back off even for a moment were in vain; they were scheduled to collect the garbage, and by George, that’s what they were gonna do. The ambulance would just have to wait.
And this is why standard old off-the-shelf Java isn’t used in real-time or safety-critical applications. While its more restrictive design, as compared to C or C++, actually helps system reliability in many respects, reducing the chances for system failure, it’s got a few unpredictable, non-deterministic characteristics that just won’t fly. Literally.
There are, broadly speaking, three levels of criticality for system operation. Most software we overtly use is at the least critical level, as is evident each time we try to access a website and the request just doesn’t go through; no explanations, no apologies. Or each time our computer crashes for no good reason. (OK, purists might say there’s never a good reason for a computer to crash… I could be persuaded…). Way down deep under the hood, the operating system is managing events the best it can (a relative concept), and things happen when they happen, for better or for worse.
The next level of criticality is real-time: it matters when things occur. Here events have to happen at a specific time or with a specific delay or latency. This requires more discipline on the part of the OS. While we hardly ever deal with this kind of system directly, we might do so through the use of systems with embedded processors employing a real-time operating system (RTOS).
The last level of criticality is called safety-critical. This is for systems whose failure could endanger lives. Things simply must never go wrong; everything must be thought through, every nook and cranny of the software must be statically testable, and code must be traceable from source all the way to object code.
You might say that at the lowest level, nothing (much) is at stake; at the real-time level, important system functionality is at stake, and at the safety-critical level, lives are at stake.
So you might well imagine that standard vanilla Java – the software, not some fru-fru wannabe coffee drink – will have some trouble with both real-time and safety-critical applications. When it comes to deterministic behavior, garbage collection (GC) is generally exhibit A in The Impassive People In Black Suits vs. The Java Developers In Cutoffs. Because its onset can’t be predicted and may not be the same from run to run, it can’t be used for real-time operation.
And for traceability, well, things are even worse. The standard Java environment compiles source code to byte code and then executes the byte code on a Virtual Machine (VM). During execution, classes can be loaded dynamically – and through the use of agents, those classes can even be instrumented in real time, completely changing the behavior of the program. This absolutely runs afoul of the traceability requirements that say that every step of the reduction of code from source to object must be traceable. In fact, with standard Java, that reduction process doesn’t complete until run time, and it is done entirely out of the control of the program developer — not the kind of thing that would give you a warm fuzzy feeling if your life depended on it.
One of the principal areas where these requirements are found is in avionics. In fact, contrary to what you might expect, commercial avionics requirements are stricter than defense requirements (although in reality, defense systems often use the commercial grade – if for no other reason that a military plane using a commercial airport or airspace must meet commercial requirements). Significant efforts are underway to modernize the software and systems in cockpits — notable in this effort is the development of an Integrated Modular Avionics (IMA) system — while meeting the requirements of the ARINC 653 standard.
ARINC 653 specifies the partitioning of systems, allowing multiple applications per processor. Memory can also be allocated among applications. The different partitions can be assigned different safety ratings so that a failure in a less-critical module won’t cause a hiccup in a more critical one. The DIANA project (Distributed equipment Independent environment for Advanced avioNic Applications – sheesh, the things people will do to name a project Diana… just Google “diana project” and see what comes up) in Europe is a relatively visible effort to use this and other technology to prove that such a system can be built with off-the-shelf layered software.
Defense and aerospace originally looked to the Ada language to avoid the million-and-one ways C++ code can be crashed. (Come on, we’ve all had pointers access address zero, right? Haven’t you? Isn’t it a rite of passage?) Problem is, Ada has a limited market, so there’s not much in the way of slick new toolage for development. Worse yet, Ada has been branded with the dreaded For Defense Only reputation, meaning few developers want to go there, and the pool of talent is therefore small.
So Java enters the picture, but now with a couple variants. First is the Real Time Specification for Java (RTSJ). This allows Java applications to operate with deterministic timing by making changes in the areas of scheduling (a scheduler is required); memory management (GC has to be characterizable – and developers can explicitly control allocation and release); shared resource management; asynchronous events, transfers of control, and thread termination; and access to physical memory (as the spec notes, not strictly a real-time issue, but something real-time systems often need, so it’s kind of a bonus).
The second Java variant, intended for safety-critical applications, is a further restriction of RTSJ. It’s being standardized as JSR-302, and this process is still underway. Among other things, it further restricts heap usage and provides a final compiler to bring byte code to object level in a traceable manner. It also dispenses with what some consider to be the “kitchen sink” approach to RTSJ. One example cited was the ability to ask for notification when a deadline is missed: this is complex both for checking the deadline and for exception handling; it, as well as many of the other complexities of RTSJ, is proposed to be eliminated in the safety-critical version (“proposed” because it’s not a standard yet, and it ain’t over till it’s over).
One of the more visible players in the real-time and safety-critical arenas is Aonix, which sells Java environments and development tools for use in embedded applications. They are on the JSR-302 Experts Group and have clearly articulated support for the emerging elements of safety-critical Java as an area of focus.
They have three levels of Java product to meet the three different levels of criticality mapped out above. Their standard lowest-criticality product is PERC Ultra, and it’s been on the market for many years. PERC Pico, recently announced, adds real-time capabilities and is also taking many of its cues from the ongoing JSR-302 work, even though it isn’t officially certified as safety critical. It relies much more on stack usage than the heap and includes a memory analysis capability that allows programmers to evaluate the impact of changes to code on what are usually limited memory resources without the standard test-and-debug approach. PERC Pico will be used in the DIANA project as a fundamental execution platform, largely due to the elements of JSR-302 that it enables.
PERC Pico can be used alongside PERC Ultra in an application where, for example, the bulk of the application uses PERC Ultra, but drivers are written using PERC Pico. This eliminates the need to use the Java Native Interface, with low-level code written in C; everything can be Java. PERC Pico can also be used for those rare applications that will operate on “bare metal” – that is, without any operating system at all. Because the Java code can be compiled all the way down to object code, there is no intrinsic requirement for a VM or an OS.
Once the safety-critical Java standardization work settles out, PERC Raven will be certified for safety-critical applications that require all of the bureaucracy safety controls necessary for keeping us in the air when we fly. Or for keeping that life support equipment working after a heart attack, without garbage collection getting in the way.