blob: 743cc2cb14efb57106ece50bda00657139e7b020 [file] [edit]
.. _docs-os-threadx-features:
========================
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 ThreadX, these APIs offer significant design and safety
advantages over raw native ThreadX APIs.
--------------------------
Synchronization vs ThreadX
--------------------------
.. note::
For internal design details and configuration options, see the
:ref:`module-pw_sync_threadx` backend documentation.
Type-safe mutex interfaces
==========================
Native ThreadX mutexes are represented by raw ``TX_MUTEX`` structures and
handles, created via ``tx_mutex_create()``. There is no distinction at the
type level between a mutex and other types of synchronization objects.
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 ThreadX.
Pigweed's Mutex and TimedMutex implementations assert that they are not
called in interrupt context via ``PW_DASSERT(!interrupt::InInterruptContext())``.
* **Recursive locking prevention**: Native ThreadX mutexes 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: ``PW_DASSERT(backend::NotRecursivelyHeld(native_type_))``, which checks
that ``tx_mutex_ownership_count == 1``.
* **Release assertions**: Unlocking a native ThreadX mutex returns a status that
can silently go unchecked. Pigweed's ``Mutex::unlock()`` asserts that releasing the lock
succeeded via ``PW_ASSERT(tx_mutex_put(&native_type_) == TX_SUCCESS)``.
Port-safe interrupt spinlocks
=============================
ThreadX does not offer a native interrupt spinlock API. Developers typically
attempt to implement mutual exclusion between threads and interrupts using global
interrupt control flags.
Pigweed's ``pw::sync::InterruptSpinLock`` provides a unified, port-safe spinlock
implementation. It uses ``tx_interrupt_control(TX_INT_DISABLE)`` to create a critical
section. Furthermore, to prevent accidental thread context switches while the
``InterruptSpinLock`` is held by a thread, Pigweed's implementation raises the
preemption threshold of the current thread to ``0`` (the highest priority) using
``tx_thread_preemption_change()``.
It also detects recursive locking or unlocking from an incorrect context (e.g., locking
in thread context and unlocking in interrupt context, or vice-versa) via state
verification checks on ``native_type_.state``.
.. warning::
This backend does not support SMP yet as there's no internal lock to spin on.
Thread notifications
====================
Although one may be tempted to use ``tx_thread_sleep`` and ``tx_thread_wait_abort``
to implement direct-to-thread signaling in ThreadX, this contains a race condition:
if another thread or interrupt attempts to invoke ``tx_thread_wait_abort`` before the
blocking thread has actually executed ``tx_thread_sleep``, the wait abort would fail
silently.
To prevent this race condition, Pigweed's ``pw::sync::ThreadNotification`` and
``pw::sync::TimedThreadNotification`` backends for ThreadX are backed by the binary
semaphore backends (``pw_sync:binary_semaphore_thread_notification_backend``).
This ensures race-free, reliable thread signaling.
Compile-time lock safety via thread-safety annotations
======================================================
Native ThreadX mutexes 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 :ref:`pw_sync thread-safety lock annotations <module-pw_sync-thread-safety-lock-annotations>`
reference documentation.
-------------------
Sleeping vs ThreadX
-------------------
.. note::
For details on thread options, static stack allocations, joining support,
thread iteration, and snapshot integration, see the :ref:`module-pw_thread_threadx`
backend documentation.
Safe sleep and yield APIs
=========================
In native ThreadX, calling ``tx_thread_sleep()`` or ``tx_thread_relinquish()``
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 yield and sleep functions map directly to ``tx_thread_relinquish()`` and
``tx_thread_sleep()`` (if sleep duration is at least one tick).
Safe arbitrarily long durations
===============================
Native ThreadX sleep and timeout functions require durations represented as ticks
(``ULONG``). If a thread requests a sleep or synchronization timeout that exceeds
the maximum value representable by ``ULONG``, it can overflow or truncate.
Pigweed's sleep and timed synchronization APIs automatically split long durations
exceeding ``pw::chrono::threadx::kMaxTimeout`` into loop iterations under the hood,
ensuring timeouts of arbitrary lengths behave correctly.
---------------
Time vs ThreadX
---------------
.. note::
For configuration requirements and operational expectations (such as how
frequently ``SystemClock::now()`` must be called to handle native tick overflows),
see the :ref:`module-pw_chrono_threadx` backend documentation.
Non-overflowing 64-bit system clock
===================================
ThreadX's native tick counter (``tx_time_get()``) returns a ``ULONG`` (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.
.. warning::
This clock backend is not compatible with ``TX_NO_TIMER`` as that disables
``tx_time_get()``.
-------------------
Other functionality
-------------------
Some native ThreadX 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 flags (``TX_EVENT_FLAGS_GROUP``)
======================================
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 :ref:`module-pw_async2`: For waiting on multiple
asynchronous resources or events without blocking threads.
Queues (``TX_QUEUE``)
=====================
Pigweed does not wrap ThreadX's native C-style queue 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.
Block & byte memory pools (``TX_BLOCK_POOL`` / ``TX_BYTE_POOL``)
================================================================
ThreadX's C-style block and byte memory pool structures are not wrapped by Pigweed.
Developers should prefer standard, type-safe C++ allocator interfaces and wrappers provided by:
* :ref:`module-pw_allocator`: For type-safe, generic, and configurable memory allocation.
Repeating/periodic application timers (``TX_TIMER``)
====================================================
``pw::chrono::SystemTimer`` is exclusively a one-shot timer. Pigweed does
not wrap ThreadX's native periodic application timers. Periodic timer behavior
should be implemented by rescheduling the ``SystemTimer`` from within its expiry
callback.