Skip to main content

Crate osal_rs

Crate osal_rs 

Source
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 (freertos backend)
  • Host Testing: Runs under std on POSIX hosts for native testing (posix backend)
  • Type Safety: Leverages Rust’s type system for correctness
  • Async/Await: Backend-agnostic async/await without Tokio (feature async)

§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 (feature async)
  • async_primitives - Async wrappers for OSAL primitives (feature async)
  • utils - Utility types and error definitions
  • log - Logging macros
  • traits - Private module defining the trait abstractions
  • freertos - Private FreeRTOS implementation (enabled with freertos feature)
  • posix - Private POSIX implementation (enabled with posix feature)

§Features

FeatureDefaultDescription
freertosFreeRTOS backend
posixPOSIX/host backend
asyncAsync/await without Tokio
serdeSerialization via osal-rs-serde
real_timePOSIX 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.h options:
    • configTICK_RATE_HZ - Defines the tick frequency
    • configUSE_MUTEXES - Must be 1 for mutex support
    • configUSE_COUNTING_SEMAPHORES - Must be 1 for semaphore support
    • configUSE_TIMERS - Must be 1 for timer support
    • configSUPPORT_DYNAMIC_ALLOCATION - Must be 1 for dynamic allocation

When using with POSIX:

  • A POSIX-compliant host implementing pthreads, timer_create(2)/sigwait(3) and CLOCK_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: the posix feature builds the crate against std

§Platform Support

Currently tested on:

  • ARM Cortex-M (Raspberry Pi Pico/RP2040, RP2350) - freertos backend
  • ARM Cortex-M4F (STM32F4 series) - freertos backend
  • ARM Cortex-M7 (STM32H7 series) - freertos backend
  • RISC-V (RP2350 RISC-V cores) - freertos backend
  • glibc/Linux - posix backend

§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_isr suffix 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_isr variants)
  • 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

  • freertos backend: allocations happen on the FreeRTOS heap (via the #[global_allocator] in os), not the system heap
  • posix backend: uses the system allocator and native OS threads; each os::Timer spawns 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

  1. Thread Creation: Always specify appropriate stack sizes based on usage
  2. Mutexes: Prefer scoped locking with guards to prevent deadlocks
  3. Queues: Use type-safe QueueStreamed when possible
  4. Semaphores: Use binary semaphores for signaling, counting for resources
  5. ISR Handlers: Keep ISR code minimal, defer work to tasks
  6. Error Handling: Always check Result return values
  7. Async Tasks: Call block_on once 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.
print
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.