xref: /aosp_15_r20/external/pigweed/docs/os/index.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _docs-os:
2
3==========
4OS support
5==========
6
7.. toctree::
8   :hidden:
9
10   zephyr/index
11
12Pigweed’s operating system abstraction layers are portable and configurable
13building blocks, giving users full control while maintaining high performance
14and low overhead.
15
16Although we primarily target smaller-footprint MMU-less 32-bit microcontrollers,
17the OS abstraction layers are written to work on everything from single-core
18bare metal low end microcontrollers to asymmetric multiprocessing (AMP) and
19symmetric multiprocessing (SMP) embedded systems using Real Time Operating
20Systems (RTOS). They even fully work on your developer workstation on Linux,
21Windows, or MacOS!
22
23Pigweed has ports for the following systems:
24
25.. list-table::
26
27  * - **Environment**
28    - **Status**
29  * - STL (Mac, Window, & Linux)
30    - **✔ Supported**
31  * - `FreeRTOS <https://www.freertos.org/>`_
32    - **✔ Supported**
33  * - :ref:`Zephyr <docs-os-zephyr>`
34    - **✔ Supported**
35  * - `Azure RTOS (formerly ThreadX) <https://azure.microsoft.com/en-us/services/rtos/>`_
36    - **✔ Supported**
37  * - `SEGGER embOS <https://www.segger.com/products/rtos/embos/>`_
38    - **✔ Supported**
39  * - Baremetal
40    - *In Progress*
41
42Pigweed's OS abstraction layers are divided by the **functional grouping of the
43primitives**. Many of our APIs are similar or **nearly identical to C++'s
44Standard Template Library (STL)** with the notable exception that we do not
45support exceptions. We opted to follow the STL's APIs partially because they
46are relatively well thought out and many developers are already familiar with
47them, but also because this means they are compatible with existing helpers in
48the STL; for example, ``std::lock_guard``.
49
50---------------
51Time Primitives
52---------------
53The :ref:`module-pw_chrono` module provides the building blocks for expressing
54durations, timestamps, and acquiring the current time. This in turn is used by
55other modules, including  :ref:`module-pw_sync` and :ref:`module-pw_thread` as
56the basis for any time bound APIs (i.e. with timeouts and/or deadlines). Note
57that this module is optional and bare metal targets may opt not to use this.
58
59.. list-table::
60
61  * - **Supported On**
62    - **SystemClock**
63  * - FreeRTOS
64    - :ref:`module-pw_chrono_freertos`
65  * - Zephyr
66    - :ref:`module-pw_chrono_zephyr`
67  * - ThreadX
68    - :ref:`module-pw_chrono_threadx`
69  * - embOS
70    - :ref:`module-pw_chrono_embos`
71  * - STL
72    - :ref:`module-pw_chrono_stl`
73  * - Baremetal
74    - Planned
75
76
77System Clock
78============
79For RTOS and HAL interactions, we provide a ``pw::chrono::SystemClock`` facade
80which provides 64 bit timestamps and duration support along with a C API. For
81C++ there is an optional virtual wrapper, ``pw::chrono::VirtualSystemClock``,
82around the singleton clock facade to enable dependency injection.
83
84.. code-block:: cpp
85
86   #include <chrono>
87
88   #include "pw_thread/sleep.h"
89
90   using namespace std::literals::chrono_literals;
91
92   void ThisSleeps() {
93     pw::thread::sleep_for(42ms);
94   }
95
96Unlike the STL's time bound templated APIs which are not specific to a
97particular clock, Pigweed's time bound APIs are strongly typed to use the
98``pw::chrono::SystemClock``'s ``duration`` and ``time_points`` directly.
99
100.. code-block:: cpp
101
102   #include "pw_chrono/system_clock.h"
103
104   bool HasThisPointInTimePassed(const SystemClock::time_point timestamp) {
105     return SystemClock::now() > timestamp;
106   }
107
108--------------------------
109Synchronization Primitives
110--------------------------
111The :ref:`module-pw_sync` provides the building blocks for synchronizing between
112threads and/or interrupts through signaling primitives and critical section lock
113primitives.
114
115Critical Section Lock Primitives
116================================
117Pigweed's locks support Clang's thread safety lock annotations and the STL's
118RAII helpers.
119
120.. list-table::
121
122  * - **Supported On**
123    - **Mutex**
124    - **TimedMutex**
125    - **InterruptSpinLock**
126  * - FreeRTOS
127    - :ref:`module-pw_sync_freertos`
128    - :ref:`module-pw_sync_freertos`
129    - :ref:`module-pw_sync_freertos`
130  * - Zephyr
131    - :ref:`module-pw_sync_zephyr`
132    - Planned
133    - :ref:`module-pw_sync_zephyr`
134  * - ThreadX
135    - :ref:`module-pw_sync_threadx`
136    - :ref:`module-pw_sync_threadx`
137    - :ref:`module-pw_sync_threadx`
138  * - embOS
139    - :ref:`module-pw_sync_embos`
140    - :ref:`module-pw_sync_embos`
141    - :ref:`module-pw_sync_embos`
142  * - STL
143    - :ref:`module-pw_sync_stl`
144    - :ref:`module-pw_sync_stl`
145    - :ref:`module-pw_sync_stl`
146  * - Baremetal
147    - Planned, not ready for use
148    - ✗
149    - Planned, not ready for use
150
151
152Thread Safe Mutex
153-----------------
154The ``pw::sync::Mutex`` protects shared data from being simultaneously accessed
155by multiple threads. Optionally, the ``pw::sync::TimedMutex`` can be used as an
156extension with timeout and deadline based semantics.
157
158.. code-block:: cpp
159
160   #include <mutex>
161
162   #include "pw_sync/mutex.h"
163
164   pw::sync::Mutex mutex;
165
166   void ThreadSafeCriticalSection() {
167     std::lock_guard lock(mutex);
168     NotThreadSafeCriticalSection();
169   }
170
171Interrupt Safe InterruptSpinLock
172--------------------------------
173The ``pw::sync::InterruptSpinLock`` protects shared data from being
174simultaneously accessed by multiple threads and/or interrupts as a targeted
175global lock, with the exception of Non-Maskable Interrupts (NMIs). Unlike global
176interrupt locks, this also works safely and efficiently on SMP systems.
177
178.. code-block:: cpp
179
180   #include <mutex>
181
182   #include "pw_sync/interrupt_spin_lock.h"
183
184   pw::sync::InterruptSpinLock interrupt_spin_lock;
185
186   void InterruptSafeCriticalSection() {
187     std::lock_guard lock(interrupt_spin_lock);
188     NotThreadSafeCriticalSection();
189   }
190
191Signaling Primitives
192====================
193Native signaling primitives tend to vary more compared to critical section locks
194across different platforms. For example, although common signaling primitives
195like semaphores are in most if not all RTOSes and even POSIX, it was not in the
196STL before C++20. Likewise many C++ developers are surprised that conditional
197variables tend to not be natively supported on RTOSes. Although you can usually
198build any signaling primitive based on other native signaling primitives,
199this may come with non-trivial added overhead in ROM, RAM, and execution
200efficiency.
201
202For this reason, Pigweed intends to provide some simpler signaling primitives
203which exist to solve a narrow programming need but can be implemented as
204efficiently as possible for the platform that it is used on. This simpler but
205highly portable class of signaling primitives is intended to ensure that a
206portability efficiency tradeoff does not have to be made up front.
207
208.. list-table::
209
210  * - **Supported On**
211    - **ThreadNotification**
212    - **TimedThreadNotification**
213    - **CountingSemaphore**
214    - **BinarySemaphore**
215  * - FreeRTOS
216    - :ref:`module-pw_sync_freertos`
217    - :ref:`module-pw_sync_freertos`
218    - :ref:`module-pw_sync_freertos`
219    - :ref:`module-pw_sync_freertos`
220  * - Zephyr
221    - :ref:`module-pw_sync_stl`
222    - :ref:`module-pw_sync_stl`
223    - Planned
224    - :ref:`module-pw_sync_stl`
225  * - ThreadX
226    - :ref:`module-pw_sync_threadx`
227    - :ref:`module-pw_sync_threadx`
228    - :ref:`module-pw_sync_threadx`
229    - :ref:`module-pw_sync_threadx`
230  * - embOS
231    - :ref:`module-pw_sync_embos`
232    - :ref:`module-pw_sync_embos`
233    - :ref:`module-pw_sync_embos`
234    - :ref:`module-pw_sync_embos`
235  * - STL
236    - :ref:`module-pw_sync_stl`
237    - :ref:`module-pw_sync_stl`
238    - :ref:`module-pw_sync_stl`
239    - :ref:`module-pw_sync_stl`
240  * - Baremetal
241    - Planned
242    - ✗
243    - TBD
244    - TBD
245
246Thread Notification
247-------------------
248Pigweed intends to provide the ``pw::sync::ThreadNotification`` and
249``pw::sync::TimedThreadNotification`` facades which permit a single consumer to
250block until an event occurs. This should be backed by the most efficient native
251primitive for a target, regardless of whether that is a semaphore, event flag
252group, condition variable, direct task notification with a critical section, or
253something else.
254
255Counting Semaphore
256------------------
257The ``pw::sync::CountingSemaphore`` is a synchronization primitive that can be
258used for counting events and/or resource management where receiver(s) can block
259on acquire until notifier(s) signal by invoking release.
260
261.. code-block:: cpp
262
263   #include "pw_sync/counting_semaphore.h"
264
265   pw::sync::CountingSemaphore event_semaphore;
266
267   void NotifyEventOccurred() {
268     event_semaphore.release();
269   }
270
271   void HandleEventsForever() {
272     while (true) {
273       event_semaphore.acquire();
274       HandleEvent();
275     }
276   }
277
278Binary Semaphore
279----------------
280The ``pw::sync::BinarySemaphore`` is a specialization of the counting semaphore
281with an arbitrary token limit of 1, meaning it's either full or empty.
282
283.. code-block:: cpp
284
285   #include "pw_sync/binary_semaphore.h"
286
287   pw::sync::BinarySemaphore do_foo_semaphore;
288
289   void NotifyResultReady() {
290     result_ready_semaphore.release();
291   }
292
293   void BlockUntilResultReady() {
294     result_ready_semaphore.acquire();
295   }
296
297--------------------
298Threading Primitives
299--------------------
300The :ref:`module-pw_thread` module provides the building blocks for creating and
301using threads including yielding and sleeping.
302
303.. list-table::
304
305  * - **Supported On**
306    - **Thread Creation**
307    - **Thread Id/Sleep/Yield**
308  * - FreeRTOS
309    - :ref:`module-pw_thread_freertos`
310    - :ref:`module-pw_thread_freertos`
311  * - Zephyr
312    - :ref:`module-pw_thread_zephyr`
313    - :ref:`module-pw_thread_zephyr`
314  * - ThreadX
315    - :ref:`module-pw_thread_threadx`
316    - :ref:`module-pw_thread_threadx`
317  * - embOS
318    - :ref:`module-pw_thread_embos`
319    - :ref:`module-pw_thread_embos`
320  * - STL
321    - :ref:`module-pw_thread_stl`
322    - :ref:`module-pw_thread_stl`
323  * - Baremetal
324    - ✗
325    - ✗
326
327Thread Creation
328===============
329The :cpp:type:`pw::Thread`’s API is C++11 STL ``std::thread`` like. Unlike
330``std::thread``, the Pigweed's API requires ``pw::thread::Options`` as an
331argument for creating a thread. This is used to give the user full control over
332the native OS's threading options without getting in your way.
333
334.. code-block:: cpp
335
336   #include "pw_thread/detached_thread.h"
337   #include "pw_thread_freertos/context.h"
338   #include "pw_thread_freertos/options.h"
339
340   pw::thread::freertos::ContextWithStack<42> example_thread_context;
341
342   void StartDetachedExampleThread() {
343      pw::thread::DetachedThread(
344        pw::thread::freertos::Options()
345            .set_name("static_example_thread")
346            .set_priority(kFooPriority)
347            .set_static_context(example_thread_context),
348        example_thread_function);
349   }
350
351Controlling the current thread
352==============================
353Beyond thread creation, Pigweed offers support for sleeping, identifying, and
354yielding the current thread.
355
356.. code-block:: cpp
357
358   #include "pw_thread/yield.h"
359
360   void CooperativeBusyLooper() {
361     while (true) {
362       DoChunkOfWork();
363       pw::this_thread::yield();
364     }
365   }
366
367------------------
368Execution Contexts
369------------------
370Code runs in *execution contexts*. Common examples of execution contexts on
371microcontrollers are **thread context** and **interrupt context**, though there
372are others. Since OS abstractions deal with concurrency, it's important to
373understand what API primitives are safe to call in what contexts.  Since the
374number of execution contexts is too large for Pigweed to cover exhaustively,
375Pigweed has the following classes of APIs:
376
377**Thread Safe APIs** - These APIs are safe to use in any execution context where
378one can use blocking or yielding APIs such as sleeping, blocking on a mutex
379waiting on a semaphore.
380
381**Interrupt (IRQ) Safe APIs** - These APIs can be used in any execution context
382which cannot use blocking and yielding APIs. These APIs must protect themselves
383from preemption from maskable interrupts, etc. This includes critical section
384thread contexts in addition to "real" interrupt contexts. Our definition
385explicitly excludes any interrupts which are not masked when holding a SpinLock,
386those are all considered non-maskable interrupts. An interrupt safe API may
387always be safely used in a context which permits thread safe APIs.
388
389**Non-Maskable Interrupt (NMI) Safe APIs** - Like the Interrupt Safe APIs, these
390can be used in any execution context which cannot use blocking or yielding APIs.
391In addition, these may be used by interrupts which are not masked when for
392example holding a SpinLock like CPU exceptions or C++/POSIX signals. These tend
393to come with significant overhead and restrictions compared to regular interrupt
394safe APIs as they **cannot rely on critical sections**, instead
395only atomic signaling can be used. An interrupt safe API may always be
396used in a context which permits interrupt safe and thread safe APIs.
397
398On naming
399=========
400Instead of having context specific APIs like FreeRTOS's ``...FromISR()``,
401Pigweed has a single API which validates the context requirements through
402``DASSERT`` and ``DCHECK`` in the backends (user configurable). We did this for
403a few reasons:
404
405#. **Too many contexts** - Since there are contexts beyond just thread,
406   interrupt, and NMI, having context-specific APIs would be a hard to
407   maintain. The proliferation of postfixed APIs (``...FromISR``,
408   ``...FromNMI``, ``...FromThreadCriticalSection``, and so on) would also be
409   confusing for users.
410
411#. **Must verify context anyway** - Backends are required to enforce context
412   requirements with ``DCHECK`` or related calls, so we chose a simple API
413   which happens to match both the C++'s STL and Google's Abseil.
414
415#. **Multi-context code** - Code running in multiple contexts would need to be
416   duplicated for each context if the APIs were postfixed, or duplicated with
417   macros. The authors chose the duplication/macro route in previous projects
418   and found it clunky and hard to maintain.
419
420-----------------------------
421Construction & Initialization
422-----------------------------
423**TL;DR: Pigweed OS primitives are initialized through C++ construction.**
424
425We have chosen to go with a model which initializes the synchronization
426primitive during C++ object construction. This means that there is a requirement
427in order for static instantiation to be safe that the user ensures that any
428necessary kernel and/or platform initialization is done before the global static
429constructors are run which would include construction of the C++ synchronization
430primitives.
431
432In addition this model for now assumes that Pigweed code will always be used to
433construct synchronization primitives used with Pigweed modules. Note that with
434this model the backend provider can decide if they want to statically
435preallocate space for the primitives or rely on dynamic allocation strategies.
436If we discover at a later point that this is not sufficiently portable than we
437can either produce an optional constructor that takes in a reference to an
438existing native synchronization type and wastes a little bit RAM or we can
439refactor the existing class into two layers where one is a StaticMutex for
440example and the other is a Mutex which only holds a handle to the native mutex
441type. This would then permit users who cannot construct their synchronization
442primitives to skip the optional static layer.
443
444Kernel / Platform Initialization Before C++ Global Static Constructors
445======================================================================
446What is this kernel and/or platform initialization that must be done first?
447
448It's not uncommon for an RTOS to require some initialization functions to be
449invoked before more of its API can be safely used. For example for CMSIS RTOSv2
450``osKernelInitialize()`` must be invoked before anything but two basic getters
451are called. Similarly, Segger's embOS requires ``OS_Init()`` to be invoked first
452before any other embOS API.
453
454.. Note::
455  To get around this one should invoke these initialization functions earlier
456  and/or delay the static C++ constructors to meet this ordering requirement. As
457  an example if you were using :ref:`module-pw_boot_cortex_m`, then
458  ``pw_boot_PreStaticConstructorInit()`` would be a great place to invoke kernel
459  initialization.
460
461-------
462Roadmap
463-------
464Pigweed is still actively expanding and improving its OS Abstraction Layers.
465That being said, the following concrete areas are being worked on and can be
466expected to land at some point in the future:
467
4681. We'd like to offer a system clock based timer abstraction facade which can be
469   used on either an RTOS or a hardware timer.
4702. We are evaluating a less-portable but very useful portability facade for
471   event flags / groups. This would make it even easier to ensure all firmware
472   can be fully executed on the host.
4733. Cooperative cancellation thread joining along with a ``std::jthread`` like
474   wrapper is in progress.
4754. We'd like to add support for queues, message queues, and similar channel
476   abstractions which also support interprocessor communication in a transparent
477   manner.
4785. We're interested in supporting asynchronous worker queues and worker queue
479   pools.
4806. Migrate HAL and similar APIs to use deadlines for the backend virtual
481   interfaces to permit a smaller vtable which supports both public timeout and
482   deadline semantics.
4837. Baremetal support is partially in place today, but it's not ready for use.
4848. Most of our APIs today are focused around synchronous blocking APIs, however
485   we would love to extend this to include asynchronous APIs.
486