OS abstraction layer providing unified C and C++ APIs for common OS and RTOS primitives. Architecture is organized around a portable C layer with an optional C++ wrapper for RAII and object-oriented use.
Main features:
- dual-layer API: thin C API (
osal::c) for direct use, and C++ RAII wrappers (osal::cpp) built on top of it, - ISR-safe variants: dedicated
*Isr()operations for mutex and semaphore use from interrupt context, - threads: create, join, yield, and prioritize with a 5-level priority system,
- synchronization: mutexes (recursive and non-recursive), semaphores, and scoped locks — all with optional timeouts,
- time and sleep: timestamp functions, time unit conversions, and
std::chrono-based sleep.
OSAL_PLATFORM |
Backend details |
|---|---|
linux |
Linux backend using POSIX API |
freertos |
FreeRTOS backend using FreeRTOS API |
Backend selection is controlled at build time with OSAL_PLATFORM. The architecture is designed to accommodate
additional backends over time.
Important
osal requires the target project to use CMake.
-
osal— C API layer and platform backends. Exposes primitives for:- threads,
- mutexes,
- semaphores,
- sleep,
- time/timestamp utilities.
-
cpp— C++ wrapper layer built on top of the C API. Adds RAII and object-oriented abstractions.
The diagram below illustrates the layering. The C++ API is a thin wrapper over the C API. The C API delegates all platform-specific work to the selected backend:
classDiagram
namespace osal_cpp {
class Thread {
+start(function) error_code
+join() error_code
}
}
namespace osal_c {
class OsalThread {
+ThreadImpl impl
}
}
namespace linux {
class LinuxThreadImpl["ThreadImpl"] {
+pthread_t handle
}
}
namespace freertos {
class FreeRTOSThreadImpl["ThreadImpl"] {
+TaskHandle_t handle
}
}
Thread *-- OsalThread : contains
OsalThread ..> LinuxThreadImpl : OSAL_PLATFORM=linux
OsalThread ..> FreeRTOSThreadImpl : OSAL_PLATFORM=freertos
- Language: C++23, C17
- Build System: CMake (minimum version 3.28)
- Package Manager: Conan (test dependencies only)
- Static Analysis: clang-format, clang-tidy
- CI/CD: GitHub Actions
osal follows the standard kubasejdak-org repository layout for C++ libraries:
osal/
├── cmake/ # CMake build system
│ ├── compilation-flags.cmake # Internal compilation flags
│ ├── components.cmake # Helper component loader (FetchContent helper)
│ ├── modules/ # CMake Find*.cmake modules for dependencies
│ └── presets/ # Internal preset helpers
├── lib/ # Core components
│ ├── osal/ # C API (osal::c) and platform backends
│ │ ├── common/ # Platform-independent implementation (osal::common)
│ │ ├── linux/ # Linux backend (pthread)
│ │ └── freertos/ # FreeRTOS backend
│ └── cpp/ # C++ wrapper API (osal::cpp)
├── tests/ # Test suite (Catch2)
├── tools/ # Development tools and scripts
├── .devcontainer/ # Dev container configuration
├── .github/workflows/ # GitHub Actions workflows
└── CMakePresets.json # Development CMake presetsCreate a Findosal.cmake module (typically in cmake/modules directory):
include(FetchContent)
FetchContent_Declare(osal
GIT_REPOSITORY https://github.com/kubasejdak-org/osal.git
GIT_TAG <commit-sha|branch|tag>
)
FetchContent_MakeAvailable(osal)
include(${osal_SOURCE_DIR}/cmake/components.cmake)Note
GIT_TAG accepts any ref recognized by CMake FetchContent: a full commit SHA, a branch name, or a tag.
Then add the module directory to the CMake search path and request the library:
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
find_package(osal)Control platform selection via CMake variables (typically in CMakePresets.json):
| Variable | Purpose | Values |
|---|---|---|
OSAL_PLATFORM |
Selects the platform backend to use | linux, freertos |
Set it in your preset or pass it directly:
cmake -DOSAL_PLATFORM=linux --preset linux-native-gcc-debug . -B out/build/linux-native-gcc-debugImportant
OSAL_PLATFORM must be defined before osal::c is built. The build will fail with an explicit error if it is
missing.
Link against osal::cpp for the C++ API (pulls in osal::c transitively) or against osal::c directly when C++
wrappers are not needed:
target_link_libraries(my-app
PRIVATE
osal::cpp # C++ RAII API, osal::c is linked transitively
)target_link_libraries(my-c-app
PRIVATE
osal::c # C API only
)osal::cpp is an optional convenience layer built on top of osal::c. Projects that prefer the plain C interface can
link osal::c directly.
C++:
#include <osal/Thread.hpp>
osal::NormalPrioThread<> worker("worker-thread", [] {
// thread body
});
worker.join();C:
#include <osal/Thread.h>
static void worker(void* arg)
{
// thread body
}
struct OsalThread thread;
struct OsalThreadConfig config = {Normal, cOsalThreadDefaultStackSize, NULL};
osalThreadCreateEx(&thread, config, worker, NULL, "worker-thread");
osalThreadJoin(&thread);
osalThreadDestroy(&thread);C++: RAII with ScopedLock
#include <osal/Mutex.hpp>
#include <osal/ScopedLock.hpp>
osal::Mutex mutex;
{
osal::ScopedLock lock(mutex);
if (!lock)
return; // failed to acquire
// critical section
} // mutex unlocked automatically on scope exitC (manual lock/unlock):
#include <osal/Mutex.h>
struct OsalMutex mutex;
osalMutexCreate(&mutex, cOsalMutexDefaultType);
osalMutexLock(&mutex);
// critical section
osalMutexUnlock(&mutex);
osalMutexDestroy(&mutex);C++:
#include <osal/Semaphore.hpp>
osal::Semaphore sem(0);
sem.signal(); // producer
sem.wait(); // consumer (blocks until signalled)C:
#include <osal/Semaphore.h>
struct OsalSemaphore sem;
osalSemaphoreCreate(&sem, 0);
osalSemaphoreSignal(&sem); // producer
osalSemaphoreWait(&sem); // consumer (blocks until signalled)
osalSemaphoreDestroy(&sem);C++:
#include <osal/sleep.hpp>
using namespace std::chrono_literals;
osal::sleep(100ms);C:
#include <osal/sleep.h>
osalSleepMs(100);Note
This section is relevant when working on osal itself in standalone mode. The presets defined here can also serve as
a reference for dependent projects.
- Configure:
cmake --preset <preset-name> . -B out/build/<preset-name> - Build:
cmake --build out/build/<preset-name> --parallel - Run tests:
cd out/build/<preset-name>/bin; ./osal-tests - Reformat code:
tools/check-clang-format.sh - Run linter:
cd out/build/<preset-name>; ../../../tools/check-clang-tidy.sh- Must be launched with a clang preset (usually inside the clang dev container)
- Native Linux:
- System dependencies:
linux-native-{gcc,clang}-{debug,release} - Conan dependencies:
linux-native-conan-{gcc,clang}-{debug,release}
- System dependencies:
- Cross-compilation:
- Generic ARM64:
linux-arm64-conan-{gcc,clang}-{debug,release} - Yocto (via SDK):
yocto-sdk-{gcc,clang}-{debug,release} - FreeRTOS ARMv7 Cortex-M4:
freertos-armv7-m4-conan-gcc-{debug,release}
- Generic ARM64:
- Sanitizers:
*-{asan,lsan,tsan,ubsan}variants (Linux native and Conan presets)
Note
For local development use the linux-native-conan-gcc-debug preset.
- Zero Warning Policy: All warnings treated as errors (
-Wall -Wextra -Wpedantic -Werror) - No Exceptions: C++ code is built with
-fno-exceptions; errors are reported viastd::error_code - Code Formatting: clang-format with project-specific style (120-character line length)
- Static Analysis: clang-tidy configuration enforced in CI
- Sanitizers: Address, leak, thread, and undefined behavior sanitizer presets available
-
Component structure:
osal::candosal::cppare separate CMake components inlib/osal/andlib/cpp/respectively. Public headers live underinclude/osal/within each component directory. Platform-specific internal headers (ThreadImpl.h,MutexImpl.h,SemaphoreImpl.h) are inlib/osal/<platform>/include/internal/and are never included by consumers. -
Adding a new platform backend: Create
lib/osal/<platform>/with the same structure aslinux/orfreertos/. Implement every function declared inlib/osal/include/osal/and provide the three*Impl.hheaders in<platform>/include/internal/. Pass the new directory name asOSAL_PLATFORMto activate it. -
Testing: Tests live in
tests/and use Catch2 3.13.0 (fetched via Conan). Use a Conan preset to run them. The test binary is namedosal-tests; on non-UNIX targets an.binimage is generated from it instead. -
Dependencies: Conan is used only for test dependencies (Catch2). The runtime library has no external package requirements beyond what the selected backend needs —
pthreadforlinux, the FreeRTOS kernel forfreertos. -
Error handling: Every operation returns
std::error_code. TheOsalErrorenum is mapped to anstd::error_categoryviaosal::ErrorCategory(seelib/cpp/include/osal/Error.hpp). Exceptions are explicitly disabled at compile time via-fno-exceptions. -
ISR safety: Functions suffixed with
Isr()(e.g.osalMutexUnlockIsr(),osal::Semaphore::signalIsr()) must only be called from interrupt context. The standard variants must not be called from ISR. Mixing the two is undefined behavior. -
Thread priority aliases:
osal::Thread<Priority, StackSize>has five convenience aliases defined inlib/cpp/include/osal/Thread.hpp:LowestPrioThread,LowPrioThread,NormalPrioThread,HighPrioThread, andHighestPrioThread. Prefer these over explicitly spelling out the template parameters. -
Code style: Formatting is enforced by
.clang-format(120-character lines, project-specific brace and spacing rules). Runtools/check-clang-format.shlocally before submitting changes. Static analysis is enforced via.clang-tidy; run the linter with a clang preset to surface issues before CI.