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 SEGGER embOS, these APIs offer significant design and safety
advantages over raw native embOS APIs.
Synchronization vs embOS#
Note
For internal design details and configuration options, see the pw_sync_embos backend documentation.
Type-safe mutex interfaces#
Native embOS mutexes are represented by OS_RSEMA structures and created via
OS_CreateRSema(). While embOS distinguishes between resource semaphores
(OS_RSEMA) and counting semaphores (OS_CSEMA) at the type level, they are
raw C-level structures that lack C++ class ergonomics and compile-time safety
safeguards.
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 embOS. Pigweed’s Mutex and TimedMutex implementations assert that they are not called in interrupt context via
PW_DASSERT(!interrupt::InInterruptContext()).Recursive locking prevention: Native embOS resources (
OS_RSEMA) support recursive locking. However, Pigweed’s Mutex interface does not support recursive locking. To enforce this contract, Pigweed’s implementation asserts that the mutex is not recursively held: checking that the returned lock count fromOS_Use()is exactly1(and checking semaphore value fortry_lock()).Release semantics: Unlocking a mutex maps directly to
OS_Unuse(). BecauseOS_Unuse()has avoidreturn type, the backend does not perform release success assertions, but the wrapper ensures the resource is safely released.
Compile-time lock safety via thread-safety annotations#
Native embOS synchronization primitives are C-level structures 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.
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.
Port-safe interrupt spinlocks#
embOS does not offer a native interrupt spinlock API. Developers typically attempt to implement mutual exclusion between threads and interrupts using global interrupt disable/enable functions.
Pigweed’s pw::sync::InterruptSpinLock provides a unified, port-safe spinlock
implementation. It uses OS_IncDI() to mask interrupts and OS_SuspendAllTasks()
to disable task switching. This prevents context switches or scheduling modifications while the
lock is held.
This backend does not support SMP; on a uniprocessor target, lock acquisition
cannot fail. However, recursive locking is still caught via assertions on a
backend boolean: PW_DCHECK(!native_type_.locked, "Recursive InterruptSpinLock::lock() detected").
Thread notifications#
For thread notifications, Pigweed’s pw::sync::ThreadNotification and
pw::sync::TimedThreadNotification are implemented using the generic binary
semaphore backend (pw_sync:binary_semaphore_thread_notification_backend).
On embOS, this maps directly to OS_CreateCSema / OS_DeleteCSema.
Unlike FreeRTOS, embOS does not offer native direct-to-task notifications,
meaning this backend allocates a full OS_CSEMA semaphore structure per
notification object.
Threading and sleeping vs embOS#
Note
For details on thread options, static stack allocations, joining support, thread iteration, and snapshot integration, see the pw_thread_embos backend documentation.
C++ thread lifecycle management (joining and detaching)#
embOS does not provide a native API to join or wait for terminating threads.
Pigweed’s pw::thread::Thread implements C++11-style join() and detach()
semantics on top of embOS event objects (OS_EVENT), allowing portable
C++ thread management code to run unmodified on embOS.
Interrupt-context guardrails for sleep and yield#
In native embOS, calling delay or yield functions from an interrupt context is undefined behavior and can lead to kernel instability.
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 yield and sleep functions map directly to OS_Yield() and OS_Delay()
(if sleep duration is at least one tick).
Support for arbitrarily long durations#
Native embOS delay and timeout functions accept durations represented in ticks
(OS_TIME, which is typically a 32-bit integer). If a duration exceeds the maximum
value representable by OS_TIME, it would overflow or truncate.
Pigweed’s sleep and timed synchronization APIs prevent this by automatically
splitting durations that exceed pw::chrono::embos::kMaxTimeout into multiple
loop iterations under the hood, ensuring timeouts of arbitrary lengths behave
correctly.
Time & timers vs embOS#
Note
For configuration requirements and operational expectations, see the pw_chrono_embos backend documentation.
Non-overflowing 64-bit system clock#
embOS’s native tick counter (OS_GetTime32()) returns a 32-bit unsigned tick value.
At a standard 1 kHz tick rate, a 32-bit counter wraps around every 49.7 days.
Pigweed’s pw::chrono::SystemClock expands this tick count to a signed
64-bit value by tracking overflows in software using an InterruptSpinLock.
Warning
To track overflows correctly, SystemClock::now() must be called at least
once per overflow period (49.7 days at a 1 kHz tick rate).
Arbitrarily long timer deadlines and static allocation#
Pigweed’s pw::chrono::SystemTimer is implemented without dynamic memory allocation,
wrapping embOS’s native OS_TIMER_EX structures.
It supports arbitrarily long deadlines by automatically rescheduling itself under the hood if the user-provided deadline is further out than the maximum native timeout.
Other functionality#
Some native embOS features are not wrapped by generic facades in Pigweed. In most cases, Pigweed offers modern, target-agnostic C++ alternatives that should be used instead:
Event variables (OS_EVENT)#
Pigweed does not provide a multi-bit event-flag or poll facade. Instead, developers can achieve similar signaling and waiting behaviors using:
pw::sync::ThreadNotification: For simple thread unblocking/signaling.pw::sync::BinarySemaphore/pw::sync::CountingSemaphore: For signaling and resource sharing.Cooperative multitasking via pw_async2: For waiting on multiple asynchronous resources or events without blocking threads.
Mailboxes and queues (OS_MAILBOX / OS_Q)#
Pigweed does not wrap embOS’s native C-style queue and mailbox structures. 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.pw_rpc: For robust, structured, and cross-process/cross-device messaging.
Memory pools (OS_MEMPOOL)#
embOS’s C-style memory pool structures are not wrapped by Pigweed. Developers should prefer standard, type-safe C++ allocator interfaces and wrappers provided by:
pw_allocator: For type-safe, generic, and configurable memory allocation.
Repeating software timers (OS_TIMER)#
pw::chrono::SystemTimer is exclusively a one-shot timer. Pigweed does
not wrap embOS’s native repeating software timers. Periodic timer behavior
should be implemented by rescheduling the SystemTimer from within its expiry
callback.