|
Safe API Framework
Layered API framework for safety-related applications (ERTMS RBC reference targeting CENELEC EN 50128 SIL 4)
|
Accepted
safeAPIRBC2oo2GP's process startup (app_main_common.c, both the AB and C variants) called sapi_posix_backend_init_realtime(80U) directly - a symbol exported by safeAPIBackendPosix, not by safeAPIFreamwork. That is a layering violation: ADR-001 ยง3.5 and ADR-005 require application code to reach the OS only through the framework's OAL API, with the single exception of the composition-root wiring that registers a concrete backend (sapi_*_register_backend()). sapi_posix_backend_init_realtime() is not registration - it is behaviour (POSIX mlockall() + SCHED_FIFO) the application was invoking by reaching around the framework, and there was no OAL API for it to use instead.
It also hid a real bug. The POSIX implementation called mlockall(MCL_CURRENT | MCL_FUTURE) and treated only failure of that call as noteworthy. On a host with a bounded RLIMIT_MEMLOCK (a stock Ubuntu VM: 8 MiB soft/hard cap) the call succeeds, and MCL_FUTURE then forces every subsequent mapping - including each new 8 MiB thread stack - to be locked. The next pthread_create() fails with EAGAIN, so sapi_timer's backend cannot start its cyclic-timer thread, sapi_appmanager init fails with INTERNAL_ERROR, and every RBC process exits at startup. 13 of 16 end-to-end scenario tests failed as a result, all with the same downstream symptom (no RBC indications ever produced).
A minimal validate-then-dispatch service in the same shape as sapi_reboot (ADR-004/005) and split consumer/backend headers per ADR-021:
The name is platform, not realtime, so the one service can absorb other one-shot platform bring-up concerns later without another ADR; the only operation today is realtime_init.
realtime_init is defined as best-effort (REQ-OAL-PLATFORM-001): a backend that cannot get RT scheduling or full memory residency still returns SAPI_STATUS_OK - and, critically, must not leave the process unable to create threads afterwards. That last clause is what the POSIX backend's old code violated.
The RLIMIT_MEMLOCK fix: before mlockall, the backend now calls getrlimit(RLIMIT_MEMLOCK). If the limit is not RLIM_INFINITY, it locks with MCL_CURRENT only (never MCL_FUTURE) and logs a WARNING explaining why. A privileged / memlock=unlimited deployment is unaffected and still gets MCL_CURRENT | MCL_FUTURE; an unprivileged dev/CI host keeps its current footprint resident but no longer poisons later thread creation.
app_main_common.c (AB and C) replaces sapi_posix_backend_init_realtime(80U) with sapi_platform_realtime_init(80U) and includes safeapi/oal/platform/sapi_platform.h. It still includes sapi_posix_backend.h for sapi_posix_backend_register_all() and sapi_posix_backend_reboot_set_argv() - those are the sanctioned composition-root wiring; the RT call no longer is.