blob: b5835fb693d89e19bc195107418d505ad1bd0100 [file] [log] [blame]
Ewout van Bekkum58901932020-11-09 12:46:52 -08001.. _module-pw_sync:
2
Ewout van Bekkumf84638b2021-03-12 16:09:08 -08003=======
Ewout van Bekkum58901932020-11-09 12:46:52 -08004pw_sync
Ewout van Bekkumf84638b2021-03-12 16:09:08 -08005=======
6The ``pw_sync`` module contains utilities for synchronizing between threads
7and/or interrupts through signaling primitives and critical section lock
8primitives.
Ewout van Bekkum58901932020-11-09 12:46:52 -08009
Ewout van Bekkumf84638b2021-03-12 16:09:08 -080010.. contents::
11 :local:
12 :depth: 2
13
14.. Warning::
15 This module is still under construction, the API is not yet stable.
16
17.. Note::
18 The objects in this module do not have an Init() style public API which is
19 common in many RTOS C APIs. Instead, they rely on being able to invoke the
20 native initialization APIs for synchronization primitives during C++
21 construction.
22 In order to support global statically constructed synchronization without
23 constexpr constructors, the user and/or backend **MUST** ensure that any
24 initialization required in your environment is done prior to the creation
25 and/or initialization of the native synchronization primitives
26 (e.g. kernel initialization).
27
28--------------------------------
29Critical Section Lock Primitives
30--------------------------------
31The critical section lock primitives provided by this module comply with
32`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_,
33`Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_, and where
34relevant
35`TimedLockable <https://en.cppreference.com/w/cpp/named_req/TimedLockable>`_ C++
36named requirements. This means that they are compatible with existing helpers in
37the STL's ``<mutex>`` thread support library. For example `std::lock_guard <https://en.cppreference.com/w/cpp/thread/lock_guard>`_
38and `std::unique_lock <https://en.cppreference.com/w/cpp/thread/unique_lock>`_ can be directly used.
39
40Mutex
41=====
42The Mutex is a synchronization primitive that can be used to protect shared data
43from being simultaneously accessed by multiple threads. It offers exclusive,
44non-recursive ownership semantics where priority inheritance is used to solve
45the classic priority-inversion problem.
46
47The Mutex's API is C++11 STL
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -070048`std::mutex <https://en.cppreference.com/w/cpp/thread/mutex>`_ like,
Ewout van Bekkumf84638b2021-03-12 16:09:08 -080049meaning it is a
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -070050`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_
51and `Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_.
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -070052
53.. list-table::
54
55 * - *Supported on*
56 - *Backend module*
57 * - FreeRTOS
58 - :ref:`module-pw_sync_freertos`
59 * - ThreadX
60 - :ref:`module-pw_sync_threadx`
61 * - embOS
62 - :ref:`module-pw_sync_embos`
63 * - STL
64 - :ref:`module-pw_sync_stl`
65 * - Baremetal
66 - Planned
67 * - Zephyr
68 - Planned
69 * - CMSIS-RTOS API v2 & RTX5
70 - Planned
Ewout van Bekkumf84638b2021-03-12 16:09:08 -080071
72C++
73---
74.. cpp:class:: pw::sync::Mutex
75
76 .. cpp:function:: void lock()
77
78 Locks the mutex, blocking indefinitely. Failures are fatal.
79
80 **Precondition:** The lock isn't already held by this thread. Recursive
81 locking is undefined behavior.
82
83 .. cpp:function:: bool try_lock()
84
85 Attempts to lock the mutex in a non-blocking manner.
86 Returns true if the mutex was successfully acquired.
87
88 **Precondition:** The lock isn't already held by this thread. Recursive
89 locking is undefined behavior.
90
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -070091 .. cpp:function:: void unlock()
92
93 Unlocks the mutex. Failures are fatal.
94
95 **Precondition:** The mutex is held by this thread.
96
97
98 .. list-table::
99
100 * - *Safe to use in context*
101 - *Thread*
102 - *Interrupt*
103 - *NMI*
104 * - ``Mutex::Mutex``
105 - ✔
106 -
107 -
108 * - ``Mutex::~Mutex``
109 - ✔
110 -
111 -
112 * - ``void Mutex::lock``
113 - ✔
114 -
115 -
116 * - ``bool Mutex::try_lock``
117 - ✔
118 -
119 -
120 * - ``void Mutex::unlock``
121 - ✔
122 -
123 -
124
125Examples in C++
126^^^^^^^^^^^^^^^
127.. code-block:: cpp
128
129 #include "pw_sync/mutex.h"
130
131 pw::sync::Mutex mutex;
132
133 void ThreadSafeCriticalSection() {
134 mutex.lock();
135 NotThreadSafeCriticalSection();
136 mutex.unlock();
137 }
138
139
140Alternatively you can use C++'s RAII helpers to ensure you always unlock.
141
142.. code-block:: cpp
143
144 #include <mutex>
145
146 #include "pw_sync/mutex.h"
147
148 pw::sync::Mutex mutex;
149
150 void ThreadSafeCriticalSection() {
151 std::lock_guard lock(mutex);
152 NotThreadSafeCriticalSection();
153 }
154
155
156C
157-
158The Mutex must be created in C++, however it can be passed into C using the
159``pw_sync_Mutex`` opaque struct alias.
160
161.. cpp:function:: void pw_sync_Mutex_Lock(pw_sync_Mutex* mutex)
162
163 Invokes the ``Mutex::lock`` member function on the given ``mutex``.
164
165.. cpp:function:: bool pw_sync_Mutex_TryLock(pw_sync_Mutex* mutex)
166
167 Invokes the ``Mutex::try_lock`` member function on the given ``mutex``.
168
169.. cpp:function:: void pw_sync_Mutex_Unlock(pw_sync_Mutex* mutex)
170
171 Invokes the ``Mutex::unlock`` member function on the given ``mutex``.
172
173.. list-table::
174
175 * - *Safe to use in context*
176 - *Thread*
177 - *Interrupt*
178 - *NMI*
179 * - ``void pw_sync_Mutex_Lock``
180 - ✔
181 -
182 -
183 * - ``bool pw_sync_Mutex_TryLock``
184 - ✔
185 -
186 -
187 * - ``void pw_sync_Mutex_Unlock``
188 - ✔
189 -
190 -
191
192Example in C
193^^^^^^^^^^^^
194.. code-block:: cpp
195
196 #include "pw_sync/mutex.h"
197
198 pw::sync::Mutex mutex;
199
200 extern pw_sync_Mutex mutex; // This can only be created in C++.
201
202 void ThreadSafeCriticalSection(void) {
203 pw_sync_Mutex_Lock(&mutex);
204 NotThreadSafeCriticalSection();
205 pw_sync_Mutex_Unlock(&mutex);
206 }
207
208TimedMutex
209==========
210The TimedMutex is an extension of the Mutex which offers timeout and deadline
211based semantics.
212
213The TimedMutex's API is C++11 STL
214`std::timed_mutex <https://en.cppreference.com/w/cpp/thread/timed_mutex>`_ like,
215meaning it is a
216`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_,
217`Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_, and
218`TimedLockable <https://en.cppreference.com/w/cpp/named_req/TimedLockable>`_.
219
220Note that the ``TimedMutex`` is a derived ``Mutex`` class, meaning that
221a ``TimedMutex`` can be used by someone who needs the basic ``Mutex``. This is
222in stark contrast to the C++ STL's
223`std::timed_mutex <https://en.cppreference.com/w/cpp/thread/timed_mutex>`_.
224
225
226.. list-table::
227
228 * - *Supported on*
229 - *Backend module*
230 * - FreeRTOS
231 - :ref:`module-pw_sync_freertos`
232 * - ThreadX
233 - :ref:`module-pw_sync_threadx`
234 * - embOS
235 - :ref:`module-pw_sync_embos`
236 * - STL
237 - :ref:`module-pw_sync_stl`
238 * - Zephyr
239 - Planned
240 * - CMSIS-RTOS API v2 & RTX5
241 - Planned
242
243C++
244---
245.. cpp:class:: pw::sync::TimedMutex
246
247 .. cpp:function:: void lock()
248
249 Locks the mutex, blocking indefinitely. Failures are fatal.
250
251 **Precondition:** The lock isn't already held by this thread. Recursive
252 locking is undefined behavior.
253
254 .. cpp:function:: bool try_lock()
255
256 Attempts to lock the mutex in a non-blocking manner.
257 Returns true if the mutex was successfully acquired.
258
259 **Precondition:** The lock isn't already held by this thread. Recursive
260 locking is undefined behavior.
261
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800262 .. cpp:function:: bool try_lock_for(chrono::SystemClock::duration for_at_least)
263
264 Attempts to lock the mutex where, if needed, blocking for at least the
265 specified duration.
266 Returns true if the mutex was successfully acquired.
267
268 **Precondition:** The lock isn't already held by this thread. Recursive
269 locking is undefined behavior.
270
271 .. cpp:function:: bool try_lock_until(chrono::SystemClock::time_point until_at_least)
272
273 Attempts to lock the mutex where, if needed, blocking until at least the
274 specified time_point.
275 Returns true if the mutex was successfully acquired.
276
277 **Precondition:** The lock isn't already held by this thread. Recursive
278 locking is undefined behavior.
279
280 .. cpp:function:: void unlock()
281
282 Unlocks the mutex. Failures are fatal.
283
284 **Precondition:** The mutex is held by this thread.
285
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800286
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700287 .. list-table::
288
289 * - *Safe to use in context*
290 - *Thread*
291 - *Interrupt*
292 - *NMI*
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700293 * - ``TimedMutex::TimedMutex``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700294 - ✔
295 -
296 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700297 * - ``TimedMutex::~TimedMutex``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700298 - ✔
299 -
300 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700301 * - ``void TimedMutex::lock``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700302 - ✔
303 -
304 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700305 * - ``bool TimedMutex::try_lock``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700306 - ✔
307 -
308 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700309 * - ``bool TimedMutex::try_lock_for``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700310 - ✔
311 -
312 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700313 * - ``bool TimedMutex::try_lock_until``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700314 - ✔
315 -
316 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700317 * - ``void TimedMutex::unlock``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700318 - ✔
319 -
320 -
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800321
322Examples in C++
323^^^^^^^^^^^^^^^
324.. code-block:: cpp
325
326 #include "pw_chrono/system_clock.h"
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700327 #include "pw_sync/timed_mutex.h"
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800328
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700329 pw::sync::TimedMutex mutex;
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800330
331 bool ThreadSafeCriticalSectionWithTimeout(
332 const SystemClock::duration timeout) {
333 if (!mutex.try_lock_for(timeout)) {
334 return false;
335 }
336 NotThreadSafeCriticalSection();
337 mutex.unlock();
338 return true;
339 }
340
341
342Alternatively you can use C++'s RAII helpers to ensure you always unlock.
343
344.. code-block:: cpp
345
346 #include <mutex>
347
348 #include "pw_chrono/system_clock.h"
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700349 #include "pw_sync/timed_mutex.h"
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800350
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700351 pw::sync::TimedMutex mutex;
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800352
353 bool ThreadSafeCriticalSectionWithTimeout(
354 const SystemClock::duration timeout) {
355 std::unique_lock lock(mutex, std::defer_lock);
356 if (!lock.try_lock_for(timeout)) {
357 return false;
358 }
359 NotThreadSafeCriticalSection();
360 return true;
361 }
362
363
364
365C
366-
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700367The TimedMutex must be created in C++, however it can be passed into C using the
368``pw_sync_TimedMutex`` opaque struct alias.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800369
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700370.. cpp:function:: void pw_sync_TimedMutex_Lock(pw_sync_TimedMutex* mutex)
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800371
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700372 Invokes the ``TimedMutex::lock`` member function on the given ``mutex``.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800373
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700374.. cpp:function:: bool pw_sync_TimedMutex_TryLock(pw_sync_TimedMutex* mutex)
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800375
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700376 Invokes the ``TimedMutex::try_lock`` member function on the given ``mutex``.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800377
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700378.. cpp:function:: bool pw_sync_TimedMutex_TryLockFor(pw_sync_TimedMutex* mutex, pw_chrono_SystemClock_Duration for_at_least)
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800379
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700380 Invokes the ``TimedMutex::try_lock_for`` member function on the given ``mutex``.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800381
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700382.. cpp:function:: bool pw_sync_TimedMutex_TryLockUntil(pw_sync_TimedMutex* mutex, pw_chrono_SystemClock_TimePoint until_at_least)
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800383
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700384 Invokes the ``TimedMutex::try_lock_until`` member function on the given ``mutex``.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800385
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700386.. cpp:function:: void pw_sync_TimedMutex_Unlock(pw_sync_TimedMutex* mutex)
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800387
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700388 Invokes the ``TimedMutex::unlock`` member function on the given ``mutex``.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800389
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700390.. list-table::
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800391
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700392 * - *Safe to use in context*
393 - *Thread*
394 - *Interrupt*
395 - *NMI*
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700396 * - ``void pw_sync_TimedMutex_Lock``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700397 - ✔
398 -
399 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700400 * - ``bool pw_sync_TimedMutex_TryLock``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700401 - ✔
402 -
403 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700404 * - ``bool pw_sync_TimedMutex_TryLockFor``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700405 - ✔
406 -
407 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700408 * - ``bool pw_sync_TimedMutex_TryLockUntil``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700409 - ✔
410 -
411 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700412 * - ``void pw_sync_TimedMutex_Unlock``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700413 - ✔
414 -
415 -
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800416
417Example in C
418^^^^^^^^^^^^
419.. code-block:: cpp
420
421 #include "pw_chrono/system_clock.h"
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700422 #include "pw_sync/timed_mutex.h"
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800423
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700424 pw::sync::TimedMutex mutex;
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800425
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700426 extern pw_sync_TimedMutex mutex; // This can only be created in C++.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800427
428 bool ThreadSafeCriticalSectionWithTimeout(
429 const pw_chrono_SystemClock_Duration timeout) {
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700430 if (!pw_sync_TimedMutex_TryLockFor(&mutex, timeout)) {
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800431 return false;
432 }
433 NotThreadSafeCriticalSection();
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700434 pw_sync_TimedMutex_Unlock(&mutex);
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800435 return true;
436 }
437
438
439InterruptSpinLock
440=================
441The InterruptSpinLock is a synchronization primitive that can be used to protect
442shared data from being simultaneously accessed by multiple threads and/or
443interrupts as a targeted global lock, with the exception of Non-Maskable
444Interrupts (NMIs). It offers exclusive, non-recursive ownership semantics where
445IRQs up to a backend defined level of "NMIs" will be masked to solve
446priority-inversion.
447
448This InterruptSpinLock relies on built-in local interrupt masking to make it
449interrupt safe without requiring the caller to separately mask and unmask
450interrupts when using this primitive.
451
452Unlike global interrupt locks, this also works safely and efficiently on SMP
453systems. On systems which are not SMP, spinning is not required but some state
454may still be used to detect recursion.
455
456The InterruptSpinLock is a
457`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_
458and
459`Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_.
460
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -0700461.. list-table::
462
463 * - *Supported on*
464 - *Backend module*
465 * - FreeRTOS
466 - :ref:`module-pw_sync_freertos`
467 * - ThreadX
468 - :ref:`module-pw_sync_threadx`
469 * - embOS
470 - :ref:`module-pw_sync_embos`
471 * - STL
472 - :ref:`module-pw_sync_stl`
473 * - Baremetal
474 - Planned, not ready for use
475 * - Zephyr
476 - Planned
477 * - CMSIS-RTOS API v2 & RTX5
478 - Planned
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800479
480C++
481---
482.. cpp:class:: pw::sync::InterruptSpinLock
483
484 .. cpp:function:: void lock()
485
486 Locks the spinlock, blocking indefinitely. Failures are fatal.
487
488 **Precondition:** Recursive locking is undefined behavior.
489
490 .. cpp:function:: bool try_lock()
491
492 Attempts to lock the spinlock in a non-blocking manner.
493 Returns true if the spinlock was successfully acquired.
494
495 **Precondition:** Recursive locking is undefined behavior.
496
497 .. cpp:function:: void unlock()
498
499 Unlocks the mutex. Failures are fatal.
500
501 **Precondition:** The spinlock is held by the caller.
502
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700503 .. list-table::
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800504
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700505 * - *Safe to use in context*
506 - *Thread*
507 - *Interrupt*
508 - *NMI*
509 * - ``InterruptSpinLock::InterruptSpinLock``
510 - ✔
511 - ✔
512 -
513 * - ``InterruptSpinLock::~InterruptSpinLock``
514 - ✔
515 - ✔
516 -
517 * - ``void InterruptSpinLock::lock``
518 - ✔
519 - ✔
520 -
521 * - ``bool InterruptSpinLock::try_lock``
522 - ✔
523 - ✔
524 -
525 * - ``void InterruptSpinLock::unlock``
526 - ✔
527 - ✔
528 -
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800529
530Examples in C++
531^^^^^^^^^^^^^^^
532.. code-block:: cpp
533
534 #include "pw_sync/interrupt_spin_lock.h"
535
536 pw::sync::InterruptSpinLock interrupt_spin_lock;
537
538 void InterruptSafeCriticalSection() {
539 interrupt_spin_lock.lock();
540 NotThreadSafeCriticalSection();
541 interrupt_spin_lock.unlock();
542 }
543
544
545Alternatively you can use C++'s RAII helpers to ensure you always unlock.
546
547.. code-block:: cpp
548
549 #include <mutex>
550
551 #include "pw_sync/interrupt_spin_lock.h"
552
553 pw::sync::InterruptSpinLock interrupt_spin_lock;
554
555 void InterruptSafeCriticalSection() {
556 std::lock_guard lock(interrupt_spin_lock);
557 NotThreadSafeCriticalSection();
558 }
559
560
561C
562-
563The InterruptSpinLock must be created in C++, however it can be passed into C using the
564``pw_sync_InterruptSpinLock`` opaque struct alias.
565
566.. cpp:function:: void pw_sync_InterruptSpinLock_Lock(pw_sync_InterruptSpinLock* interrupt_spin_lock)
567
568 Invokes the ``InterruptSpinLock::lock`` member function on the given ``interrupt_spin_lock``.
569
570.. cpp:function:: bool pw_sync_InterruptSpinLock_TryLock(pw_sync_InterruptSpinLock* interrupt_spin_lock)
571
572 Invokes the ``InterruptSpinLock::try_lock`` member function on the given ``interrupt_spin_lock``.
573
574.. cpp:function:: void pw_sync_InterruptSpinLock_Unlock(pw_sync_InterruptSpinLock* interrupt_spin_lock)
575
576 Invokes the ``InterruptSpinLock::unlock`` member function on the given ``interrupt_spin_lock``.
577
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700578.. list-table::
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800579
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700580 * - *Safe to use in context*
581 - *Thread*
582 - *Interrupt*
583 - *NMI*
584 * - ``void pw_sync_InterruptSpinLock_Lock``
585 - ✔
586 - ✔
587 -
588 * - ``bool pw_sync_InterruptSpinLock_TryLock``
589 - ✔
590 - ✔
591 -
592 * - ``void pw_sync_InterruptSpinLock_Unlock``
593 - ✔
594 - ✔
595 -
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800596
597Example in C
598^^^^^^^^^^^^
599.. code-block:: cpp
600
601 #include "pw_chrono/system_clock.h"
602 #include "pw_sync/interrupt_spin_lock.h"
603
604 pw::sync::InterruptSpinLock interrupt_spin_lock;
605
606 extern pw_sync_InterruptSpinLock interrupt_spin_lock; // This can only be created in C++.
607
608 void InterruptSafeCriticalSection(void) {
609 pw_sync_InterruptSpinLock_Lock(&interrupt_spin_lock);
610 NotThreadSafeCriticalSection();
611 pw_sync_InterruptSpinLock_Unlock(&interrupt_spin_lock);
612 }
613
Ewout van Bekkumcc9ef832021-04-08 08:51:16 -0700614Thread Safety Lock Annotations
615==============================
616Pigweed's critical section lock primitives support Clang's thread safety
617analysis extension for C++. The analysis is completely static at compile-time.
618This is only supported when building with Clang. The annotations are no-ops when
619using different compilers.
620
621Pigweed provides the ``pw_sync/lock_annotations.h`` header file with macro
622definitions to allow developers to document the locking policies of
623multi-threaded code. The annotations can also help program analysis tools to
624identify potential thread safety issues.
625
626More information on Clang's thread safety analysis system can be found
627`here <https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>`_.
628
629Enabling Clang's Analysis
630-------------------------
631In order to enable the analysis, Clang requires that the ``-Wthread-safety``
632compilation flag be used. In addition, if any STL components like
633``std::lock_guard`` are used, the STL's built in annotations have to be manually
634enabled, typically by setting the ``_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS``
635macro.
636
637If using GN, the ``pw_build:clang_thread_safety_warnings`` config is provided
638to do this for you, when added to your clang toolchain definition's default
639configs.
640
641Why use lock annotations?
642-------------------------
643Lock annotations can help warn you about potential race conditions in your code
644when using locks: you have to remember to grab lock(s) before entering a
645critical section, yuou have to remember to unlock it when you leave, and you
646have to avoid deadlocks.
647
648Clang's lock annotations let you inform the compiler and anyone reading your
649code which variables are guarded by which locks, which locks should or cannot be
650held when calling which function, which order locks should be acquired in, etc.
651
652Using Lock Annotations
653----------------------
654When referring to locks in the arguments of the attributes, you should
655use variable names or more complex expressions (e.g. ``my_object->lock_``)
656that evaluate to a concrete lock object whenever possible. If the lock
657you want to refer to is not in scope, you may use a member pointer
658(e.g. ``&MyClass::lock_``) to refer to a lock in some (unknown) object.
659
660Annotating Lock Usage
661^^^^^^^^^^^^^^^^^^^^^
662.. cpp:function:: PW_GUARDED_BY(x)
663
664 Documents if a shared field or global variable needs to be protected by a
665 lock. ``PW_GUARDED_BY()`` allows the user to specify a particular lock that
666 should be held when accessing the annotated variable.
667
668 Although this annotation (and ``PW_PT_GUARDED_BY``, below) cannot be applied
669 to local variables, a local variable and its associated lock can often be
670 combined into a small class or struct, thereby allowing the annotation.
671
672 Example:
673
674 .. code-block:: cpp
675
676 class Foo {
677 Mutex mu_;
678 int p1_ PW_GUARDED_BY(mu_);
679 ...
680 };
681
682.. cpp:function:: PW_PT_GUARDED_BY(x)
683
684 Documents if the memory location pointed to by a pointer should be guarded
685 by a lock when dereferencing the pointer.
686
687 Example:
688
689 .. code-block:: cpp
690
691 class Foo {
692 Mutex mu_;
693 int *p1_ PW_PT_GUARDED_BY(mu_);
694 ...
695 };
696
697 Note that a pointer variable to a shared memory location could itself be a
698 shared variable.
699
700 Example:
701
702 .. code-block:: cpp
703
704 // `q_`, guarded by `mu1_`, points to a shared memory location that is
705 // guarded by `mu2_`:
706 int *q_ PW_GUARDED_BY(mu1_) PW_PT_GUARDED_BY(mu2_);
707
708.. cpp:function:: PW_ACQUIRED_AFTER(...)
709.. cpp:function:: PW_ACQUIRED_BEFORE(...)
710
711 Documents the acquisition order between locks that can be held
712 simultaneously by a thread. For any two locks that need to be annotated
713 to establish an acquisition order, only one of them needs the annotation.
714 (i.e. You don't have to annotate both locks with both ``PW_ACQUIRED_AFTER``
715 and ``PW_ACQUIRED_BEFORE``.)
716
717 As with ``PW_GUARDED_BY``, this is only applicable to locks that are shared
718 fields or global variables.
719
720 Example:
721
722 .. code-block:: cpp
723
724 Mutex m1_;
725 Mutex m2_ PW_ACQUIRED_AFTER(m1_);
726
727.. cpp:function:: PW_EXCLUSIVE_LOCKS_REQUIRED(...)
728.. cpp:function:: PW_SHARED_LOCKS_REQUIRED(...)
729
730 Documents a function that expects a lock to be held prior to entry.
731 The lock is expected to be held both on entry to, and exit from, the
732 function.
733
734 An exclusive lock allows read-write access to the guarded data member(s), and
735 only one thread can acquire a lock exclusively at any one time. A shared lock
736 allows read-only access, and any number of threads can acquire a shared lock
737 concurrently.
738
739 Generally, non-const methods should be annotated with
740 ``PW_EXCLUSIVE_LOCKS_REQUIRED``, while const methods should be annotated with
741 ``PW_SHARED_LOCKS_REQUIRED``.
742
743 Example:
744
745 .. code-block:: cpp
746
747 Mutex mu1, mu2;
748 int a PW_GUARDED_BY(mu1);
749 int b PW_GUARDED_BY(mu2);
750
751 void foo() PW_EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... }
752 void bar() const PW_SHARED_LOCKS_REQUIRED(mu1, mu2) { ... }
753
754.. cpp:function:: PW_LOCKS_EXCLUDED(...)
755
756 Documents the locks acquired in the body of the function. These locks
757 cannot be held when calling this function (as Pigweed's default locks are
758 non-reentrant).
759
760 Example:
761
762 .. code-block:: cpp
763
764 Mutex mu;
765 int a PW_GUARDED_BY(mu);
766
767 void foo() PW_LOCKS_EXCLUDED(mu) {
768 mu.lock();
769 ...
770 mu.unlock();
771 }
772
773.. cpp:function:: PW_LOCK_RETURNED(...)
774
775 Documents a function that returns a lock without acquiring it. For example,
776 a public getter method that returns a pointer to a private lock should
777 be annotated with ``PW_LOCK_RETURNED``.
778
779 Example:
780
781 .. code-block:: cpp
782
783 class Foo {
784 public:
785 Mutex* mu() PW_LOCK_RETURNED(mu) { return &mu; }
786
787 private:
788 Mutex mu;
789 };
790
791.. cpp:function:: PW_NO_LOCK_SAFETY_ANALYSIS()
792
793 Turns off thread safety checking within the body of a particular function.
794 This annotation is used to mark functions that are known to be correct, but
795 the locking behavior is more complicated than the analyzer can handle.
796
797Annotating Lock Objects
798^^^^^^^^^^^^^^^^^^^^^^^
799In order of lock usage annotation to work, the lock objects themselves need to
800be annotated as well. In case you are providing your own lock or psuedo-lock
801object, you can use the macros in this section to annotate it.
802
803As an example we've annotated a Lock and a RAII ScopedLocker object for you, see
804the macro documentation after for more details:
805
806.. code-block:: cpp
807
808 class PW_LOCKABLE("Lock") Lock {
809 public:
810 void Lock() PW_EXCLUSIVE_LOCK_FUNCTION();
811
812 void ReaderLock() PW_SHARED_LOCK_FUNCTION();
813
814 void Unlock() PW_UNLOCK_FUNCTION();
815
816 void ReaderUnlock() PW_SHARED_TRYLOCK_FUNCTION();
817
818 bool TryLock() PW_EXCLUSIVE_TRYLOCK_FUNCTION(true);
819
820 bool ReaderTryLock() PW_SHARED_TRYLOCK_FUNCTION(true);
821
822 void AssertHeld() PW_ASSERT_EXCLUSIVE_LOCK();
823
824 void AssertReaderHeld() PW_ASSERT_SHARED_LOCK();
825 };
826
827
828 // Tag types for selecting a constructor.
829 struct adopt_lock_t {} inline constexpr adopt_lock = {};
830 struct defer_lock_t {} inline constexpr defer_lock = {};
831 struct shared_lock_t {} inline constexpr shared_lock = {};
832
833 class PW_SCOPED_LOCKABLE ScopedLocker {
834 // Acquire lock, implicitly acquire *this and associate it with lock.
835 ScopedLocker(Lock *lock) PW_EXCLUSIVE_LOCK_FUNCTION(lock)
836 : lock_(lock), locked(true) {
837 lock->Lock();
838 }
839
840 // Assume lock is held, implicitly acquire *this and associate it with lock.
841 ScopedLocker(Lock *lock, adopt_lock_t) PW_EXCLUSIVE_LOCKS_REQUIRED(lock)
842 : lock_(lock), locked(true) {}
843
844 // Acquire lock in shared mode, implicitly acquire *this and associate it
845 // with lock.
846 ScopedLocker(Lock *lock, shared_lock_t) PW_SHARED_LOCK_FUNCTION(lock)
847 : lock_(lock), locked(true) {
848 lock->ReaderLock();
849 }
850
851 // Assume lock is held in shared mode, implicitly acquire *this and associate
852 // it with lock.
853 ScopedLocker(Lock *lock, adopt_lock_t, shared_lock_t)
854 PW_SHARED_LOCKS_REQUIRED(lock) : lock_(lock), locked(true) {}
855
856 // Assume lock is not held, implicitly acquire *this and associate it with
857 // lock.
858 ScopedLocker(Lock *lock, defer_lock_t) PW_LOCKS_EXCLUDED(lock)
859 : lock_(lock), locked(false) {}
860
861 // Release *this and all associated locks, if they are still held.
862 // There is no warning if the scope was already unlocked before.
863 ~ScopedLocker() PW_UNLOCK_FUNCTION() {
864 if (locked)
865 lock_->GenericUnlock();
866 }
867
868 // Acquire all associated locks exclusively.
869 void Lock() PW_EXCLUSIVE_LOCK_FUNCTION() {
870 lock_->Lock();
871 locked = true;
872 }
873
874 // Try to acquire all associated locks exclusively.
875 bool TryLock() PW_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
876 return locked = lock_->TryLock();
877 }
878
879 // Acquire all associated locks in shared mode.
880 void ReaderLock() PW_SHARED_LOCK_FUNCTION() {
881 lock_->ReaderLock();
882 locked = true;
883 }
884
885 // Try to acquire all associated locks in shared mode.
886 bool ReaderTryLock() PW_SHARED_TRYLOCK_FUNCTION(true) {
887 return locked = lock_->ReaderTryLock();
888 }
889
890 // Release all associated locks. Warn on double unlock.
891 void Unlock() PW_UNLOCK_FUNCTION() {
892 lock_->Unlock();
893 locked = false;
894 }
895
896 // Release all associated locks. Warn on double unlock.
897 void ReaderUnlock() PW_UNLOCK_FUNCTION() {
898 lock_->ReaderUnlock();
899 locked = false;
900 }
901
902 private:
903 Lock* lock_;
904 bool locked_;
905 };
906
907.. cpp:function:: PW_LOCKABLE(name)
908
909 Documents if a class/type is a lockable type (such as the ``pw::sync::Mutex``
910 class). The name is used in the warning messages. This can also be useful on
911 classes which have locking like semantics but aren't actually locks.
912
913.. cpp:function:: PW_SCOPED_LOCKABLE()
914
915 Documents if a class does RAII locking. The name is used in the warning
916 messages.
917
918 The constructor should use ``LOCK_FUNCTION()`` to specify the lock that is
919 acquired, and the destructor should use ``UNLOCK_FUNCTION()`` with no
920 arguments; the analysis will assume that the destructor unlocks whatever the
921 constructor locked.
922
923.. cpp:function:: PW_EXCLUSIVE_LOCK_FUNCTION()
924
925 Documents functions that acquire a lock in the body of a function, and do
926 not release it.
927
928.. cpp:function:: PW_SHARED_LOCK_FUNCTION()
929
930 Documents functions that acquire a shared (reader) lock in the body of a
931 function, and do not release it.
932
933.. cpp:function:: PW_UNLOCK_FUNCTION()
934
935 Documents functions that expect a lock to be held on entry to the function,
936 and release it in the body of the function.
937
938.. cpp:function:: PW_EXCLUSIVE_TRYLOCK_FUNCTION(try_success)
939.. cpp:function:: PW_SHARED_TRYLOCK_FUNCTION(try_success)
940
941 Documents functions that try to acquire a lock, and return success or failure
942 (or a non-boolean value that can be interpreted as a boolean).
943 The first argument should be ``true`` for functions that return ``true`` on
944 success, or ``false`` for functions that return `false` on success. The second
945 argument specifies the lock that is locked on success. If unspecified, this
946 lock is assumed to be ``this``.
947
948.. cpp:function:: PW_ASSERT_EXCLUSIVE_LOCK()
949.. cpp:function:: PW_ASSERT_SHARED_LOCK()
950
951 Documents functions that dynamically check to see if a lock is held, and fail
952 if it is not held.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800953
954--------------------
955Signaling Primitives
956--------------------
957
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -0700958Native signaling primitives tend to vary more compared to critial section locks
959across different platforms. For example, although common signaling primtives
960like semaphores are in most if not all RTOSes and even POSIX, it was not in the
961STL before C++20. Likewise many C++ developers are surprised that conditional
962variables tend to not be natively supported on RTOSes. Although you can usually
963build any signaling primitive based on other native signaling primitives, this
964may come with non-trivial added overhead in ROM, RAM, and execution efficiency.
965
966For this reason, Pigweed intends to provide some "simpler" signaling primitives
967which exist to solve a narrow programming need but can be implemented as
968efficiently as possible for the platform that it is used on.
969
970This simpler but highly portable class of signaling primitives is intended to
971ensure that a portability efficiency tradeoff does not have to be made up front.
Ewout van Bekkumcc756c82021-05-12 07:57:43 -0700972For example we intend to provide a ``pw::sync::ThreadNotification`` facade which
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -0700973permits a singler consumer to block until an event occurs. This should be
974backed by the most efficient native primitive for a target, regardless of
975whether that is a semaphore, event flag group, condition variable, or something
976else.
977
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800978CountingSemaphore
979=================
980The CountingSemaphore is a synchronization primitive that can be used for
981counting events and/or resource management where receiver(s) can block on
982acquire until notifier(s) signal by invoking release.
983
984Note that unlike Mutexes, priority inheritance is not used by semaphores meaning
985semaphores are subject to unbounded priority inversions. Due to this, Pigweed
986does not recommend semaphores for mutual exclusion.
987
988The CountingSemaphore is initialized to being empty or having no tokens.
989
990The entire API is thread safe, but only a subset is interrupt safe. None of it
991is NMI safe.
992
993.. Warning::
994 Releasing multiple tokens is often not natively supported, meaning you may
995 end up invoking the native kernel API many times, i.e. once per token you
996 are releasing!
997
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -0700998.. list-table::
999
1000 * - *Supported on*
1001 - *Backend module*
1002 * - FreeRTOS
1003 - :ref:`module-pw_sync_freertos`
1004 * - ThreadX
1005 - :ref:`module-pw_sync_threadx`
1006 * - embOS
1007 - :ref:`module-pw_sync_embos`
1008 * - STL
1009 - :ref:`module-pw_sync_stl`
1010 * - Zephyr
1011 - Planned
1012 * - CMSIS-RTOS API v2 & RTX5
1013 - Planned
1014
Ewout van Bekkumf84638b2021-03-12 16:09:08 -08001015BinarySemaphore
1016===============
1017BinarySemaphore is a specialization of CountingSemaphore with an arbitrary token
1018limit of 1. Note that that ``max()`` is >= 1, meaning it may be released up to
1019``max()`` times but only acquired once for those N releases.
1020
1021Implementations of BinarySemaphore are typically more efficient than the
1022default implementation of CountingSemaphore.
1023
1024The BinarySemaphore is initialized to being empty or having no tokens.
1025
1026The entire API is thread safe, but only a subset is interrupt safe. None of it
1027is NMI safe.
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -07001028
1029.. list-table::
1030
1031 * - *Supported on*
1032 - *Backend module*
1033 * - FreeRTOS
1034 - :ref:`module-pw_sync_freertos`
1035 * - ThreadX
1036 - :ref:`module-pw_sync_threadx`
1037 * - embOS
1038 - :ref:`module-pw_sync_embos`
1039 * - STL
1040 - :ref:`module-pw_sync_stl`
1041 * - Zephyr
1042 - Planned
1043 * - CMSIS-RTOS API v2 & RTX5
1044 - Planned
1045
Ewout van Bekkumcc756c82021-05-12 07:57:43 -07001046Conditional Variables
1047=====================
1048We've decided for now to skip on conditional variables. These are constructs,
1049which are typically not natively available on RTOSes. CVs would have to be
1050backed by a multiple hidden semaphore(s) in addition to the explicit public
1051mutex. In other words a CV typically ends up as a a composition of
1052synchronization primitives on RTOSes. That being said, one could implement them
1053using our semaphore and mutex layers and we may consider providing this in the
1054future. However for most of our resource constrained customers they will mostly
1055likely be using semaphores more often than CVs.
1056
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -07001057Coming Soon
1058===========
1059We are intending to provide facades for:
1060
Ewout van Bekkumcc756c82021-05-12 07:57:43 -07001061* ``pw::sync::ThreadNotification``: A portable abstraction to allow a single
1062 thread to receive notification of a single occurrence of a single event.
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -07001063
1064* ``pw::sync::EventGroup`` A facade for a common primitive on RTOSes like
1065 FreeRTOS, RTX5, ThreadX, and embOS which permit threads and interrupts to
1066 signal up to 32 events. This permits others threads to be notified when either
1067 any or some combination of these events have been signaled. This is frequently
1068 used as an alternative to a set of binary semaphore(s). This is not supported
1069 natively on Zephyr.