Advantages & differences#
Pigweed’s OS abstraction layers (pw_sync, pw_thread, and pw_chrono)
provide type-safe C++ interfaces with robust compile-time and runtime checking.
When targeting FreeRTOS, these APIs offer significant design and safety
advantages over raw native FreeRTOS APIs.
Synchronization vs FreeRTOS#
Note
For internal design details, configuration options, and constraints (such as
how InterruptSpinLock manages synchronous versus asynchronous yield behavior or how
to customize the notification index to prevent task notification collisions), see the
pw_sync_freertos backend documentation.
Type-safe mutex interfaces#
Native FreeRTOS mutexes are represented by raw SemaphoreHandle_t variables
created via xSemaphoreCreateMutexStatic(). There is no distinction at the
type level between a mutex, a counting semaphore, or a binary semaphore.
Pigweed’s mutex wrapper utilizes StaticSemaphore_t static allocations underneath
to bypass dynamic memory overhead. For configuration requirements and API mappings, see the
Mutex & TimedMutex backend reference.
Pigweed’s pw::sync::Mutex and pw::sync::TimedMutex are distinct,
type-safe C++ classes. They enforce correct usage at compile-time and restrict
invalid runtime operations:
Interrupt-context guardrails: Locking or unlocking a mutex within an interrupt handler causes undefined behavior or deadlock in FreeRTOS. Pigweed’s implementations assert that they are not called in interrupt context via
PW_DASSERT(!interrupt::InInterruptContext()).Release assertions: Unlocking a native FreeRTOS semaphore can silently fail or succeed under incorrect ownership depending on configuration. Pigweed’s
Mutex::unlock()asserts that releasing the lock succeeded viaPW_ASSERT(xSemaphoreGive(...) == pdTRUE).
Port-safe interrupt spinlocks#
FreeRTOS does not offer a native interrupt spinlock API. Developers typically attempt to implement mutual exclusion between threads and interrupts using task critical sections.
However, on FreeRTOS ports that implement synchronous context switches
(synchronous yields), calling signaling APIs inside a critical section can
trigger taskYIELD() immediately, causing a context switch and enabling
interrupts prematurely.
Pigweed’s pw::sync::InterruptSpinLock provides a unified, port-safe spinlock
implementation. It automatically detects ports with synchronous yield behavior
and disables/restores scheduling via vTaskSuspendAll() /
xTaskResumeAll() within the critical section to prevent immediate task
switching. It also catches accidental recursive locking via
PW_DASSERT(!native_type_.locked).
For a deep dive into how Pigweed implements this to handle critical-section yields on ports with synchronous context switches, see the InterruptSpinLock design notes.
Optimal task notifications#
FreeRTOS direct-to-task notifications are lightweight but suffer from potential
collision because they share a single global state (ucNotifyState) and
value array index (historically only index 0) per task’s TCB. Multiple
independent libraries attempting to use index 0 of direct task notifications
will conflict, triggering subtle, silent, and difficult-to-debug race
conditions.
Pigweed’s pw::sync::ThreadNotification and
pw::sync::TimedThreadNotification provide a clean, object-oriented
abstraction that maps optimally to FreeRTOS task notifications:
Zero Extra Allocation: They consume task notifications directly inside the TCB, incurring zero extra RAM or static semaphore memory allocation.
Safe and Clean Lifecycle: Pigweed only registers the task for notifications when it is about to block, and cleanly clears the notification state on timeout or abort. This matches the internal mechanism of FreeRTOS Stream/Message Buffers, making it safe and highly efficient.
Customizable Indices: The target index is configurable via
PW_SYNC_FREERTOS_CONFIG_THREAD_NOTIFICATION_INDEXto avoid collisions with existing external libraries.
For details on how this optimized backend interacts with native task notifications and how the lifecycle is cleaned up on timeout or abort, see the ThreadNotification & TimedThreadNotification backend reference.
Compile-time lock safety via thread-safety annotations#
Native FreeRTOS mutexes are C-level structures and handles (SemaphoreHandle_t)
with no integration with compile-time analysis tools. The compiler cannot verify
whether a shared variable is accessed under the correct lock, nor can it enforce
consistent lock acquisition orders, making concurrency bugs like data races and
deadlocks easy to introduce and difficult to diagnose.
Pigweed’s C++ synchronization wrappers (such as pw::sync::Mutex and
pw::sync::InterruptSpinLock) integrate natively with Clang’s static thread
safety analysis. By annotating members with attributes like PW_GUARDED_BY
and methods with PW_EXCLUSIVE_LOCKS_REQUIRED, the compiler statically checks
and warns against missing locks or improper release sequences at compile-time.
For detailed configuration requirements and lists of supported lock safety macros, refer to the pw_sync thread-safety lock annotations reference documentation.
Sleeping vs FreeRTOS#
Note
For details on thread options, static versus dynamic stack allocations, joining support, thread iteration, and snapshot integration, see the pw_thread_freertos backend documentation.
Safe sleep and yield APIs#
In native FreeRTOS, calling vTaskDelay() or taskYIELD() from an
interrupt context leads to crashes or corrupts kernel state.
Pigweed’s pw::this_thread::sleep_for(), sleep_until(), and
pw::thread::yield() enforce safety by asserting they are only invoked from
thread contexts via PW_DCHECK(get_id() != Thread::id()).
These delay and yield functions map directly to vTaskDelay() and taskYIELD()
as detailed in the Thread Sleep Backend backend reference.
Safe arbitrarily long durations#
Native FreeRTOS delay functions require timeouts represented as a
TickType_t. If a thread requests a sleep or synchronization timeout that
exceeds the maximum value representable by TickType_t, it can overflow or
truncate.
Pigweed’s sleep and timed synchronization APIs automatically split long
durations exceeding pw::chrono::freertos::kMaxTimeout into loop
iterations under the hood, ensuring timeouts of arbitrary lengths behave
correctly.
Time & timers vs FreeRTOS#
Note
For configuration requirements (such as software timer configuration) and operational
expectations (like how frequently SystemClock::now() must be called to handle native tick
overflows), see the pw_chrono_freertos backend documentation.
Non-overflowing 64-bit system clock#
FreeRTOS’s native tick counters (xTaskGetTickCount() and
xTaskGetTickCountFromISR()) return a TickType_t (typically unsigned
32-bit). At a standard 1 kHz tick rate, a 32-bit counter wraps around in
approximately 49.7 days. Failing to handle this wraparound correctly in
application logic is a frequent source of long-term stability bugs.
Pigweed’s pw::chrono::SystemClock uses a thread- and interrupt-safe
InterruptSpinLock to track tick overflows, expanding the tick count into
a signed 64-bit value. This signed 64-bit clock will not overflow for millions
of years, allowing developers to safely write standard time arithmetic without
overflow concerns.
For requirements regarding clock overflow tracking and periodic now() invocation details,
see the SystemClock backend backend reference.
Race-free system timer destructor#
Native FreeRTOS software timers are deleted asynchronously via
xTimerDelete() through a command queue. This creates a race condition:
if a timer is about to expire and the timer service task has lower priority,
the timer’s callback may run after xTimerDelete() is called, potentially
accessing freed memory or invalid application state.
Pigweed’s pw::chrono::SystemTimer wrapper eliminates this race condition.
Its destructor calls Cancel(), enqueues xTimerDelete(), and then
yields or blocks in a loop until the kernel reports that the timer is
officially inactive (xTimerIsTimerActive() is false).
Additionally, SystemTimer supports arbitrarily long deadlines by
automatically rescheduling itself under the hood if the user-provided
deadline is further out than the maximum native timeout.
For more details on how the static timer API and daemon tasks are integrated, see the SystemTimer backend backend reference.
Other functionality#
Some native FreeRTOS features are not wrapped by generic facades in Pigweed. In most cases, Pigweed offers modern, target-agnostic alternatives that should be used instead:
Event groups (EventGroupHandle_t)#
Pigweed does not provide a multi-bit event-flag/event-group facade. Instead, developers can achieve similar synchronization using:
pw::sync::ThreadNotification: For simple thread unblocking/signaling.pw::sync::BinarySemaphore/pw::sync::CountingSemaphore: For signaling and resource sharing.
Queues and queue sets (QueueHandle_t)#
Pigweed does not wrap FreeRTOS’s C-style queue primitives. Instead, developers should use Pigweed’s type-safe and allocation-free containers and utilities:
pw_containers: For standard queues, circular buffers, or deques.pw::work_queue::WorkQueue: For offloading tasks to a thread.pw::ring_buffer::Reader: For circular byte-buffers.
Stream buffers and message buffers#
Pigweed does not wrap FreeRTOS stream and message buffers. Target-agnostic messaging can be achieved using:
pw_rpc: For robust, structured, and cross-process/cross-device streaming.pw_ring_buffer: For safe, lock-free or locked data streaming between threads or interrupts.
Direct task notifications as general-purpose flags/registers#
FreeRTOS task notifications can be used to store custom flags or values
directly in a task’s TCB. Pigweed’s pw::sync::ThreadNotification uses
task notifications internally but hides the raw indexes. Pigweed does not
expose a facade for custom task notification flags.
Repeating/periodic software timers#
pw::chrono::SystemTimer is exclusively a one-shot timer. Pigweed does
not wrap FreeRTOS’s auto-reload/periodic software timers. Periodic behavior
should be implemented by rescheduling the SystemTimer from within its
expiry callback.
Co-routines#
FreeRTOS includes a legacy co-routines feature. Pigweed does not wrap or support FreeRTOS co-routines. Modern cooperative multitasking in Pigweed is supported through modern asynchronous frameworks like pw_async2.