blob: fd2bef8ea29752250c5fcd2877957abab6037083 [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.. Warning::
11 This module is still under construction, the API is not yet stable.
12
13.. Note::
14 The objects in this module do not have an Init() style public API which is
15 common in many RTOS C APIs. Instead, they rely on being able to invoke the
16 native initialization APIs for synchronization primitives during C++
17 construction.
18 In order to support global statically constructed synchronization without
19 constexpr constructors, the user and/or backend **MUST** ensure that any
20 initialization required in your environment is done prior to the creation
21 and/or initialization of the native synchronization primitives
22 (e.g. kernel initialization).
23
24--------------------------------
25Critical Section Lock Primitives
26--------------------------------
27The critical section lock primitives provided by this module comply with
28`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_,
29`Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_, and where
30relevant
31`TimedLockable <https://en.cppreference.com/w/cpp/named_req/TimedLockable>`_ C++
32named requirements. This means that they are compatible with existing helpers in
33the STL's ``<mutex>`` thread support library. For example `std::lock_guard <https://en.cppreference.com/w/cpp/thread/lock_guard>`_
34and `std::unique_lock <https://en.cppreference.com/w/cpp/thread/unique_lock>`_ can be directly used.
35
36Mutex
37=====
38The Mutex is a synchronization primitive that can be used to protect shared data
39from being simultaneously accessed by multiple threads. It offers exclusive,
40non-recursive ownership semantics where priority inheritance is used to solve
41the classic priority-inversion problem.
42
43The Mutex's API is C++11 STL
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -070044`std::mutex <https://en.cppreference.com/w/cpp/thread/mutex>`_ like,
Ewout van Bekkumf84638b2021-03-12 16:09:08 -080045meaning it is a
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -070046`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_
47and `Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_.
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -070048
49.. list-table::
50
51 * - *Supported on*
52 - *Backend module*
53 * - FreeRTOS
54 - :ref:`module-pw_sync_freertos`
55 * - ThreadX
56 - :ref:`module-pw_sync_threadx`
57 * - embOS
58 - :ref:`module-pw_sync_embos`
59 * - STL
60 - :ref:`module-pw_sync_stl`
61 * - Baremetal
62 - Planned
63 * - Zephyr
64 - Planned
65 * - CMSIS-RTOS API v2 & RTX5
66 - Planned
Ewout van Bekkumf84638b2021-03-12 16:09:08 -080067
68C++
69---
70.. cpp:class:: pw::sync::Mutex
71
72 .. cpp:function:: void lock()
73
74 Locks the mutex, blocking indefinitely. Failures are fatal.
75
76 **Precondition:** The lock isn't already held by this thread. Recursive
77 locking is undefined behavior.
78
79 .. cpp:function:: bool try_lock()
80
Ewout van Bekkum5ff8cc52021-09-07 15:46:36 -070081 Tries to lock the mutex in a non-blocking manner.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -080082 Returns true if the mutex was successfully acquired.
83
84 **Precondition:** The lock isn't already held by this thread. Recursive
85 locking is undefined behavior.
86
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -070087 .. cpp:function:: void unlock()
88
89 Unlocks the mutex. Failures are fatal.
90
91 **Precondition:** The mutex is held by this thread.
92
93
94 .. list-table::
95
96 * - *Safe to use in context*
97 - *Thread*
98 - *Interrupt*
99 - *NMI*
100 * - ``Mutex::Mutex``
101 - ✔
102 -
103 -
104 * - ``Mutex::~Mutex``
105 - ✔
106 -
107 -
108 * - ``void Mutex::lock``
109 - ✔
110 -
111 -
112 * - ``bool Mutex::try_lock``
113 - ✔
114 -
115 -
116 * - ``void Mutex::unlock``
117 - ✔
118 -
119 -
120
121Examples in C++
122^^^^^^^^^^^^^^^
123.. code-block:: cpp
124
125 #include "pw_sync/mutex.h"
126
127 pw::sync::Mutex mutex;
128
129 void ThreadSafeCriticalSection() {
130 mutex.lock();
131 NotThreadSafeCriticalSection();
132 mutex.unlock();
133 }
134
135
136Alternatively you can use C++'s RAII helpers to ensure you always unlock.
137
138.. code-block:: cpp
139
140 #include <mutex>
141
142 #include "pw_sync/mutex.h"
143
144 pw::sync::Mutex mutex;
145
146 void ThreadSafeCriticalSection() {
147 std::lock_guard lock(mutex);
148 NotThreadSafeCriticalSection();
149 }
150
151
152C
153-
154The Mutex must be created in C++, however it can be passed into C using the
155``pw_sync_Mutex`` opaque struct alias.
156
157.. cpp:function:: void pw_sync_Mutex_Lock(pw_sync_Mutex* mutex)
158
159 Invokes the ``Mutex::lock`` member function on the given ``mutex``.
160
161.. cpp:function:: bool pw_sync_Mutex_TryLock(pw_sync_Mutex* mutex)
162
163 Invokes the ``Mutex::try_lock`` member function on the given ``mutex``.
164
165.. cpp:function:: void pw_sync_Mutex_Unlock(pw_sync_Mutex* mutex)
166
167 Invokes the ``Mutex::unlock`` member function on the given ``mutex``.
168
169.. list-table::
170
171 * - *Safe to use in context*
172 - *Thread*
173 - *Interrupt*
174 - *NMI*
175 * - ``void pw_sync_Mutex_Lock``
176 - ✔
177 -
178 -
179 * - ``bool pw_sync_Mutex_TryLock``
180 - ✔
181 -
182 -
183 * - ``void pw_sync_Mutex_Unlock``
184 - ✔
185 -
186 -
187
188Example in C
189^^^^^^^^^^^^
190.. code-block:: cpp
191
192 #include "pw_sync/mutex.h"
193
194 pw::sync::Mutex mutex;
195
196 extern pw_sync_Mutex mutex; // This can only be created in C++.
197
198 void ThreadSafeCriticalSection(void) {
199 pw_sync_Mutex_Lock(&mutex);
200 NotThreadSafeCriticalSection();
201 pw_sync_Mutex_Unlock(&mutex);
202 }
203
204TimedMutex
205==========
206The TimedMutex is an extension of the Mutex which offers timeout and deadline
207based semantics.
208
209The TimedMutex's API is C++11 STL
210`std::timed_mutex <https://en.cppreference.com/w/cpp/thread/timed_mutex>`_ like,
211meaning it is a
212`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_,
213`Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_, and
214`TimedLockable <https://en.cppreference.com/w/cpp/named_req/TimedLockable>`_.
215
216Note that the ``TimedMutex`` is a derived ``Mutex`` class, meaning that
217a ``TimedMutex`` can be used by someone who needs the basic ``Mutex``. This is
Ewout van Bekkum0bf96252021-08-24 09:44:06 -0700218in contrast to the C++ STL's
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700219`std::timed_mutex <https://en.cppreference.com/w/cpp/thread/timed_mutex>`_.
220
221
222.. list-table::
223
224 * - *Supported on*
225 - *Backend module*
226 * - FreeRTOS
227 - :ref:`module-pw_sync_freertos`
228 * - ThreadX
229 - :ref:`module-pw_sync_threadx`
230 * - embOS
231 - :ref:`module-pw_sync_embos`
232 * - STL
233 - :ref:`module-pw_sync_stl`
234 * - Zephyr
235 - Planned
236 * - CMSIS-RTOS API v2 & RTX5
237 - Planned
238
239C++
240---
241.. cpp:class:: pw::sync::TimedMutex
242
243 .. cpp:function:: void lock()
244
245 Locks the mutex, blocking indefinitely. Failures are fatal.
246
247 **Precondition:** The lock isn't already held by this thread. Recursive
248 locking is undefined behavior.
249
250 .. cpp:function:: bool try_lock()
251
Ewout van Bekkum5ff8cc52021-09-07 15:46:36 -0700252 Tries to lock the mutex in a non-blocking manner.
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700253 Returns true if the mutex was successfully acquired.
254
255 **Precondition:** The lock isn't already held by this thread. Recursive
256 locking is undefined behavior.
257
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800258
Ewout van Bekkum5ff8cc52021-09-07 15:46:36 -0700259 .. cpp:function:: bool try_lock_for(const chrono::SystemClock::duration& timeout)
260
261 Tries to lock the mutex. Blocks until specified the timeout has elapsed or
262 the lock is acquired, whichever comes first.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800263 Returns true if the mutex was successfully acquired.
264
265 **Precondition:** The lock isn't already held by this thread. Recursive
266 locking is undefined behavior.
267
Ewout van Bekkum5ff8cc52021-09-07 15:46:36 -0700268 .. cpp:function:: bool try_lock_until(const chrono::SystemClock::time_point& deadline)
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800269
Ewout van Bekkum5ff8cc52021-09-07 15:46:36 -0700270 Tries to lock the mutex. Blocks until specified deadline has been reached
271 or the lock is acquired, whichever comes first.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800272 Returns true if the mutex was successfully acquired.
273
274 **Precondition:** The lock isn't already held by this thread. Recursive
275 locking is undefined behavior.
276
277 .. cpp:function:: void unlock()
278
279 Unlocks the mutex. Failures are fatal.
280
281 **Precondition:** The mutex is held by this thread.
282
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800283
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700284 .. list-table::
285
286 * - *Safe to use in context*
287 - *Thread*
288 - *Interrupt*
289 - *NMI*
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700290 * - ``TimedMutex::TimedMutex``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700291 - ✔
292 -
293 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700294 * - ``TimedMutex::~TimedMutex``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700295 - ✔
296 -
297 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700298 * - ``void TimedMutex::lock``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700299 - ✔
300 -
301 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700302 * - ``bool TimedMutex::try_lock``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700303 - ✔
304 -
305 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700306 * - ``bool TimedMutex::try_lock_for``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700307 - ✔
308 -
309 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700310 * - ``bool TimedMutex::try_lock_until``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700311 - ✔
312 -
313 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700314 * - ``void TimedMutex::unlock``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700315 - ✔
316 -
317 -
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800318
319Examples in C++
320^^^^^^^^^^^^^^^
321.. code-block:: cpp
322
323 #include "pw_chrono/system_clock.h"
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700324 #include "pw_sync/timed_mutex.h"
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800325
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700326 pw::sync::TimedMutex mutex;
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800327
328 bool ThreadSafeCriticalSectionWithTimeout(
329 const SystemClock::duration timeout) {
330 if (!mutex.try_lock_for(timeout)) {
331 return false;
332 }
333 NotThreadSafeCriticalSection();
334 mutex.unlock();
335 return true;
336 }
337
338
339Alternatively you can use C++'s RAII helpers to ensure you always unlock.
340
341.. code-block:: cpp
342
343 #include <mutex>
344
345 #include "pw_chrono/system_clock.h"
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700346 #include "pw_sync/timed_mutex.h"
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800347
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700348 pw::sync::TimedMutex mutex;
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800349
350 bool ThreadSafeCriticalSectionWithTimeout(
351 const SystemClock::duration timeout) {
352 std::unique_lock lock(mutex, std::defer_lock);
353 if (!lock.try_lock_for(timeout)) {
354 return false;
355 }
356 NotThreadSafeCriticalSection();
357 return true;
358 }
359
360
361
362C
363-
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700364The TimedMutex must be created in C++, however it can be passed into C using the
365``pw_sync_TimedMutex`` opaque struct alias.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800366
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700367.. cpp:function:: void pw_sync_TimedMutex_Lock(pw_sync_TimedMutex* mutex)
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800368
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700369 Invokes the ``TimedMutex::lock`` member function on the given ``mutex``.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800370
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700371.. cpp:function:: bool pw_sync_TimedMutex_TryLock(pw_sync_TimedMutex* mutex)
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800372
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700373 Invokes the ``TimedMutex::try_lock`` member function on the given ``mutex``.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800374
Ewout van Bekkum5ff8cc52021-09-07 15:46:36 -0700375.. cpp:function:: bool pw_sync_TimedMutex_TryLockFor(pw_sync_TimedMutex* mutex, pw_chrono_SystemClock_Duration timeout)
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800376
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700377 Invokes the ``TimedMutex::try_lock_for`` member function on the given ``mutex``.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800378
Ewout van Bekkum5ff8cc52021-09-07 15:46:36 -0700379.. cpp:function:: bool pw_sync_TimedMutex_TryLockUntil(pw_sync_TimedMutex* mutex, pw_chrono_SystemClock_TimePoint deadline)
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800380
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700381 Invokes the ``TimedMutex::try_lock_until`` member function on the given ``mutex``.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800382
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700383.. cpp:function:: void pw_sync_TimedMutex_Unlock(pw_sync_TimedMutex* mutex)
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800384
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700385 Invokes the ``TimedMutex::unlock`` member function on the given ``mutex``.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800386
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700387.. list-table::
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800388
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700389 * - *Safe to use in context*
390 - *Thread*
391 - *Interrupt*
392 - *NMI*
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700393 * - ``void pw_sync_TimedMutex_Lock``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700394 - ✔
395 -
396 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700397 * - ``bool pw_sync_TimedMutex_TryLock``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700398 - ✔
399 -
400 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700401 * - ``bool pw_sync_TimedMutex_TryLockFor``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700402 - ✔
403 -
404 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700405 * - ``bool pw_sync_TimedMutex_TryLockUntil``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700406 - ✔
407 -
408 -
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700409 * - ``void pw_sync_TimedMutex_Unlock``
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700410 - ✔
411 -
412 -
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800413
414Example in C
415^^^^^^^^^^^^
416.. code-block:: cpp
417
418 #include "pw_chrono/system_clock.h"
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700419 #include "pw_sync/timed_mutex.h"
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800420
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700421 pw::sync::TimedMutex mutex;
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800422
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700423 extern pw_sync_TimedMutex mutex; // This can only be created in C++.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800424
425 bool ThreadSafeCriticalSectionWithTimeout(
426 const pw_chrono_SystemClock_Duration timeout) {
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700427 if (!pw_sync_TimedMutex_TryLockFor(&mutex, timeout)) {
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800428 return false;
429 }
430 NotThreadSafeCriticalSection();
Ewout van Bekkum6f5b8fb2021-04-06 16:15:22 -0700431 pw_sync_TimedMutex_Unlock(&mutex);
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800432 return true;
433 }
434
435
436InterruptSpinLock
437=================
438The InterruptSpinLock is a synchronization primitive that can be used to protect
439shared data from being simultaneously accessed by multiple threads and/or
440interrupts as a targeted global lock, with the exception of Non-Maskable
441Interrupts (NMIs). It offers exclusive, non-recursive ownership semantics where
442IRQs up to a backend defined level of "NMIs" will be masked to solve
443priority-inversion.
444
445This InterruptSpinLock relies on built-in local interrupt masking to make it
446interrupt safe without requiring the caller to separately mask and unmask
447interrupts when using this primitive.
448
449Unlike global interrupt locks, this also works safely and efficiently on SMP
450systems. On systems which are not SMP, spinning is not required but some state
451may still be used to detect recursion.
452
453The InterruptSpinLock is a
454`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_
455and
456`Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_.
457
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -0700458.. list-table::
459
460 * - *Supported on*
461 - *Backend module*
462 * - FreeRTOS
463 - :ref:`module-pw_sync_freertos`
464 * - ThreadX
465 - :ref:`module-pw_sync_threadx`
466 * - embOS
467 - :ref:`module-pw_sync_embos`
468 * - STL
469 - :ref:`module-pw_sync_stl`
470 * - Baremetal
471 - Planned, not ready for use
472 * - Zephyr
473 - Planned
474 * - CMSIS-RTOS API v2 & RTX5
475 - Planned
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800476
477C++
478---
479.. cpp:class:: pw::sync::InterruptSpinLock
480
481 .. cpp:function:: void lock()
482
483 Locks the spinlock, blocking indefinitely. Failures are fatal.
484
485 **Precondition:** Recursive locking is undefined behavior.
486
487 .. cpp:function:: bool try_lock()
488
Ewout van Bekkum5ff8cc52021-09-07 15:46:36 -0700489 Tries to lock the spinlock in a non-blocking manner.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800490 Returns true if the spinlock was successfully acquired.
491
492 **Precondition:** Recursive locking is undefined behavior.
493
494 .. cpp:function:: void unlock()
495
496 Unlocks the mutex. Failures are fatal.
497
498 **Precondition:** The spinlock is held by the caller.
499
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700500 .. list-table::
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800501
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700502 * - *Safe to use in context*
503 - *Thread*
504 - *Interrupt*
505 - *NMI*
506 * - ``InterruptSpinLock::InterruptSpinLock``
507 - ✔
508 - ✔
509 -
510 * - ``InterruptSpinLock::~InterruptSpinLock``
511 - ✔
512 - ✔
513 -
514 * - ``void InterruptSpinLock::lock``
515 - ✔
516 - ✔
517 -
518 * - ``bool InterruptSpinLock::try_lock``
519 - ✔
520 - ✔
521 -
522 * - ``void InterruptSpinLock::unlock``
523 - ✔
524 - ✔
525 -
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800526
527Examples in C++
528^^^^^^^^^^^^^^^
529.. code-block:: cpp
530
531 #include "pw_sync/interrupt_spin_lock.h"
532
533 pw::sync::InterruptSpinLock interrupt_spin_lock;
534
535 void InterruptSafeCriticalSection() {
536 interrupt_spin_lock.lock();
537 NotThreadSafeCriticalSection();
538 interrupt_spin_lock.unlock();
539 }
540
541
542Alternatively you can use C++'s RAII helpers to ensure you always unlock.
543
544.. code-block:: cpp
545
546 #include <mutex>
547
548 #include "pw_sync/interrupt_spin_lock.h"
549
550 pw::sync::InterruptSpinLock interrupt_spin_lock;
551
552 void InterruptSafeCriticalSection() {
553 std::lock_guard lock(interrupt_spin_lock);
554 NotThreadSafeCriticalSection();
555 }
556
557
558C
559-
560The InterruptSpinLock must be created in C++, however it can be passed into C using the
561``pw_sync_InterruptSpinLock`` opaque struct alias.
562
563.. cpp:function:: void pw_sync_InterruptSpinLock_Lock(pw_sync_InterruptSpinLock* interrupt_spin_lock)
564
565 Invokes the ``InterruptSpinLock::lock`` member function on the given ``interrupt_spin_lock``.
566
567.. cpp:function:: bool pw_sync_InterruptSpinLock_TryLock(pw_sync_InterruptSpinLock* interrupt_spin_lock)
568
569 Invokes the ``InterruptSpinLock::try_lock`` member function on the given ``interrupt_spin_lock``.
570
571.. cpp:function:: void pw_sync_InterruptSpinLock_Unlock(pw_sync_InterruptSpinLock* interrupt_spin_lock)
572
573 Invokes the ``InterruptSpinLock::unlock`` member function on the given ``interrupt_spin_lock``.
574
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700575.. list-table::
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800576
Ewout van Bekkumfe700662021-04-02 16:48:50 -0700577 * - *Safe to use in context*
578 - *Thread*
579 - *Interrupt*
580 - *NMI*
581 * - ``void pw_sync_InterruptSpinLock_Lock``
582 - ✔
583 - ✔
584 -
585 * - ``bool pw_sync_InterruptSpinLock_TryLock``
586 - ✔
587 - ✔
588 -
589 * - ``void pw_sync_InterruptSpinLock_Unlock``
590 - ✔
591 - ✔
592 -
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800593
594Example in C
595^^^^^^^^^^^^
596.. code-block:: cpp
597
598 #include "pw_chrono/system_clock.h"
599 #include "pw_sync/interrupt_spin_lock.h"
600
601 pw::sync::InterruptSpinLock interrupt_spin_lock;
602
603 extern pw_sync_InterruptSpinLock interrupt_spin_lock; // This can only be created in C++.
604
605 void InterruptSafeCriticalSection(void) {
606 pw_sync_InterruptSpinLock_Lock(&interrupt_spin_lock);
607 NotThreadSafeCriticalSection();
608 pw_sync_InterruptSpinLock_Unlock(&interrupt_spin_lock);
609 }
610
Ewout van Bekkumcc9ef832021-04-08 08:51:16 -0700611Thread Safety Lock Annotations
612==============================
613Pigweed's critical section lock primitives support Clang's thread safety
614analysis extension for C++. The analysis is completely static at compile-time.
615This is only supported when building with Clang. The annotations are no-ops when
616using different compilers.
617
618Pigweed provides the ``pw_sync/lock_annotations.h`` header file with macro
619definitions to allow developers to document the locking policies of
620multi-threaded code. The annotations can also help program analysis tools to
621identify potential thread safety issues.
622
623More information on Clang's thread safety analysis system can be found
624`here <https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>`_.
625
626Enabling Clang's Analysis
627-------------------------
628In order to enable the analysis, Clang requires that the ``-Wthread-safety``
629compilation flag be used. In addition, if any STL components like
630``std::lock_guard`` are used, the STL's built in annotations have to be manually
631enabled, typically by setting the ``_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS``
632macro.
633
634If using GN, the ``pw_build:clang_thread_safety_warnings`` config is provided
635to do this for you, when added to your clang toolchain definition's default
636configs.
637
638Why use lock annotations?
639-------------------------
640Lock annotations can help warn you about potential race conditions in your code
641when using locks: you have to remember to grab lock(s) before entering a
642critical section, yuou have to remember to unlock it when you leave, and you
643have to avoid deadlocks.
644
645Clang's lock annotations let you inform the compiler and anyone reading your
646code which variables are guarded by which locks, which locks should or cannot be
647held when calling which function, which order locks should be acquired in, etc.
648
649Using Lock Annotations
650----------------------
651When referring to locks in the arguments of the attributes, you should
652use variable names or more complex expressions (e.g. ``my_object->lock_``)
653that evaluate to a concrete lock object whenever possible. If the lock
654you want to refer to is not in scope, you may use a member pointer
655(e.g. ``&MyClass::lock_``) to refer to a lock in some (unknown) object.
656
657Annotating Lock Usage
658^^^^^^^^^^^^^^^^^^^^^
659.. cpp:function:: PW_GUARDED_BY(x)
660
661 Documents if a shared field or global variable needs to be protected by a
662 lock. ``PW_GUARDED_BY()`` allows the user to specify a particular lock that
663 should be held when accessing the annotated variable.
664
665 Although this annotation (and ``PW_PT_GUARDED_BY``, below) cannot be applied
666 to local variables, a local variable and its associated lock can often be
667 combined into a small class or struct, thereby allowing the annotation.
668
669 Example:
670
671 .. code-block:: cpp
672
673 class Foo {
674 Mutex mu_;
675 int p1_ PW_GUARDED_BY(mu_);
676 ...
677 };
678
679.. cpp:function:: PW_PT_GUARDED_BY(x)
680
681 Documents if the memory location pointed to by a pointer should be guarded
682 by a lock when dereferencing the pointer.
683
684 Example:
685
686 .. code-block:: cpp
687
688 class Foo {
689 Mutex mu_;
690 int *p1_ PW_PT_GUARDED_BY(mu_);
691 ...
692 };
693
694 Note that a pointer variable to a shared memory location could itself be a
695 shared variable.
696
697 Example:
698
699 .. code-block:: cpp
700
701 // `q_`, guarded by `mu1_`, points to a shared memory location that is
702 // guarded by `mu2_`:
703 int *q_ PW_GUARDED_BY(mu1_) PW_PT_GUARDED_BY(mu2_);
704
705.. cpp:function:: PW_ACQUIRED_AFTER(...)
706.. cpp:function:: PW_ACQUIRED_BEFORE(...)
707
708 Documents the acquisition order between locks that can be held
709 simultaneously by a thread. For any two locks that need to be annotated
710 to establish an acquisition order, only one of them needs the annotation.
711 (i.e. You don't have to annotate both locks with both ``PW_ACQUIRED_AFTER``
712 and ``PW_ACQUIRED_BEFORE``.)
713
714 As with ``PW_GUARDED_BY``, this is only applicable to locks that are shared
715 fields or global variables.
716
717 Example:
718
719 .. code-block:: cpp
720
721 Mutex m1_;
722 Mutex m2_ PW_ACQUIRED_AFTER(m1_);
723
724.. cpp:function:: PW_EXCLUSIVE_LOCKS_REQUIRED(...)
725.. cpp:function:: PW_SHARED_LOCKS_REQUIRED(...)
726
727 Documents a function that expects a lock to be held prior to entry.
728 The lock is expected to be held both on entry to, and exit from, the
729 function.
730
731 An exclusive lock allows read-write access to the guarded data member(s), and
732 only one thread can acquire a lock exclusively at any one time. A shared lock
733 allows read-only access, and any number of threads can acquire a shared lock
734 concurrently.
735
736 Generally, non-const methods should be annotated with
737 ``PW_EXCLUSIVE_LOCKS_REQUIRED``, while const methods should be annotated with
738 ``PW_SHARED_LOCKS_REQUIRED``.
739
740 Example:
741
742 .. code-block:: cpp
743
744 Mutex mu1, mu2;
745 int a PW_GUARDED_BY(mu1);
746 int b PW_GUARDED_BY(mu2);
747
748 void foo() PW_EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... }
749 void bar() const PW_SHARED_LOCKS_REQUIRED(mu1, mu2) { ... }
750
751.. cpp:function:: PW_LOCKS_EXCLUDED(...)
752
753 Documents the locks acquired in the body of the function. These locks
754 cannot be held when calling this function (as Pigweed's default locks are
755 non-reentrant).
756
757 Example:
758
759 .. code-block:: cpp
760
761 Mutex mu;
762 int a PW_GUARDED_BY(mu);
763
764 void foo() PW_LOCKS_EXCLUDED(mu) {
765 mu.lock();
766 ...
767 mu.unlock();
768 }
769
770.. cpp:function:: PW_LOCK_RETURNED(...)
771
772 Documents a function that returns a lock without acquiring it. For example,
773 a public getter method that returns a pointer to a private lock should
774 be annotated with ``PW_LOCK_RETURNED``.
775
776 Example:
777
778 .. code-block:: cpp
779
780 class Foo {
781 public:
782 Mutex* mu() PW_LOCK_RETURNED(mu) { return &mu; }
783
784 private:
785 Mutex mu;
786 };
787
788.. cpp:function:: PW_NO_LOCK_SAFETY_ANALYSIS()
789
790 Turns off thread safety checking within the body of a particular function.
791 This annotation is used to mark functions that are known to be correct, but
792 the locking behavior is more complicated than the analyzer can handle.
793
794Annotating Lock Objects
795^^^^^^^^^^^^^^^^^^^^^^^
796In order of lock usage annotation to work, the lock objects themselves need to
797be annotated as well. In case you are providing your own lock or psuedo-lock
798object, you can use the macros in this section to annotate it.
799
800As an example we've annotated a Lock and a RAII ScopedLocker object for you, see
801the macro documentation after for more details:
802
803.. code-block:: cpp
804
805 class PW_LOCKABLE("Lock") Lock {
806 public:
807 void Lock() PW_EXCLUSIVE_LOCK_FUNCTION();
808
809 void ReaderLock() PW_SHARED_LOCK_FUNCTION();
810
811 void Unlock() PW_UNLOCK_FUNCTION();
812
813 void ReaderUnlock() PW_SHARED_TRYLOCK_FUNCTION();
814
815 bool TryLock() PW_EXCLUSIVE_TRYLOCK_FUNCTION(true);
816
817 bool ReaderTryLock() PW_SHARED_TRYLOCK_FUNCTION(true);
818
819 void AssertHeld() PW_ASSERT_EXCLUSIVE_LOCK();
820
821 void AssertReaderHeld() PW_ASSERT_SHARED_LOCK();
822 };
823
824
825 // Tag types for selecting a constructor.
826 struct adopt_lock_t {} inline constexpr adopt_lock = {};
827 struct defer_lock_t {} inline constexpr defer_lock = {};
828 struct shared_lock_t {} inline constexpr shared_lock = {};
829
830 class PW_SCOPED_LOCKABLE ScopedLocker {
831 // Acquire lock, implicitly acquire *this and associate it with lock.
832 ScopedLocker(Lock *lock) PW_EXCLUSIVE_LOCK_FUNCTION(lock)
833 : lock_(lock), locked(true) {
834 lock->Lock();
835 }
836
837 // Assume lock is held, implicitly acquire *this and associate it with lock.
838 ScopedLocker(Lock *lock, adopt_lock_t) PW_EXCLUSIVE_LOCKS_REQUIRED(lock)
839 : lock_(lock), locked(true) {}
840
841 // Acquire lock in shared mode, implicitly acquire *this and associate it
842 // with lock.
843 ScopedLocker(Lock *lock, shared_lock_t) PW_SHARED_LOCK_FUNCTION(lock)
844 : lock_(lock), locked(true) {
845 lock->ReaderLock();
846 }
847
848 // Assume lock is held in shared mode, implicitly acquire *this and associate
849 // it with lock.
850 ScopedLocker(Lock *lock, adopt_lock_t, shared_lock_t)
851 PW_SHARED_LOCKS_REQUIRED(lock) : lock_(lock), locked(true) {}
852
853 // Assume lock is not held, implicitly acquire *this and associate it with
854 // lock.
855 ScopedLocker(Lock *lock, defer_lock_t) PW_LOCKS_EXCLUDED(lock)
856 : lock_(lock), locked(false) {}
857
858 // Release *this and all associated locks, if they are still held.
859 // There is no warning if the scope was already unlocked before.
860 ~ScopedLocker() PW_UNLOCK_FUNCTION() {
861 if (locked)
862 lock_->GenericUnlock();
863 }
864
865 // Acquire all associated locks exclusively.
866 void Lock() PW_EXCLUSIVE_LOCK_FUNCTION() {
867 lock_->Lock();
868 locked = true;
869 }
870
871 // Try to acquire all associated locks exclusively.
872 bool TryLock() PW_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
873 return locked = lock_->TryLock();
874 }
875
876 // Acquire all associated locks in shared mode.
877 void ReaderLock() PW_SHARED_LOCK_FUNCTION() {
878 lock_->ReaderLock();
879 locked = true;
880 }
881
882 // Try to acquire all associated locks in shared mode.
883 bool ReaderTryLock() PW_SHARED_TRYLOCK_FUNCTION(true) {
884 return locked = lock_->ReaderTryLock();
885 }
886
887 // Release all associated locks. Warn on double unlock.
888 void Unlock() PW_UNLOCK_FUNCTION() {
889 lock_->Unlock();
890 locked = false;
891 }
892
893 // Release all associated locks. Warn on double unlock.
894 void ReaderUnlock() PW_UNLOCK_FUNCTION() {
895 lock_->ReaderUnlock();
896 locked = false;
897 }
898
899 private:
900 Lock* lock_;
901 bool locked_;
902 };
903
904.. cpp:function:: PW_LOCKABLE(name)
905
906 Documents if a class/type is a lockable type (such as the ``pw::sync::Mutex``
907 class). The name is used in the warning messages. This can also be useful on
908 classes which have locking like semantics but aren't actually locks.
909
910.. cpp:function:: PW_SCOPED_LOCKABLE()
911
912 Documents if a class does RAII locking. The name is used in the warning
913 messages.
914
915 The constructor should use ``LOCK_FUNCTION()`` to specify the lock that is
916 acquired, and the destructor should use ``UNLOCK_FUNCTION()`` with no
917 arguments; the analysis will assume that the destructor unlocks whatever the
918 constructor locked.
919
920.. cpp:function:: PW_EXCLUSIVE_LOCK_FUNCTION()
921
922 Documents functions that acquire a lock in the body of a function, and do
923 not release it.
924
925.. cpp:function:: PW_SHARED_LOCK_FUNCTION()
926
927 Documents functions that acquire a shared (reader) lock in the body of a
928 function, and do not release it.
929
930.. cpp:function:: PW_UNLOCK_FUNCTION()
931
932 Documents functions that expect a lock to be held on entry to the function,
933 and release it in the body of the function.
934
935.. cpp:function:: PW_EXCLUSIVE_TRYLOCK_FUNCTION(try_success)
936.. cpp:function:: PW_SHARED_TRYLOCK_FUNCTION(try_success)
937
938 Documents functions that try to acquire a lock, and return success or failure
939 (or a non-boolean value that can be interpreted as a boolean).
940 The first argument should be ``true`` for functions that return ``true`` on
941 success, or ``false`` for functions that return `false` on success. The second
942 argument specifies the lock that is locked on success. If unspecified, this
943 lock is assumed to be ``this``.
944
945.. cpp:function:: PW_ASSERT_EXCLUSIVE_LOCK()
946.. cpp:function:: PW_ASSERT_SHARED_LOCK()
947
948 Documents functions that dynamically check to see if a lock is held, and fail
949 if it is not held.
Ewout van Bekkumf84638b2021-03-12 16:09:08 -0800950
Ewout van Bekkumf6c3a782021-08-31 11:23:35 -0700951-----------------------------
952Critical Section Lock Helpers
953-----------------------------
954
Ewout van Bekkumbaf2fdc2021-09-09 14:11:51 -0700955Virtual Lock Interfaces
956=======================
957Virtual lock interfaces can be useful when lock selection cannot be templated.
958
959Why use virtual locks?
960----------------------
961Virtual locks enable depending on locks without templating implementation code
962on the type, while retaining flexibility with respect to the concrete lock type.
963Pigweed tries to avoid pushing policy on to users, and virtual locks are one way
964to accomplish that without templating everything.
965
966A case when virtual locks are useful is when the concrete lock type changes at
967run time. For example, access to flash may be protected at run time by an
968internal mutex, however at crash time we may want to switch to a no-op lock. A
969virtual lock interface could be used here to minimize the code-size cost that
970would occur otherwise if the flash driver were templated.
971
972VirtualBasicLock
973----------------
974The ``VirtualBasicLock`` interface meets the
975`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_ C++
976named requirement. Our critical section lock primitives offer optional virtual
977versions, including:
978
979* ``pw::sync::VirtualMutex``
980* ``pw::sync::VirtualTimedMutex``
981* ``pw::sync::VirtualInterruptSpinLock``
982
Ewout van Bekkumf6c3a782021-08-31 11:23:35 -0700983Borrowable
984==========
985The Borrowable is a helper construct that enables callers to borrow an object
986which is guarded by a lock, enabling a containerized style of external locking.
987
988Users who need access to the guarded object can ask to acquire a
989``BorrowedPointer`` which permits access while the lock is held.
990
991This class is compatible with locks which comply with
992`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_,
993`Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_, and
994`TimedLockable <https://en.cppreference.com/w/cpp/named_req/TimedLockable>`_
995C++ named requirements.
996
Ewout van Bekkumbaf2fdc2021-09-09 14:11:51 -0700997By default the selected lock type is a ``pw::sync::VirtualBasicLockable``. If
998this virtual interface is used, the templated lock parameter can be skipped.
999
Ewout van Bekkumf6c3a782021-08-31 11:23:35 -07001000External vs Internal locking
1001----------------------------
1002Before we explain why Borrowable is useful, it's important to understand the
1003trade-offs when deciding on using internal and/or external locking.
1004
1005Internal locking is when the lock is hidden from the caller entirely and is used
1006internally to the API. For example:
1007
1008.. code-block:: cpp
1009
1010 class BankAccount {
1011 public:
1012 void Deposit(int amount) {
1013 std::lock_guard lock(mutex_);
1014 balance_ += amount;
1015 }
1016
1017 void Withdraw(int amount) {
1018 std::lock_guard lock(mutex_);
1019 balance_ -= amount;
1020 }
1021
1022 void Balance() const {
1023 std::lock_guard lock(mutex_);
1024 return balance_;
1025 }
1026
1027 private:
1028 int balance_ PW_GUARDED_BY(mutex_);
1029 pw::sync::Mutex mutex_;
1030 };
1031
1032Internal locking guarantees that any concurrent calls to its public member
1033functions don't corrupt an instance of that class. This is typically ensured by
1034having each member function acquire a lock on the object upon entry. This way,
1035for any instance, there can only be one member function call active at any
1036moment, serializing the operations.
1037
1038One common issue that pops up is that member functions may have to call other
1039member functions which also require locks. This typically results in a
1040duplication of the public API into an internal mirror where the lock is already
1041held. This along with having to modify every thread-safe public member function
1042may results in an increased code size.
1043
1044However, with the per-method locking approach, it is not possible to perform a
1045multi-method thread-safe transaction. For example, what if we only wanted to
1046withdraw money if the balance was high enough? With the current API there would
1047be a risk that money is withdrawn after we've checked the balance.
1048
1049This is usually why external locking is used. This is when the lock is exposed
1050to the caller and may be used externally to the public API. External locking
1051can take may forms which may even include mixing internal and external locking.
1052In its most simplistic form it is an external lock used along side each
1053instance, e.g.:
1054
1055.. code-block:: cpp
1056
1057 class BankAccount {
1058 public:
1059 void Deposit(int amount) {
1060 balance_ += amount;
1061 }
1062
1063 void Withdraw(int amount) {
1064 balance_ -= amount;
1065 }
1066
1067 void Balance() const {
1068 return balance_;
1069 }
1070
1071 private:
1072 int balance_;
1073 };
1074
1075 pw::sync::Mutex bobs_account_mutex;
1076 BankAccount bobs_account PW_GUARDED_BY(bobs_account_mutex);
1077
1078The lock is acquired before the bank account is used for a transaction. In
1079addition, we do not have to modify every public function and its trivial to
1080call other public member functions from a public member function. However, as
1081you can imagine instantiating and passing around the instances and their locks
1082can become error prone.
1083
1084This is why ``Borrowable`` exists.
1085
1086Why use Borrowable?
1087-------------------
1088``Borrowable`` offers code-size efficient way to enable external locking that is
1089easy and safe to use. It is effectively a container which holds references to a
1090protected instance and its lock which provides RAII-style access.
1091
1092.. code-block:: cpp
1093
1094 pw::sync::Mutex bobs_account_mutex;
1095 BankAccount bobs_account PW_GUARDED_BY(bobs_account_mutex);
1096 pw::sync::Borrowable<BankAccount&, pw::sync::Mutex> bobs_acount(
1097 bobs_account_mutex, bobs_account);
1098
1099This construct is useful when sharing objects or data which are transactional in
1100nature where making individual operations threadsafe is insufficient. See the
1101section on internal vs external locking tradeoffs above.
1102
1103It can also offer a code-size and stack-usage efficient way to separate timeout
1104constraints between the acquiring of the shared object and timeouts used for the
1105shared object's API. For example, imagine you have an I2c bus which is used by
1106several threads and you'd like to specify an ACK timeout of 50ms. It'd be ideal
1107if the duration it takes to gain exclusive access to the I2c bus does not eat
1108into the ACK timeout you'd like to use for the transaction. Borrowable can help
1109you do exactly this if you provide access to the I2c bus through a
1110``Borrowable``.
1111
1112C++
1113---
Ewout van Bekkumbaf2fdc2021-09-09 14:11:51 -07001114.. cpp:class:: template <typename GuardedType, typename Lock = pw::sync::VirtualBasicLockable> pw::sync::BorrowedPointer
Ewout van Bekkumf6c3a782021-08-31 11:23:35 -07001115
1116 The BorrowedPointer is an RAII handle which wraps a pointer to a borrowed
1117 object along with a held lock which is guarding the object. When destroyed,
1118 the lock is released.
1119
1120 This object is moveable, but not copyable.
1121
1122 .. cpp:function:: GuardedType* operator->()
1123
1124 Provides access to the borrowed object's members.
1125
1126 .. cpp:function:: GuardedType& operator*()
1127
1128 Provides access to the borrowed object directly.
1129
1130 **Warning:** The member of pointer member access operator, operator->(), is
1131 recommended over this API as this is prone to leaking references. However,
1132 this is sometimes necessary.
1133
1134 **Warning:** Be careful not to leak references to the borrowed object.
1135
Ewout van Bekkumbaf2fdc2021-09-09 14:11:51 -07001136.. cpp:class:: template <typename GuardedReference, typename Lock = pw::sync::VirtualBasicLockable> pw::sync::Borrowable
Ewout van Bekkumf6c3a782021-08-31 11:23:35 -07001137
1138 .. cpp:function:: BorrowedPointer<GuardedType, Lock> acquire()
1139
1140 Blocks indefinitely until the object can be borrowed. Failures are fatal.
1141
1142 .. cpp:function:: std::optional<BorrowedPointer<GuardedType, Lock>> try_acquire()
1143
1144 Tries to borrow the object in a non-blocking manner. Returns a
1145 BorrowedPointer on success, otherwise std::nullopt (nothing).
1146
1147 .. cpp:function:: template <class Rep, class Period> std::optional<BorrowedPointer<GuardedType, Lock>> try_acquire_for(std::chrono::duration<Rep, Period> timeout)
1148
1149 Tries to borrow the object. Blocks until the specified timeout has elapsed
1150 or the object has been borrowed, whichever comes first. Returns a
1151 BorrowedPointer on success, otherwise std::nullopt (nothing).
1152
1153 .. cpp:function:: template <class Rep, class Period> std::optional<BorrowedPointer<GuardedType, Lock>> try_acquire_until(std::chrono::duration<Rep, Period> deadline)
1154
1155 Tries to borrow the object. Blocks until the specified deadline has been
1156 reached or the object has been borrowed, whichever comes first. Returns a
1157 BorrowedPointer on success, otherwise std::nullopt (nothing).
1158
1159Example in C++
1160^^^^^^^^^^^^^^
1161
1162.. code-block:: cpp
1163
1164 #include <chrono>
1165
1166 #include "pw_bytes/span.h"
1167 #include "pw_i2c/initiator.h"
1168 #include "pw_status/try.h"
1169 #include "pw_status/result.h"
1170 #include "pw_sync/borrow.h"
1171 #include "pw_sync/mutex.h"
1172
1173 class ExampleI2c : public pw::i2c::Initiator;
1174
Ewout van Bekkumbaf2fdc2021-09-09 14:11:51 -07001175 pw::sync::VirtualMutex i2c_mutex;
Ewout van Bekkumf6c3a782021-08-31 11:23:35 -07001176 ExampleI2c i2c;
Ewout van Bekkumbaf2fdc2021-09-09 14:11:51 -07001177 pw::sync::Borrowable<ExampleI2c&> borrowable_i2c(i2c_mutex, i2c);
Ewout van Bekkumf6c3a782021-08-31 11:23:35 -07001178
1179 pw::Result<ConstByteSpan> ReadI2cData(ByteSpan buffer) {
1180 // Block indefinitely waiting to borrow the i2c bus.
Ewout van Bekkumbaf2fdc2021-09-09 14:11:51 -07001181 pw::sync::BorrowedPointer<ExampleI2c> borrowed_i2c =
Ewout van Bekkumf6c3a782021-08-31 11:23:35 -07001182 borrowable_i2c.acquire();
1183
1184 // Execute a sequence of transactions to get the needed data.
1185 PW_TRY(borrowed_i2c->WriteFor(kFirstWrite, std::chrono::milliseconds(50)));
1186 PW_TRY(borrowed_i2c->WriteReadFor(kSecondWrite, buffer,
1187 std::chrono::milliseconds(10)));
1188
1189 // Borrowed i2c pointer is returned when the scope exits.
1190 return buffer;
1191 }
1192
Ewout van Bekkumf84638b2021-03-12 16:09:08 -08001193--------------------
1194Signaling Primitives
1195--------------------
1196
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -07001197Native signaling primitives tend to vary more compared to critial section locks
1198across different platforms. For example, although common signaling primtives
1199like semaphores are in most if not all RTOSes and even POSIX, it was not in the
1200STL before C++20. Likewise many C++ developers are surprised that conditional
1201variables tend to not be natively supported on RTOSes. Although you can usually
1202build any signaling primitive based on other native signaling primitives, this
1203may come with non-trivial added overhead in ROM, RAM, and execution efficiency.
1204
Ewout van Bekkumf0106062021-05-06 14:08:33 -07001205For this reason, Pigweed intends to provide some simpler signaling primitives
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -07001206which exist to solve a narrow programming need but can be implemented as
1207efficiently as possible for the platform that it is used on.
1208
1209This simpler but highly portable class of signaling primitives is intended to
1210ensure that a portability efficiency tradeoff does not have to be made up front.
Ewout van Bekkumf0106062021-05-06 14:08:33 -07001211Today this is class of simpler signaling primitives is limited to the
1212``pw::sync::ThreadNotification`` and ``pw::sync::TimedThreadNotification``.
1213
1214ThreadNotification
1215==================
1216The ThreadNotification is a synchronization primitive that can be used to
1217permit a SINGLE thread to block and consume a latching, saturating
1218notification from multiple notifiers.
1219
Ewout van Bekkum0bf96252021-08-24 09:44:06 -07001220.. Note::
1221 Although only a single thread can block on a ThreadNotification at a time,
1222 many instances may be used by a single thread just like binary semaphores.
1223 This is in contrast to some native RTOS APIs, such as direct task
1224 notifications, which re-use the same state within a thread's context.
1225
Ewout van Bekkumf0106062021-05-06 14:08:33 -07001226.. Warning::
1227 This is a single consumer/waiter, multiple producer/notifier API!
1228 The acquire APIs must only be invoked by a single consuming thread. As a
1229 result, having multiple threads receiving notifications via the acquire API
1230 is unsupported.
1231
1232This is effectively a subset of the ``pw::sync::BinarySemaphore`` API, except
1233that only a single thread can be notified and block at a time.
1234
1235The single consumer aspect of the API permits the use of a smaller and/or
1236faster native APIs such as direct thread signaling. This should be
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -07001237backed by the most efficient native primitive for a target, regardless of
1238whether that is a semaphore, event flag group, condition variable, or something
1239else.
1240
Ewout van Bekkumf0106062021-05-06 14:08:33 -07001241Generic BinarySemaphore-based Backend
1242-------------------------------------
1243This module provides a generic backend for ``pw::sync::ThreadNotification`` via
1244``pw_sync:binary_semaphore_thread_notification`` which uses a
1245``pw::sync::BinarySemaphore`` as the backing primitive. See
1246:ref:`BinarySemaphore <module-pw_sync-binary-semaphore>` for backend
1247availability.
1248
1249Optimized Backend
1250-----------------
1251.. list-table::
1252
1253 * - *Supported on*
1254 - *Optimized backend module*
1255 * - FreeRTOS
1256 - Planned
1257 * - ThreadX
1258 - Planned
1259 * - embOS
1260 - Planned
1261 * - STL
1262 - Not planned, use ``pw_sync:binary_semaphore_thread_notification``
1263 * - Baremetal
1264 - Planned
1265 * - Zephyr
1266 - Planned
1267 * - CMSIS-RTOS API v2 & RTX5
1268 - Planned
1269
1270C++
1271---
1272.. cpp:class:: pw::sync::ThreadNotification
1273
1274 .. cpp:function:: void acquire()
1275
1276 Blocks indefinitely until the thread is notified, i.e. until the
1277 notification latch can be cleared because it was set.
1278
1279 Clears the notification latch.
1280
1281 **IMPORTANT:** This should only be used by a single consumer thread.
1282
1283 .. cpp:function:: bool try_acquire()
1284
1285 Returns whether the thread has been notified, i.e. whether the notificion
1286 latch was set and resets the latch regardless.
1287
1288 Clears the notification latch.
1289
1290 Returns true if the thread was notified, meaning the the internal latch was
1291 reset successfully.
1292
1293 **IMPORTANT:** This should only be used by a single consumer thread.
1294
1295 .. cpp:function:: void release()
1296
1297 Notifies the thread in a saturating manner, setting the notification latch.
1298
1299 Raising the notification multiple time without it being acquired by the
1300 consuming thread is equivalent to raising the notification once to the
1301 thread. The notification is latched in case the thread was not waiting at
1302 the time.
1303
1304 This is IRQ and thread safe.
1305
1306 .. list-table::
1307
1308 * - *Safe to use in context*
1309 - *Thread*
1310 - *Interrupt*
1311 - *NMI*
1312 * - ``ThreadNotification::ThreadNotification``
1313 - ✔
1314 -
1315 -
1316 * - ``ThreadNotification::~ThreadNotification``
1317 - ✔
1318 -
1319 -
1320 * - ``void ThreadNotification::acquire``
1321 - ✔
1322 -
1323 -
1324 * - ``bool ThreadNotification::try_acquire``
1325 - ✔
1326 -
1327 -
1328 * - ``void ThreadNotification::release``
1329 - ✔
1330 - ✔
1331 -
1332
1333Examples in C++
1334^^^^^^^^^^^^^^^
1335.. code-block:: cpp
1336
1337 #include "pw_sync/thread_notification.h"
1338 #include "pw_thread/thread_core.h"
1339
1340 class FooHandler() : public pw::thread::ThreadCore {
1341 // Public API invoked by other threads and/or interrupts.
1342 void NewFooAvailable() {
1343 new_foo_notification_.release();
1344 }
1345
1346 private:
1347 pw::sync::ThreadNotification new_foo_notification_;
1348
1349 // Thread function.
1350 void Run() override {
1351 while (true) {
1352 new_foo_notification_.acquire();
1353 HandleFoo();
1354 }
1355 }
1356
1357 void HandleFoo();
1358 }
1359
1360TimedThreadNotification
1361=======================
1362The TimedThreadNotification is an extension of the ThreadNotification which
1363offers timeout and deadline based semantics.
1364
1365.. Warning::
1366 This is a single consumer/waiter, multiple producer/notifier API!
1367 The acquire APIs must only be invoked by a single consuming thread. As a
1368 result, having multiple threads receiving notifications via the acquire API
1369 is unsupported.
1370
1371Generic BinarySemaphore-based Backend
1372-------------------------------------
1373This module provides a generic backend for ``pw::sync::TimedThreadNotification``
1374via ``pw_sync:binary_semaphore_timed_thread_notification`` which uses a
1375``pw::sync::BinarySemaphore`` as the backing primitive. See
1376:ref:`BinarySemaphore <module-pw_sync-binary-semaphore>` for backend
1377availability.
1378
1379Optimized Backend
1380-----------------
1381.. list-table::
1382
1383 * - *Supported on*
1384 - *Backend module*
1385 * - FreeRTOS
1386 - Planned
1387 * - ThreadX
1388 - Planned
1389 * - embOS
1390 - Planned
1391 * - STL
1392 - Not planned, use ``pw_sync:binary_semaphore_thread_notification``
1393 * - Zephyr
1394 - Planned
1395 * - CMSIS-RTOS API v2 & RTX5
1396 - Planned
1397
1398C++
1399---
1400.. cpp:class:: pw::sync::TimedThreadNotification
1401
1402 .. cpp:function:: void acquire()
1403
1404 Blocks indefinitely until the thread is notified, i.e. until the
1405 notification latch can be cleared because it was set.
1406
1407 Clears the notification latch.
1408
1409 **IMPORTANT:** This should only be used by a single consumer thread.
1410
1411 .. cpp:function:: bool try_acquire()
1412
1413 Returns whether the thread has been notified, i.e. whether the notificion
1414 latch was set and resets the latch regardless.
1415
1416 Clears the notification latch.
1417
1418 Returns true if the thread was notified, meaning the the internal latch was
1419 reset successfully.
1420
1421 **IMPORTANT:** This should only be used by a single consumer thread.
1422
1423 .. cpp:function:: void release()
1424
1425 Notifies the thread in a saturating manner, setting the notification latch.
1426
1427 Raising the notification multiple time without it being acquired by the
1428 consuming thread is equivalent to raising the notification once to the
1429 thread. The notification is latched in case the thread was not waiting at
1430 the time.
1431
1432 This is IRQ and thread safe.
1433
1434 .. cpp:function:: bool try_acquire_for(chrono::SystemClock::duration timeout)
1435
1436 Blocks until the specified timeout duration has elapsed or the thread
1437 has been notified (i.e. notification latch can be cleared because it was
1438 set), whichever comes first.
1439
1440 Clears the notification latch.
1441
1442 Returns true if the thread was notified, meaning the the internal latch was
1443 reset successfully.
1444
1445 **IMPORTANT:** This should only be used by a single consumer thread.
1446
1447 .. cpp:function:: bool try_acquire_until(chrono::SystemClock::time_point deadline)
1448
1449 Blocks until the specified deadline time has been reached the thread has
1450 been notified (i.e. notification latch can be cleared because it was set),
1451 whichever comes first.
1452
1453 Clears the notification latch.
1454
1455 Returns true if the thread was notified, meaning the the internal latch was
1456 reset successfully.
1457
1458 **IMPORTANT:** This should only be used by a single consumer thread.
1459
1460 .. list-table::
1461
1462 * - *Safe to use in context*
1463 - *Thread*
1464 - *Interrupt*
1465 - *NMI*
1466 * - ``ThreadNotification::ThreadNotification``
1467 - ✔
1468 -
1469 -
1470 * - ``ThreadNotification::~ThreadNotification``
1471 - ✔
1472 -
1473 -
1474 * - ``void ThreadNotification::acquire``
1475 - ✔
1476 -
1477 -
1478 * - ``bool ThreadNotification::try_acquire``
1479 - ✔
1480 -
1481 -
1482 * - ``bool ThreadNotification::try_acquire_for``
1483 - ✔
1484 -
1485 -
1486 * - ``bool ThreadNotification::try_acquire_until``
1487 - ✔
1488 -
1489 -
1490 * - ``void ThreadNotification::release``
1491 - ✔
1492 - ✔
1493 -
1494
1495Examples in C++
1496^^^^^^^^^^^^^^^
1497.. code-block:: cpp
1498
1499 #include "pw_sync/timed_thread_notification.h"
1500 #include "pw_thread/thread_core.h"
1501
1502 class FooHandler() : public pw::thread::ThreadCore {
1503 // Public API invoked by other threads and/or interrupts.
1504 void NewFooAvailable() {
1505 new_foo_notification_.release();
1506 }
1507
1508 private:
1509 pw::sync::TimedThreadNotification new_foo_notification_;
1510
1511 // Thread function.
1512 void Run() override {
1513 while (true) {
1514 if (new_foo_notification_.try_acquire_for(kNotificationTimeout)) {
1515 HandleFoo();
1516 }
1517 DoOtherStuff();
1518 }
1519 }
1520
1521 void HandleFoo();
1522 void DoOtherStuff();
1523 }
1524
Ewout van Bekkumf84638b2021-03-12 16:09:08 -08001525CountingSemaphore
1526=================
1527The CountingSemaphore is a synchronization primitive that can be used for
1528counting events and/or resource management where receiver(s) can block on
1529acquire until notifier(s) signal by invoking release.
1530
1531Note that unlike Mutexes, priority inheritance is not used by semaphores meaning
1532semaphores are subject to unbounded priority inversions. Due to this, Pigweed
1533does not recommend semaphores for mutual exclusion.
1534
1535The CountingSemaphore is initialized to being empty or having no tokens.
1536
1537The entire API is thread safe, but only a subset is interrupt safe. None of it
1538is NMI safe.
1539
1540.. Warning::
1541 Releasing multiple tokens is often not natively supported, meaning you may
1542 end up invoking the native kernel API many times, i.e. once per token you
1543 are releasing!
1544
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -07001545.. list-table::
1546
1547 * - *Supported on*
1548 - *Backend module*
1549 * - FreeRTOS
1550 - :ref:`module-pw_sync_freertos`
1551 * - ThreadX
1552 - :ref:`module-pw_sync_threadx`
1553 * - embOS
1554 - :ref:`module-pw_sync_embos`
1555 * - STL
1556 - :ref:`module-pw_sync_stl`
1557 * - Zephyr
1558 - Planned
1559 * - CMSIS-RTOS API v2 & RTX5
1560 - Planned
1561
Ewout van Bekkumf0106062021-05-06 14:08:33 -07001562.. _module-pw_sync-binary-semaphore:
1563
Ewout van Bekkumf84638b2021-03-12 16:09:08 -08001564BinarySemaphore
1565===============
1566BinarySemaphore is a specialization of CountingSemaphore with an arbitrary token
1567limit of 1. Note that that ``max()`` is >= 1, meaning it may be released up to
1568``max()`` times but only acquired once for those N releases.
1569
1570Implementations of BinarySemaphore are typically more efficient than the
1571default implementation of CountingSemaphore.
1572
1573The BinarySemaphore is initialized to being empty or having no tokens.
1574
1575The entire API is thread safe, but only a subset is interrupt safe. None of it
1576is NMI safe.
Ewout van Bekkum3b9eca42021-04-02 14:54:02 -07001577
1578.. list-table::
1579
1580 * - *Supported on*
1581 - *Backend module*
1582 * - FreeRTOS
1583 - :ref:`module-pw_sync_freertos`
1584 * - ThreadX
1585 - :ref:`module-pw_sync_threadx`
1586 * - embOS
1587 - :ref:`module-pw_sync_embos`
1588 * - STL
1589 - :ref:`module-pw_sync_stl`
1590 * - Zephyr
1591 - Planned
1592 * - CMSIS-RTOS API v2 & RTX5
1593 - Planned
1594
Ewout van Bekkumcc756c82021-05-12 07:57:43 -07001595Conditional Variables
1596=====================
1597We've decided for now to skip on conditional variables. These are constructs,
1598which are typically not natively available on RTOSes. CVs would have to be
1599backed by a multiple hidden semaphore(s) in addition to the explicit public
1600mutex. In other words a CV typically ends up as a a composition of
1601synchronization primitives on RTOSes. That being said, one could implement them
1602using our semaphore and mutex layers and we may consider providing this in the
1603future. However for most of our resource constrained customers they will mostly
1604likely be using semaphores more often than CVs.