|
Safe API Framework
Layered API framework for safety-related applications (ERTMS RBC reference targeting CENELEC EN 50128 SIL 4)
|
Accepted
safeAPIRBC2oo2 (the reference application built on this framework) guards a netlink handle shared between its own cyclic-executive thread and a background relay-rx task with a mutex (ctx->peer_send_mutex, used by channel_ab_checkpoint.c's own checkpoint send/recv adapter and channel_ab_io.c's own peer-sample/relay-envelope/site-state sends). Found live, during an architecture review of every "backend"-named symbol the application uses: that mutex was declared as a raw pthread_mutex_t and manipulated with pthread_mutex_init()/_lock()/ _unlock()/_destroy() directly, with <pthread.h> included straight into an application header (channel_ab_types.h), not confined to a POSIX-backend implementation file.
This violates this project's own foundational premise (ADR-001/ADR-005): an application built on safeAPIFreamwork should depend only on this framework's own OS-Abstraction-Layer API, never on a specific platform's threading primitives directly - the whole point of the backend-dispatch pattern every other OAL service (sapi_timer, sapi_task, sapi_ipc, sapi_netlink, ...) already follows is that swapping the platform/RTOS underneath an application should mean swapping one backend registration call, not auditing every application source file for platform-specific API usage. A pthread_mutex_t baked into an application-level struct defeats that guarantee outright: porting this application to a target with no pthreads (a bare-metal RTOS, for instance) would require rewriting application code, not just registering a different backend.
Checked before deciding on a fix: does safeAPIFreamwork already expose any portable synchronization primitive under a different name (a critical-section API bundled into sapi_task, for instance)? It does not - the OAL surface (clocksync/ipc/log/memory/netlink/nvm/ reboot/task/timer) has no mutex/lock/semaphore module at all. So this is not "the application should have called an existing framework function instead" - the framework itself was missing the primitive, the same gap-class ADR-031 (sapi_mem_util, for <string.h>) and ADR-026/032 (sapi_lifecycle, for setup-phase gating) already closed this project's history.
Add sapi_mutex as a new OAL-layer service, following the exact same consumer/backend split every other OAL service uses (ADR-021):
The POSIX backend implementation (sapi_posix_backend_mutex.c, a thin wrapper over pthread_mutex_init()/_lock()/_unlock()/_destroy()) lives in safeAPIRBC2oo2's own src/posix_backend/, not in safeAPIFreamwork - matching every other OAL backend's location per ADR-018's own "a backend is integrator-supplied, not part of the reusable framework" philosophy. This is exactly where pthread_mutex_t usage belongs: confined to the one file whose entire job is adapting a specific platform to this framework's portable API, never leaking into application code above it.
safeAPIRBC2oo2's own channel_ab_types.h/channel_ab.c/ channel_ab_checkpoint.c/channel_ab_io.c were migrated to sapi_mutex_handle_t + sapi_mutex_storage_t, dropping <pthread.h> from application code entirely (the one remaining pthread_join() use inside sapi_task_destroy()'s own doc-comment cross-reference is a framework-internal detail the application only reads about, not calls directly).