Safety-Critical Java (SCJ) is a Java profile designed for developing safety-critical real-time systems. It provides a framework for building applications that require deterministic behavior, bounded resource usage, and formal verification capabilities.
SCJ Overview and Levels
SCJ Compliance Levels
// SCJ defines three compliance levels:
// Level 0: No-heap real-time tasks
// Level 1: Single-threaded with immortal memory
// Level 2: Full-featured with scoped memory
public class SCJLevels {
// Level 0: Most restrictive, suitable for highest criticality
// Level 1: Adds immortal memory, single mission
// Level 2: Adds scoped memory, multiple missions
}
Project Setup and Dependencies
Maven Configuration for SCJ Development
<!-- pom.xml -->
<properties>
<javax.realtime.version>1.0.1</javax.realtime.version>
</properties>
<dependencies>
<!-- Real-Time Specification for Java (RTSJ) implementation -->
<dependency>
<groupId>javax.realtime</groupId>
<artifactId>realtime</artifactId>
<version>${javax.realtime.version}</version>
<scope>provided</scope>
</dependency>
<!-- Testing dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>11</source>
<target>11</target>
<compilerArgs>
<arg>-Xlint:unchecked</arg>
<arg>-Werror</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
SCJ Level 0 Implementation
Base Mission Framework
package scj.Level0;
import javax.realtime.*;
import javax.safetycritical.Mission;
import javax.safetycritical.Services;
/**
* SCJ Level 0 Mission base class
* No heap allocation allowed in Level 0
*/
public abstract class Level0Mission extends Mission {
private final PriorityParameters priority;
private final StorageParameters storage;
private final String missionName;
protected Level0Mission(String missionName, int priority, long stackSize) {
this.missionName = missionName;
this.priority = new PriorityParameters(priority);
this.storage = new StorageParameters(stackSize, null, 0, 0);
}
@Override
protected void initialize() {
Services.setCeiling(this, getInitialCeiling());
setupHandlers();
startPeriodicTasks();
}
@Override
protected void cleanup() {
// Cleanup resources in immortal memory
terminateTasks();
releaseResources();
}
@Override
public long missionMemorySize() {
return 1024 * 1024; // 1MB mission memory
}
protected abstract int getInitialCeiling();
protected abstract void setupHandlers();
protected abstract void startPeriodicTasks();
protected abstract void terminateTasks();
protected abstract void releaseResources();
// Getters
public PriorityParameters getPriority() { return priority; }
public StorageParameters getStorage() { return storage; }
public String getMissionName() { return missionName; }
}
Periodic Real-Time Task
package scj.Level0;
import javax.realtime.*;
import javax.safetycritical.PeriodicEventHandler;
import javax.safetycritical.StorageParameters;
/**
* SCJ Level 0 Periodic Event Handler
* No heap allocation allowed - all memory must be pre-allocated
*/
public abstract class Level0PeriodicHandler extends PeriodicEventHandler {
private final String handlerName;
private final RelativeTime period;
private final RelativeTime deadline;
private boolean initialized = false;
public Level0PeriodicHandler(
PriorityParameters priority,
PeriodicParameters period,
StorageParameters storage,
String name) {
super(priority, period, storage, name);
this.handlerName = name;
this.period = period.getPeriod();
this.deadline = period.getDeadline();
}
@Override
public void handleAsyncEvent() {
if (!initialized) {
initializeHandler();
initialized = true;
}
// Measure execution time for timing analysis
AbsoluteTime startTime = Clock.getRealtimeClock().getTime();
try {
// Execute the real-time task
executeTask();
// Check deadline compliance
AbsoluteTime endTime = Clock.getRealtimeClock().getTime();
RelativeTime executionTime = endTime.subtract(startTime);
if (deadline != null && executionTime.compareTo(deadline) > 0) {
handleDeadlineMiss(executionTime);
}
} catch (Throwable t) {
handleFault(t);
}
}
protected abstract void initializeHandler();
protected abstract void executeTask();
protected abstract void handleDeadlineMiss(RelativeTime executionTime);
protected abstract void handleFault(Throwable t);
// Utility methods for timing analysis
protected final void busyWait(RelativeTime duration) {
AbsoluteTime start = Clock.getRealtimeClock().getTime();
AbsoluteTime end = start.add(duration);
while (Clock.getRealtimeClock().getTime().compareTo(end) < 0) {
// Busy wait - appropriate for very short delays in SCJ
Thread.yield();
}
}
protected final boolean isWithinDeadline(AbsoluteTime start) {
if (deadline == null) return true;
AbsoluteTime current = Clock.getRealtimeClock().getTime();
RelativeTime elapsed = current.subtract(start);
return elapsed.compareTo(deadline) <= 0;
}
}
Aperiodic Event Handler
package scj.Level0;
import javax.realtime.*;
import javax.safetycritical.AperiodicEventHandler;
import javax.safetycritical.StorageParameters;
/**
* SCJ Level 0 Aperiodic Event Handler
* For handling sporadic events with minimum inter-arrival time
*/
public abstract class Level0AperiodicHandler extends AperiodicEventHandler {
private final String handlerName;
private final RelativeTime minInterArrival;
private long lastActivationTime = 0;
public Level0AperiodicHandler(
PriorityParameters priority,
AperiodicParameters release,
StorageParameters storage,
String name) {
super(priority, release, storage, name);
this.handlerName = name;
this.minInterArrival = release.getMinimumInterArrival();
}
@Override
public void handleAsyncEvent() {
long currentTime = System.nanoTime();
// Enforce minimum inter-arrival time
if (minInterArrival != null && lastActivationTime > 0) {
long timeSinceLastActivation = currentTime - lastActivationTime;
long minInterval = minInterArrival.getMilliseconds() * 1_000_000L;
if (timeSinceLastActivation < minInterval) {
handleArrivalViolation(timeSinceLastActivation, minInterval);
return;
}
}
lastActivationTime = currentTime;
try {
executeAperiodicTask();
} catch (Throwable t) {
handleAperiodicFault(t);
}
}
protected abstract void executeAperiodicTask();
protected abstract void handleArrivalViolation(long actualInterval, long minInterval);
protected abstract void handleAperiodicFault(Throwable t);
}
Aircraft Control System Example
Flight Control Mission (Level 0)
package scj.examples.aircraft;
import scj.Level0.Level0Mission;
import scj.Level0.Level0PeriodicHandler;
import javax.realtime.*;
import javax.safetycritical.Services;
import javax.safetycritical.StorageParameters;
/**
* SCJ Level 0 Flight Control System Mission
* Critical flight control tasks with hard real-time requirements
*/
public class FlightControlMission extends Level0Mission {
// Mission-specific constants
private static final int NUM_CONTROL_TASKS = 3;
private static final long CONTROL_STACK_SIZE = 16 * 1024; // 16KB per task
// Real-time task periods (in milliseconds)
private static final int ATTITUDE_CONTROL_PERIOD = 10; // 100Hz
private static final int ACTUATOR_CONTROL_PERIOD = 20; // 50Hz
private static final int SYSTEM_MONITOR_PERIOD = 100; // 10Hz
// Task priorities (higher number = higher priority)
private static final int ATTITUDE_CONTROL_PRIORITY = 30;
private static final int ACTUATOR_CONTROL_PRIORITY = 25;
private static final int SYSTEM_MONITOR_PRIORITY = 20;
// Memory regions for data sharing
private final FlightData flightData;
private final ControlSurfaces controlSurfaces;
public FlightControlMission() {
super("FlightControl", PriorityScheduler.instance().getMaxPriority(),
1024 * 1024); // 1MB mission memory
// Allocate mission data in immortal memory
this.flightData = new FlightData();
this.controlSurfaces = new ControlSurfaces();
}
@Override
protected int getInitialCeiling() {
return PriorityScheduler.instance().getMaxPriority();
}
@Override
protected void setupHandlers() {
// Setup fault handlers and monitoring
setupFaultHandlers();
setupHealthMonitoring();
}
@Override
protected void startPeriodicTasks() {
// Start all periodic control tasks
startAttitudeControlTask();
startActuatorControlTask();
startSystemMonitorTask();
}
@Override
protected void terminateTasks() {
// Safe termination of all tasks
flightData.setSystemActive(false);
}
@Override
protected void releaseResources() {
// Release any acquired resources
controlSurfaces.release();
}
private void startAttitudeControlTask() {
PriorityParameters priority = new PriorityParameters(ATTITUDE_CONTROL_PRIORITY);
PeriodicParameters period = new PeriodicParameters(
new RelativeTime(0, 0), // start immediately
new RelativeTime(ATTITUDE_CONTROL_PERIOD, 0), // period
new RelativeTime(ATTITUDE_CONTROL_PERIOD - 1, 0), // deadline
null, // no cost enforcement
null // no overrun handler
);
StorageParameters storage = new StorageParameters(
CONTROL_STACK_SIZE, null, 0, 0);
AttitudeControlHandler handler = new AttitudeControlHandler(
priority, period, storage, flightData, controlSurfaces);
handler.register();
}
private void startActuatorControlTask() {
PriorityParameters priority = new PriorityParameters(ACTUATOR_CONTROL_PRIORITY);
PeriodicParameters period = new PeriodicParameters(
new RelativeTime(0, 0),
new RelativeTime(ACTUATOR_CONTROL_PERIOD, 0),
new RelativeTime(ACTUATOR_CONTROL_PERIOD - 2, 0), // stricter deadline
null, null
);
StorageParameters storage = new StorageParameters(CONTROL_STACK_SIZE, null, 0, 0);
ActuatorControlHandler handler = new ActuatorControlHandler(
priority, period, storage, controlSurfaces);
handler.register();
}
private void startSystemMonitorTask() {
PriorityParameters priority = new PriorityParameters(SYSTEM_MONITOR_PRIORITY);
PeriodicParameters period = new PeriodicParameters(
new RelativeTime(0, 0),
new RelativeTime(SYSTEM_MONITOR_PERIOD, 0),
new RelativeTime(SYSTEM_MONITOR_PERIOD, 0), // same as period
null, null
);
StorageParameters storage = new StorageParameters(CONTROL_STACK_SIZE, null, 0, 0);
SystemMonitorHandler handler = new SystemMonitorHandler(
priority, period, storage, flightData);
handler.register();
}
private void setupFaultHandlers() {
// Setup fault detection and handling
// Implementation depends on specific hardware
}
private void setupHealthMonitoring() {
// Setup system health monitoring
// Implementation depends on system requirements
}
// Mission data classes (must be immutable or thread-safe)
public static final class FlightData {
private volatile double pitchAngle; // radians
private volatile double rollAngle; // radians
private volatile double yawAngle; // radians
private volatile double altitude; // meters
private volatile double airspeed; // m/s
private volatile boolean systemActive;
public FlightData() {
this.systemActive = true;
}
// Thread-safe getters and setters
public double getPitchAngle() { return pitchAngle; }
public void setPitchAngle(double pitchAngle) { this.pitchAngle = pitchAngle; }
public double getRollAngle() { return rollAngle; }
public void setRollAngle(double rollAngle) { this.rollAngle = rollAngle; }
public double getYawAngle() { return yawAngle; }
public void setYawAngle(double yawAngle) { this.yawAngle = yawAngle; }
public double getAltitude() { return altitude; }
public void setAltitude(double altitude) { this.altitude = altitude; }
public double getAirspeed() { return airspeed; }
public void setAirspeed(double airspeed) { this.airspeed = airspeed; }
public boolean isSystemActive() { return systemActive; }
public void setSystemActive(boolean systemActive) { this.systemActive = systemActive; }
}
public static final class ControlSurfaces {
private volatile double elevatorPosition; // radians
private volatile double aileronPosition; // radians
private volatile double rudderPosition; // radians
private volatile double throttlePosition; // 0.0 to 1.0
public void release() {
// Safe release of control surfaces
elevatorPosition = 0.0;
aileronPosition = 0.0;
rudderPosition = 0.0;
throttlePosition = 0.0;
}
// Thread-safe getters and setters
public double getElevatorPosition() { return elevatorPosition; }
public void setElevatorPosition(double elevatorPosition) {
this.elevatorPosition = elevatorPosition;
}
public double getAileronPosition() { return aileronPosition; }
public void setAileronPosition(double aileronPosition) {
this.aileronPosition = aileronPosition;
}
public double getRudderPosition() { return rudderPosition; }
public void setRudderPosition(double rudderPosition) {
this.rudderPosition = rudderPosition;
}
public double getThrottlePosition() { return throttlePosition; }
public void setThrottlePosition(double throttlePosition) {
this.throttlePosition = throttlePosition;
}
}
}
Attitude Control Handler
package scj.examples.aircraft;
import scj.Level0.Level0PeriodicHandler;
import javax.realtime.*;
import javax.safetycritical.StorageParameters;
/**
* Attitude Control Handler - Highest priority control task
* Maintains aircraft attitude (pitch, roll, yaw)
*/
public class AttitudeControlHandler extends Level0PeriodicHandler {
private final FlightControlMission.FlightData flightData;
private final FlightControlMission.ControlSurfaces controlSurfaces;
// Control gains (tuned for specific aircraft)
private final double PITCH_GAIN = 0.8;
private final double ROLL_GAIN = 0.7;
private final double YAW_GAIN = 0.6;
// Target attitudes (would come from flight management system)
private double targetPitch = 0.0;
private double targetRoll = 0.0;
private double targetYaw = 0.0;
// Control state
private double pitchIntegral = 0.0;
private double rollIntegral = 0.0;
private double yawIntegral = 0.0;
public AttitudeControlHandler(
PriorityParameters priority,
PeriodicParameters period,
StorageParameters storage,
FlightControlMission.FlightData flightData,
FlightControlMission.ControlSurfaces controlSurfaces) {
super(priority, period, storage, "AttitudeControl");
this.flightData = flightData;
this.controlSurfaces = controlSurfaces;
}
@Override
protected void initializeHandler() {
// Initialize control system
resetIntegrators();
calibrateSensors();
}
@Override
protected void executeTask() {
if (!flightData.isSystemActive()) {
return; // System shutdown requested
}
AbsoluteTime startTime = Clock.getRealtimeClock().getTime();
try {
// Read current attitude
double currentPitch = flightData.getPitchAngle();
double currentRoll = flightData.getRollAngle();
double currentYaw = flightData.getYawAngle();
// Compute control errors
double pitchError = targetPitch - currentPitch;
double rollError = targetRoll - currentRoll;
double yawError = targetYaw - currentYaw;
// Update integrators (simple PI control)
pitchIntegral += pitchError;
rollIntegral += rollError;
yawIntegral += yawError;
// Apply anti-windup
pitchIntegral = clamp(pitchIntegral, -1.0, 1.0);
rollIntegral = clamp(rollIntegral, -1.0, 1.0);
yawIntegral = clamp(yawIntegral, -1.0, 1.0);
// Compute control outputs
double elevatorCommand = PITCH_GAIN * (pitchError + 0.1 * pitchIntegral);
double aileronCommand = ROLL_GAIN * (rollError + 0.1 * rollIntegral);
double rudderCommand = YAW_GAIN * (yawError + 0.1 * yawIntegral);
// Apply control surface limits
elevatorCommand = clamp(elevatorCommand, -0.5, 0.5); // ±30 degrees
aileronCommand = clamp(aileronCommand, -0.5, 0.5);
rudderCommand = clamp(rudderCommand, -0.3, 0.3);
// Command control surfaces
controlSurfaces.setElevatorPosition(elevatorCommand);
controlSurfaces.setAileronPosition(aileronCommand);
controlSurfaces.setRudderPosition(rudderCommand);
// Check if we're still within deadline
if (!isWithinDeadline(startTime)) {
handleControlTimeout();
}
} catch (Exception e) {
handleControlFault(e);
}
}
@Override
protected void handleDeadlineMiss(RelativeTime executionTime) {
// Critical deadline miss - take emergency action
emergencyStabilization();
logDeadlineMiss(executionTime);
}
@Override
protected void handleFault(Throwable t) {
// Critical fault - initiate recovery or safe state
emergencyStabilization();
logFault(t);
}
private void resetIntegrators() {
pitchIntegral = 0.0;
rollIntegral = 0.0;
yawIntegral = 0.0;
}
private void calibrateSensors() {
// Sensor calibration routine
// Would interface with actual sensor hardware
}
private void emergencyStabilization() {
// Emergency stabilization procedure
controlSurfaces.setElevatorPosition(0.0);
controlSurfaces.setAileronPosition(0.0);
controlSurfaces.setRudderPosition(0.0);
}
private void handleControlTimeout() {
// Control computation took too long
emergencyStabilization();
}
private void handleControlFault(Exception e) {
// Control computation fault
emergencyStabilization();
}
private void logDeadlineMiss(RelativeTime executionTime) {
// Log deadline miss for analysis
// In real system, this would go to non-volatile storage
System.err.println("ATTITUDE_CONTROL deadline miss: " + executionTime);
}
private void logFault(Throwable t) {
// Log fault for analysis
System.err.println("ATTITUDE_CONTROL fault: " + t.getMessage());
}
private double clamp(double value, double min, double max) {
return Math.max(min, Math.min(max, value));
}
// Methods to update targets (would be called by higher-level systems)
public void setTargetPitch(double pitch) {
this.targetPitch = pitch;
}
public void setTargetRoll(double roll) {
this.targetRoll = roll;
}
public void setTargetYaw(double yaw) {
this.targetYaw = yaw;
}
}
System Monitor Handler
package scj.examples.aircraft;
import scj.Level0.Level0PeriodicHandler;
import javax.realtime.*;
import javax.safetycritical.StorageParameters;
/**
* System Monitor Handler - Monitors system health and performance
*/
public class SystemMonitorHandler extends Level0PeriodicHandler {
private final FlightControlMission.FlightData flightData;
// Monitoring thresholds
private static final double MAX_PITCH_ANGLE = Math.toRadians(45); // 45 degrees
private static final double MAX_ROLL_ANGLE = Math.toRadians(60); // 60 degrees
private static final double MIN_ALTITUDE = 100.0; // 100 meters
private static final double MAX_AIRSPEED = 300.0; // 300 m/s
// Watchdog counters
private int consecutiveFaults = 0;
private final int MAX_CONSECUTIVE_FAULTS = 3;
public SystemMonitorHandler(
PriorityParameters priority,
PeriodicParameters period,
StorageParameters storage,
FlightControlMission.FlightData flightData) {
super(priority, period, storage, "SystemMonitor");
this.flightData = flightData;
}
@Override
protected void initializeHandler() {
// Initialize monitoring system
resetWatchdog();
performStartupChecks();
}
@Override
protected void executeTask() {
AbsoluteTime startTime = Clock.getRealtimeClock().getTime();
try {
// Perform all system checks
boolean allChecksPassed = true;
allChecksPassed &= checkAttitudeLimits();
allChecksPassed &= checkAltitude();
allChecksPassed &= checkAirspeed();
allChecksPassed &= checkSystemState();
// Update watchdog
if (allChecksPassed) {
resetWatchdog();
} else {
handleSystemFault();
}
// Perform periodic system diagnostics
performDiagnostics();
} catch (Exception e) {
handleMonitoringFault(e);
}
}
@Override
protected void handleDeadlineMiss(RelativeTime executionTime) {
// Monitoring deadline miss - less critical but still important
logMonitoringMiss(executionTime);
}
@Override
protected void handleFault(Throwable t) {
// Monitoring system fault
logMonitoringFault(t);
}
private boolean checkAttitudeLimits() {
double pitch = Math.abs(flightData.getPitchAngle());
double roll = Math.abs(flightData.getRollAngle());
if (pitch > MAX_PITCH_ANGLE || roll > MAX_ROLL_ANGLE) {
handleAttitudeLimitExceeded(pitch, roll);
return false;
}
return true;
}
private boolean checkAltitude() {
double altitude = flightData.getAltitude();
if (altitude < MIN_ALTITUDE) {
handleLowAltitude(altitude);
return false;
}
return true;
}
private boolean checkAirspeed() {
double airspeed = flightData.getAirspeed();
if (airspeed > MAX_AIRSPEED) {
handleHighAirspeed(airspeed);
return false;
}
return true;
}
private boolean checkSystemState() {
// Check various system states
// This would interface with actual hardware monitoring
return true; // Simplified for example
}
private void handleAttitudeLimitExceeded(double pitch, double roll) {
consecutiveFaults++;
logLimitExceeded("ATTITUDE", pitch, roll);
if (consecutiveFaults >= MAX_CONSECUTIVE_FAULTS) {
initiateRecoveryProcedure();
}
}
private void handleLowAltitude(double altitude) {
consecutiveFaults++;
logLimitExceeded("ALTITUDE", altitude, MIN_ALTITUDE);
if (consecutiveFaults >= MAX_CONSECUTIVE_FAULTS) {
initiateTerrainAvoidance();
}
}
private void handleHighAirspeed(double airspeed) {
consecutiveFaults++;
logLimitExceeded("AIRSPEED", airspeed, MAX_AIRSPEED);
if (consecutiveFaults >= MAX_CONSECUTIVE_FAULTS) {
initiateSpeedReduction();
}
}
private void handleSystemFault() {
// General system fault handling
if (consecutiveFaults >= MAX_CONSECUTIVE_FAULTS) {
emergencyProcedures();
}
}
private void resetWatchdog() {
consecutiveFaults = 0;
}
private void performStartupChecks() {
// Perform comprehensive startup system checks
// This would verify all critical systems
}
private void performDiagnostics() {
// Perform periodic system diagnostics
// This would test various subsystems
}
private void initiateRecoveryProcedure() {
// Aircraft recovery procedure
// Would coordinate with other systems
}
private void initiateTerrainAvoidance() {
// Terrain avoidance procedure
}
private void initiateSpeedReduction() {
// Speed reduction procedure
}
private void emergencyProcedures() {
// General emergency procedures
flightData.setSystemActive(false);
}
private void logLimitExceeded(String parameter, double actual, double limit) {
System.err.println(parameter + " limit exceeded: " + actual + " > " + limit);
}
private void logMonitoringMiss(RelativeTime executionTime) {
System.err.println("SYSTEM_MONITOR deadline miss: " + executionTime);
}
private void logMonitoringFault(Throwable t) {
System.err.println("SYSTEM_MONITOR fault: " + t.getMessage());
}
}
SCJ Level 1 Implementation
Immortal Memory Management
package scj.Level1;
import javax.realtime.*;
import javax.safetycritical.Mission;
import javax.safetycritical.Services;
/**
* SCJ Level 1 Mission with Immortal Memory support
* Allows controlled heap usage for less critical components
*/
public abstract class Level1Mission extends Mission {
private final ImmortalMemory immortalMemory;
private final String missionName;
public Level1Mission(String missionName) {
this.missionName = missionName;
this.immortalMemory = ImmortalMemory.instance();
}
@Override
protected void initialize() {
// Execute initialization in immortal memory
ImmortalMemoryEnterer enterer = new ImmortalMemoryEnterer() {
@Override
public void run() {
initializeInImmortal();
}
};
enterer.run();
}
@Override
protected void cleanup() {
// Execute cleanup in immortal memory
ImmortalMemoryEnterer enterer = new ImmortalMemoryEnterer() {
@Override
public void run() {
cleanupInImmortal();
}
};
enterer.run();
}
/**
* Allocate object in immortal memory
*/
protected final <T> T allocateImmortal(Class<T> type) {
try {
return type.cast(immortalMemory.newInstance(type));
} catch (Exception e) {
throw new RuntimeException("Failed to allocate in immortal memory", e);
}
}
/**
* Execute code in immortal memory context
*/
protected final void executeInImmortal(Runnable task) {
ImmortalMemoryEnterer enterer = new ImmortalMemoryEnterer() {
@Override
public void run() {
task.run();
}
};
enterer.run();
}
protected abstract void initializeInImmortal();
protected abstract void cleanupInImmortal();
@Override
public long missionMemorySize() {
return 2 * 1024 * 1024; // 2MB for Level 1
}
}
Memory Management Utilities
Safe Memory Allocator
package scj.memory;
import javax.realtime.*;
import javax.safetycritical.Services;
/**
* Safe memory allocation utilities for SCJ applications
* Provides bounded memory allocation with overflow protection
*/
public final class SafeMemory {
private SafeMemory() {
// Utility class - no instantiation
}
/**
* Allocate array in scoped memory with bounds checking
*/
public static <T> T[] allocateArray(ScopedMemory scope, Class<T> type, int size) {
if (size <= 0) {
throw new IllegalArgumentException("Array size must be positive");
}
if (size > 10000) { // Application-specific limit
throw new IllegalArgumentException("Array size too large: " + size);
}
return (T[]) scope.newArray(type, size);
}
/**
* Allocate object in scoped memory with size validation
*/
public static <T> T allocateObject(ScopedMemory scope, Class<T> type) {
try {
T instance = scope.newInstance(type);
// Verify allocation succeeded
if (instance == null) {
throw new MemoryAccessError("Failed to allocate object of type: " + type.getName());
}
return instance;
} catch (InstantiationException e) {
throw new MemoryAccessError("Instantiation failed for: " + type.getName(), e);
} catch (IllegalAccessException e) {
throw new MemoryAccessError("Access denied for: " + type.getName(), e);
}
}
/**
* Check if remaining memory is sufficient for allocation
*/
public static boolean hasSufficientMemory(ScopedMemory scope, long requiredBytes) {
// This is a simplified check - actual implementation would depend on JVM
try {
// Try a test allocation
scope.newArray(byte.class, (int) Math.min(requiredBytes, 1024));
return true;
} catch (Throwable t) {
return false;
}
}
/**
* Memory region with automatic cleanup
*/
public static class ScopedRegion<T> implements Runnable {
private final ScopedMemory scope;
private T result;
private final Runnable task;
public ScopedRegion(ScopedMemory scope, Runnable task) {
this.scope = scope;
this.task = task;
}
@Override
public void run() {
task.run();
}
@SuppressWarnings("unchecked")
public T execute() {
scope.enter(this);
return result;
}
public void setResult(T result) {
this.result = result;
}
}
}
Testing Framework for SCJ
Deterministic Test Runner
package scj.testing;
import javax.realtime.*;
import java.util.concurrent.atomic.AtomicReference;
/**
* Testing framework for SCJ applications
* Provides deterministic testing of real-time behavior
*/
public class SCJTestRunner {
private final PriorityScheduler scheduler;
private final AtomicReference<Throwable> testFailure;
public SCJTestRunner() {
this.scheduler = PriorityScheduler.instance();
this.testFailure = new AtomicReference<>();
}
/**
* Run test with real-time constraints
*/
public void runTest(SCJTestCase testCase, RelativeTime timeout) {
AbsoluteTime startTime = Clock.getRealtimeClock().getTime();
AbsoluteTime endTime = startTime.add(timeout);
// Create real-time thread for test execution
PriorityParameters priority = new PriorityParameters(
scheduler.getNormPriority());
PeriodicParameters timing = new PeriodicParameters(
new RelativeTime(0, 0), // start immediately
null, // no period for one-shot test
timeout, // test timeout
null, null);
TestRunner runner = new TestRunner(priority, timing, testCase);
try {
runner.start();
runner.join();
// Check for test failures
Throwable failure = testFailure.get();
if (failure != null) {
if (failure instanceof AssertionError) {
throw (AssertionError) failure;
} else {
throw new RuntimeException("Test failed", failure);
}
}
// Check for timeout
if (Clock.getRealtimeClock().getTime().compareTo(endTime) > 0) {
throw new RuntimeException("Test timed out");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Test interrupted", e);
}
}
/**
* SCJ Test Case interface
*/
public interface SCJTestCase {
void runTest() throws Exception;
}
/**
* Real-time thread for test execution
*/
private class TestRunner extends RealtimeThread {
private final SCJTestCase testCase;
public TestRunner(PriorityParameters priority, PeriodicParameters timing,
SCJTestCase testCase) {
super(priority, timing);
this.testCase = testCase;
}
@Override
public void run() {
try {
testCase.runTest();
} catch (Throwable t) {
testFailure.set(t);
}
}
}
/**
* Test utilities
*/
public static class Assert {
public static void assertTrue(boolean condition, String message) {
if (!condition) {
throw new AssertionError(message);
}
}
public static void assertFalse(boolean condition, String message) {
assertTrue(!condition, message);
}
public static void assertEquals(Object expected, Object actual, String message) {
if (expected == null ? actual != null : !expected.equals(actual)) {
throw new AssertionError(message + " - expected: " + expected + ", actual: " + actual);
}
}
public static void assertWithinTolerance(double expected, double actual,
double tolerance, String message) {
if (Math.abs(expected - actual) > tolerance) {
throw new AssertionError(message + " - expected: " + expected +
", actual: " + actual + ", tolerance: " + tolerance);
}
}
public static void assertDeadlineMet(RelativeTime maxDuration) {
RealtimeThread current = RealtimeThread.currentRealtimeThread();
if (current instanceof PeriodicParameters) {
RelativeTime deadline = ((PeriodicParameters) current.getReleaseParameters()).getDeadline();
if (deadline != null) {
AbsoluteTime start = current.getStartTime();
AbsoluteTime now = Clock.getRealtimeClock().getTime();
RelativeTime elapsed = now.subtract(start);
if (elapsed.compareTo(deadline) > 0) {
throw new AssertionError("Deadline missed - elapsed: " +
elapsed + ", deadline: " + deadline);
}
}
}
}
}
}
Build and Deployment
SCJ-Specific Build Configuration
<!-- Maven profile for SCJ compilation --> <profiles> <profile> <id>scj</id> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>11</source> <target>11</target> <compilerArgs> <!-- SCJ-specific compiler flags --> <arg>-Xlint:scj</arg> <arg>-Werror</arg> <arg>-Xmx64m</arg> <!-- Disable heap operations for Level 0 --> <arg>-Xnoheap</arg> </compilerArgs> </configuration> </plugin> <!-- Static analysis for SCJ --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>findbugs-maven-plugin</artifactId> <version>3.0.5</version> <configuration> <effort>Max</effort> <threshold>Low</threshold> <failOnError>true</failOnError> </configuration> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </profile> </profiles>
Conclusion
This comprehensive Safety-Critical Java implementation provides:
Key SCJ Features:
- Deterministic real-time behavior with bounded execution times
- Memory safety through scoped and immortal memory regions
- Temporal safety with deadline monitoring and enforcement
- Fault containment through proper exception handling
- Formal verification support through restricted programming model
Safety-Critical Patterns:
- Cyclic Executive Pattern for periodic control tasks
- Watchdog Pattern for system health monitoring
- Safety Bag Pattern for fault detection and recovery
- Heartbeat Pattern for task liveness verification
Certification Support:
- DO-178C (Aviation)
- IEC 61508 (Industrial)
- ISO 26262 (Automotive)
Best Practices:
- Minimal heap usage in Level 0 implementations
- Bounded algorithms with predictable execution times
- Defensive programming with comprehensive error handling
- Timing analysis for worst-case execution time (WCET)
- Formal methods for critical algorithm verification
This implementation demonstrates how to build high-integrity, safety-critical systems using Java while maintaining the productivity benefits of the Java ecosystem.