Expand description
§OSAL-RS - Operating System Abstraction Layer for Rust
A cross-platform abstraction layer for embedded and real-time operating systems.
§Overview
OSAL-RS provides a unified, safe Rust API for working with different real-time operating systems. It currently ships two backends, selected at compile time via feature flags - there is no default, exactly one must be enabled:
- FreeRTOS (feature
freertos) - for bare-metal embedded targets - POSIX (feature
posix) - runs on any pthreads-capable host (Linux, macOS) so applications, tests and doc examples can execute natively without embedded hardware or a cross toolchain
Application code written against osal_rs::os::* is portable between the
two backends by switching feature flags.
§Features
- Thread Management: Create and control threads with priorities
- Synchronization: Mutexes, semaphores, and event groups
- Communication: Queues for inter-thread message passing
- Timers: Software timers for periodic and one-shot operations
- Time Management: Duration-based timing with tick conversion
- No-std Support: Works in bare-metal embedded environments (
freertosbackend) - Host Testing: Runs under
stdon POSIX hosts for native testing (posixbackend) - Type Safety: Leverages Rust’s type system for correctness
- Async/Await: Backend-agnostic
async/awaitwithout Tokio (featureasync)
§Quick Start
§Basic Thread Example
use osal_rs::os::*;
fn main() {
// Create a thread
let mut thread = Thread::new(
"worker",
4096, // stack size
5, // priority
);
thread.spawn_simple(|| {
loop {
println!("Working...");
System::delay(1000);
}
}).unwrap();
// Start the scheduler (never returns)
System::start();
}§Mutex Example
use osal_rs::os::*;
use std::sync::Arc;
let counter = Arc::new(Mutex::new(0));
let counter_clone = counter.clone();
let mut thread = Thread::new("incrementer", 2048, 5);
thread.spawn_simple(move || {
let mut guard = counter_clone.lock().unwrap();
*guard += 1;
Ok(Arc::new(()))
}).unwrap();§Queue Example
use osal_rs::os::*;
let queue = Queue::new(10, 4).unwrap();
// Send data
let data = [1u8, 2, 3, 4];
queue.post(&data, 100).unwrap();
// Receive data
let mut buffer = [0u8; 4];
queue.fetch(&mut buffer, 100).unwrap();§Semaphore Example
use osal_rs::os::*;
use osal_rs::utils::OsalRsBool;
use core::time::Duration;
let sem = Semaphore::new(1, 1).unwrap();
if sem.wait(Duration::from_millis(100)) == OsalRsBool::True {
// Critical section
sem.signal();
}§Timer Example
extern crate alloc;
use osal_rs::os::*;
use alloc::sync::Arc;
use core::time::Duration;
let timer = Timer::new_with_to_tick(
"periodic",
Duration::from_millis(500),
true, // auto-reload
None,
|_timer, _param| {
println!("Timer tick");
Ok(Arc::new(()))
}
).unwrap();
timer.start_with_to_tick(Duration::from_millis(10));§Async/Await Example (feature async)
use osal_rs::os::{block_on, AsyncMutex, AsyncQueue, AsyncSemaphore};
// Drive a future to completion on the calling RTOS task — no Tokio needed.
block_on(async {
let mutex = AsyncMutex::new(0u32);
{
let mut guard = mutex.lock().await;
*guard += 1;
}
let sem = AsyncSemaphore::new(1, 0).unwrap();
sem.signal();
sem.wait_async().await;
let queue = AsyncQueue::new(8, 4).unwrap();
queue.post_async(&[1u8, 2, 3, 4]).await.unwrap();
let mut buf = [0u8; 4];
queue.fetch_async(&mut buf).await.unwrap();
});§Module Organization
os- Main module containing all OS abstractions- Threads, mutexes, semaphores, queues, event groups, timers
- System-level functions
- Type definitions
block_on,AsyncQueue,AsyncSemaphore,AsyncMutex(featureasync)
async_primitives- Async wrappers for OSAL primitives (featureasync)utils- Utility types and error definitionslog- Logging macrostraits- Private module defining the trait abstractionsfreertos- Private FreeRTOS implementation (enabled withfreertosfeature)posix- Private POSIX implementation (enabled withposixfeature)
§Features
| Feature | Default | Description |
|---|---|---|
freertos | ❌ | FreeRTOS backend |
posix | ❌ | POSIX/host backend |
async | ❌ | Async/await without Tokio |
serde | ❌ | Serialization via osal-rs-serde |
real_time | ❌ | POSIX only: schedules spawned threads with SCHED_FIFO instead of inheriting the creating thread’s policy/priority. Not meant to be requested by hand - the build script auto-enables it when the host OS/kernel supports real-time scheduling |
There is no default backend: exactly one of freertos/posix must be
enabled explicitly. Enabling neither trips the compile_error! below;
enabling both is equally unsupported (the two are mutually exclusive by
cfg, so the crate fails to build either way).
§Requirements
When using with FreeRTOS:
- FreeRTOS kernel must be properly configured
- Link the C porting layer from
osal-rs-porting/freertos/ - Set appropriate
FreeRTOSConfig.hoptions:configTICK_RATE_HZ- Defines the tick frequencyconfigUSE_MUTEXES- Must be 1 for mutex supportconfigUSE_COUNTING_SEMAPHORES- Must be 1 for semaphore supportconfigUSE_TIMERS- Must be 1 for timer supportconfigSUPPORT_DYNAMIC_ALLOCATION- Must be 1 for dynamic allocation
When using with POSIX:
- A POSIX-compliant host implementing pthreads,
timer_create(2)/sigwait(3)andCLOCK_MONOTONIC(glibc/Linux is part of this crate’s test suite) - No special build steps: unlike
freertos, this backend links only against the host’s libc/libpthread - no cross toolchain or RTOS kernel sources required - Disables
no_std: theposixfeature builds the crate againststd
§Platform Support
Currently tested on:
- ARM Cortex-M (Raspberry Pi Pico/RP2040, RP2350) -
freertosbackend - ARM Cortex-M4F (STM32F4 series) -
freertosbackend - ARM Cortex-M7 (STM32H7 series) -
freertosbackend - RISC-V (RP2350 RISC-V cores) -
freertosbackend - glibc/Linux -
posixbackend
§Thread Safety
All types are designed with thread safety in mind:
- Most operations are thread-safe and can be called from multiple threads
- Methods with
_from_isrsuffix are ISR-safe (callable from interrupt context) - Regular methods (without
_from_isr) must not be called from ISR context - Mutexes use priority inheritance to prevent priority inversion
§ISR Context
Operations in ISR context have restrictions (applies to the freertos
backend; POSIX has no interrupt context - _from_isr variants are
provided there for API compatibility but behave like their regular
counterparts):
- Cannot block or use timeouts (must use zero timeout or
_from_isrvariants) - Must be extremely fast to avoid blocking other interrupts
- Use semaphores or queues to defer work to task context
- Event groups and notifications are ISR-safe for signaling
§Safety
This library uses unsafe internally to interface with C APIs but provides
safe Rust abstractions. All public APIs are designed to be memory-safe when
used correctly:
- Type safety through generic parameters
- RAII patterns for automatic resource management
- Rust’s ownership system prevents data races
- FFI boundaries are carefully validated
§Performance Considerations
freertosbackend: allocations happen on the FreeRTOS heap (via the#[global_allocator]inos), not the system heapposixbackend: uses the system allocator and native OS threads; eachos::Timerspawns a dedicated background thread- Stack sizes must be carefully tuned for each thread
- Priority inversion is mitigated through priority inheritance
- Context switches are triggered by blocking operations
§Best Practices
- Thread Creation: Always specify appropriate stack sizes based on usage
- Mutexes: Prefer scoped locking with guards to prevent deadlocks
- Queues: Use type-safe
QueueStreamedwhen possible - Semaphores: Use binary semaphores for signaling, counting for resources
- ISR Handlers: Keep ISR code minimal, defer work to tasks
- Error Handling: Always check
Resultreturn values - Async Tasks: Call
block_ononce per RTOS task; do not nest executors
§License
LGPL-2.1-or-later - See LICENSE file for details
Modules§
- async_
primitives - Async primitives (AsyncQueue, AsyncSemaphore, AsyncMutex). Async primitives built on OSAL synchronisation objects.
- log
- Logging system for embedded environments.
- os
- Main OSAL module re-exporting all OS abstractions and traits.
- utils
- Utility types and functions for OSAL-RS.
Macros§
- access_
static_ option - Accesses a static Option variable, returning the contained value or panicking if None.
- log_
debug - Logs a DEBUG level message.
- log_
error - Logs an ERROR level message.
- log_
fatal - Logs a FATAL level message.
- log_
info - Logs an INFO level message.
- log_
warning - Logs a WARNING level message.
- Prints formatted text without a newline.
- println
- Prints formatted text with a newline (
\r\n). - thread_
extract_ param - Extracts a typed parameter from an optional boxed Any reference.