xref: /aosp_15_r20/external/pigweed/pw_sync/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_sync:
2
3=======
4pw_sync
5=======
6The ``pw_sync`` module contains utilities for synchronizing between threads
7and/or interrupts through signaling primitives and critical section lock
8primitives.
9
10.. Warning::
11
12   This module is still under construction, the API is not yet stable.
13
14.. Note::
15
16   The objects in this module do not have an Init() style public API which is
17   common in many RTOS C APIs. Instead, they rely on being able to invoke the
18   native initialization APIs for synchronization primitives during C++
19   construction.
20
21   In order to support global statically constructed synchronization without
22   constexpr constructors, the user and/or backend **MUST** ensure that any
23   initialization required in your environment is done prior to the creation
24   and/or initialization of the native synchronization primitives
25   (e.g. kernel initialization).
26
27--------------------------------
28Critical Section Lock Primitives
29--------------------------------
30The critical section lock primitives provided by this module comply with
31`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_,
32`Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_, and where
33relevant
34`TimedLockable <https://en.cppreference.com/w/cpp/named_req/TimedLockable>`_ C++
35named requirements. This means that they are compatible with existing helpers in
36the STL's ``<mutex>`` thread support library. For example `std::lock_guard
37<https://en.cppreference.com/w/cpp/thread/lock_guard>`_ and `std::unique_lock
38<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
48`std::mutex <https://en.cppreference.com/w/cpp/thread/mutex>`_ like,
49meaning it is a
50`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_
51and `Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_.
52
53.. list-table::
54   :header-rows: 1
55
56   * - Supported on
57     - Backend module
58   * - FreeRTOS
59     - :ref:`module-pw_sync_freertos`
60   * - ThreadX
61     - :ref:`module-pw_sync_threadx`
62   * - embOS
63     - :ref:`module-pw_sync_embos`
64   * - STL
65     - :ref:`module-pw_sync_stl`
66   * - Baremetal
67     - Planned
68   * - Zephyr
69     - Planned
70   * - CMSIS-RTOS API v2 & RTX5
71     - Planned
72
73C++
74---
75.. doxygenclass:: pw::sync::Mutex
76   :members:
77
78.. cpp:namespace-push:: pw::sync::Mutex
79
80.. list-table::
81   :header-rows: 1
82   :widths: 70 10 10 10
83
84   * - Safe to use in context
85     - Thread
86     - Interrupt
87     - NMI
88   * - :cpp:class:`pw::sync::Mutex::Mutex`
89     - ✔
90     -
91     -
92   * - :cpp:func:`pw::sync::Mutex::~Mutex`
93     - ✔
94     -
95     -
96   * - :cpp:func:`lock`
97     - ✔
98     -
99     -
100   * - :cpp:func:`try_lock`
101     - ✔
102     -
103     -
104   * - :cpp:func:`unlock`
105     - ✔
106     -
107     -
108
109.. cpp:namespace-pop::
110
111
112Examples in C++
113^^^^^^^^^^^^^^^
114.. code-block:: cpp
115
116   #include "pw_sync/mutex.h"
117
118   pw::sync::Mutex mutex;
119
120   void ThreadSafeCriticalSection() {
121     mutex.lock();
122     NotThreadSafeCriticalSection();
123     mutex.unlock();
124   }
125
126
127Alternatively you can use C++'s RAII helpers to ensure you always unlock.
128
129.. code-block:: cpp
130
131   #include <mutex>
132
133   #include "pw_sync/mutex.h"
134
135   pw::sync::Mutex mutex;
136
137   void ThreadSafeCriticalSection() {
138     std::lock_guard lock(mutex);
139     NotThreadSafeCriticalSection();
140   }
141
142C
143-
144The Mutex must be created in C++, however it can be passed into C using the
145``pw_sync_Mutex`` opaque struct alias.
146
147.. doxygenfunction:: pw_sync_Mutex_Lock
148.. doxygenfunction:: pw_sync_Mutex_TryLock
149.. doxygenfunction:: pw_sync_Mutex_Unlock
150
151.. list-table::
152   :header-rows: 1
153   :widths: 70 10 10 10
154
155   * - Safe to use in context
156     - Thread
157     - Interrupt
158     - NMI
159   * - ``void pw_sync_Mutex_Lock``
160     - ✔
161     -
162     -
163   * - ``bool pw_sync_Mutex_TryLock``
164     - ✔
165     -
166     -
167   * - ``void pw_sync_Mutex_Unlock``
168     - ✔
169     -
170     -
171
172Example in C
173^^^^^^^^^^^^
174.. code-block:: cpp
175
176   #include "pw_sync/mutex.h"
177
178   pw::sync::Mutex mutex;
179
180   extern pw_sync_Mutex mutex;  // This can only be created in C++.
181
182   void ThreadSafeCriticalSection(void) {
183     pw_sync_Mutex_Lock(&mutex);
184     NotThreadSafeCriticalSection();
185     pw_sync_Mutex_Unlock(&mutex);
186   }
187
188TimedMutex
189==========
190.. cpp:namespace-push:: pw::sync
191
192The :cpp:class:`TimedMutex` is an extension of the Mutex which offers timeout
193and deadline based semantics.
194
195The :cpp:class:`TimedMutex`'s API is C++11 STL
196`std::timed_mutex <https://en.cppreference.com/w/cpp/thread/timed_mutex>`_ like,
197meaning it is a
198`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_,
199`Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_, and
200`TimedLockable <https://en.cppreference.com/w/cpp/named_req/TimedLockable>`_.
201
202Note that the :cpp:class:`TimedMutex` is a derived :cpp:class:`Mutex` class,
203meaning that a :cpp:class:`TimedMutex` can be used by someone who needs the
204basic :cpp:class:`Mutex`. This is in contrast to the C++ STL's
205`std::timed_mutex <https://en.cppreference.com/w/cpp/thread/timed_mutex>`_.
206
207.. cpp:namespace-pop::
208
209.. list-table::
210   :header-rows: 1
211
212   * - Supported on
213     - Backend module
214   * - FreeRTOS
215     - :ref:`module-pw_sync_freertos`
216   * - ThreadX
217     - :ref:`module-pw_sync_threadx`
218   * - embOS
219     - :ref:`module-pw_sync_embos`
220   * - STL
221     - :ref:`module-pw_sync_stl`
222   * - Zephyr
223     - Planned
224   * - CMSIS-RTOS API v2 & RTX5
225     - Planned
226
227C++
228---
229.. doxygenclass:: pw::sync::TimedMutex
230   :members:
231
232.. cpp:namespace-push:: pw::sync::TimedMutex
233
234.. list-table::
235   :header-rows: 1
236   :widths: 70 10 10 10
237
238   * - Safe to use in context
239     - Thread
240     - Interrupt
241     - NMI
242   * - :cpp:class:`pw::sync::TimedMutex::TimedMutex`
243     - ✔
244     -
245     -
246   * - :cpp:func:`pw::sync::TimedMutex::~TimedMutex`
247     - ✔
248     -
249     -
250   * - :cpp:func:`pw::sync::Mutex::lock`
251     - ✔
252     -
253     -
254   * - :cpp:func:`pw::sync::Mutex::try_lock`
255     - ✔
256     -
257     -
258   * - :cpp:func:`try_lock_for`
259     - ✔
260     -
261     -
262   * - :cpp:func:`try_lock_until`
263     - ✔
264     -
265     -
266   * - :cpp:func:`pw::sync::Mutex::unlock`
267     - ✔
268     -
269     -
270
271.. cpp:namespace-pop::
272
273
274Examples in C++
275^^^^^^^^^^^^^^^
276.. code-block:: cpp
277
278   #include "pw_chrono/system_clock.h"
279   #include "pw_sync/timed_mutex.h"
280
281   pw::sync::TimedMutex mutex;
282
283   bool ThreadSafeCriticalSectionWithTimeout(
284       const SystemClock::duration timeout) {
285     if (!mutex.try_lock_for(timeout)) {
286       return false;
287     }
288     NotThreadSafeCriticalSection();
289     mutex.unlock();
290     return true;
291   }
292
293Alternatively you can use C++'s RAII helpers to ensure you always unlock.
294
295.. code-block:: cpp
296
297   #include <mutex>
298
299   #include "pw_chrono/system_clock.h"
300   #include "pw_sync/timed_mutex.h"
301
302   pw::sync::TimedMutex mutex;
303
304   bool ThreadSafeCriticalSectionWithTimeout(
305       const SystemClock::duration timeout) {
306     std::unique_lock lock(mutex, std::defer_lock);
307     if (!lock.try_lock_for(timeout)) {
308       return false;
309     }
310     NotThreadSafeCriticalSection();
311     return true;
312   }
313
314C
315-
316The TimedMutex must be created in C++, however it can be passed into C using the
317``pw_sync_TimedMutex`` opaque struct alias.
318
319.. doxygenfile:: timed_mutex.h
320   :sections: func
321
322.. list-table::
323   :header-rows: 1
324   :widths: 70 10 10 10
325
326   * - Safe to use in context
327     - Thread
328     - Interrupt
329     - NMI
330   * - :cpp:func:`pw_sync_TimedMutex_Lock`
331     - ✔
332     -
333     -
334   * - :cpp:func:`pw_sync_TimedMutex_TryLock`
335     - ✔
336     -
337     -
338   * - :cpp:func:`pw_sync_TimedMutex_TryLockFor`
339     - ✔
340     -
341     -
342   * - :cpp:func:`pw_sync_TimedMutex_TryLockUntil`
343     - ✔
344     -
345     -
346   * - :cpp:func:`pw_sync_TimedMutex_Unlock`
347     - ✔
348     -
349     -
350
351Example in C
352^^^^^^^^^^^^
353.. code-block:: cpp
354
355   #include "pw_chrono/system_clock.h"
356   #include "pw_sync/timed_mutex.h"
357
358   pw::sync::TimedMutex mutex;
359
360   extern pw_sync_TimedMutex mutex;  // This can only be created in C++.
361
362   bool ThreadSafeCriticalSectionWithTimeout(
363       const pw_chrono_SystemClock_Duration timeout) {
364     if (!pw_sync_TimedMutex_TryLockFor(&mutex, timeout)) {
365       return false;
366     }
367     NotThreadSafeCriticalSection();
368     pw_sync_TimedMutex_Unlock(&mutex);
369     return true;
370   }
371
372RecursiveMutex
373==============
374``pw_sync`` provides ``pw::sync::RecursiveMutex``, a recursive mutex
375implementation. At this time, this facade can only be used internally by
376Pigweed.
377
378InterruptSpinLock
379=================
380The InterruptSpinLock is a synchronization primitive that can be used to protect
381shared data from being simultaneously accessed by multiple threads and/or
382interrupts as a targeted global lock, with the exception of Non-Maskable
383Interrupts (NMIs). It offers exclusive, non-recursive ownership semantics where
384IRQs up to a backend defined level of "NMIs" will be masked to solve
385priority-inversion.
386
387This InterruptSpinLock relies on built-in local interrupt masking to make it
388interrupt safe without requiring the caller to separately mask and unmask
389interrupts when using this primitive.
390
391Unlike global interrupt locks, this also works safely and efficiently on SMP
392systems. On systems which are not SMP, spinning is not required but some state
393may still be used to detect recursion.
394
395The InterruptSpinLock is a
396`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_
397and
398`Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_.
399
400.. list-table::
401   :header-rows: 1
402
403   * - Supported on
404     - Backend module
405   * - FreeRTOS
406     - :ref:`module-pw_sync_freertos`
407   * - ThreadX
408     - :ref:`module-pw_sync_threadx`
409   * - embOS
410     - :ref:`module-pw_sync_embos`
411   * - STL
412     - :ref:`module-pw_sync_stl`
413   * - Baremetal
414     - Planned, not ready for use
415   * - Zephyr
416     - Planned
417   * - CMSIS-RTOS API v2 & RTX5
418     - Planned
419
420C++
421---
422.. doxygenclass:: pw::sync::InterruptSpinLock
423   :members:
424
425.. cpp:namespace-push:: pw::sync::InterruptSpinLock
426
427.. list-table::
428   :widths: 70 10 10 10
429   :header-rows: 1
430
431   * - Safe to use in context
432     - Thread
433     - Interrupt
434     - NMI
435   * - :cpp:class:`pw::sync::InterruptSpinLock::InterruptSpinLock`
436     - ✔
437     - ✔
438     -
439   * - :cpp:func:`pw::sync::InterruptSpinLock::~InterruptSpinLock`
440     - ✔
441     - ✔
442     -
443   * - :cpp:func:`lock`
444     - ✔
445     - ✔
446     -
447   * - :cpp:func:`try_lock`
448     - ✔
449     - ✔
450     -
451   * - :cpp:func:`unlock`
452     - ✔
453     - ✔
454     -
455
456.. cpp:namespace-pop::
457
458Examples in C++
459^^^^^^^^^^^^^^^
460.. code-block:: cpp
461
462   #include "pw_sync/interrupt_spin_lock.h"
463
464   pw::sync::InterruptSpinLock interrupt_spin_lock;
465
466   void InterruptSafeCriticalSection() {
467     interrupt_spin_lock.lock();
468     NotThreadSafeCriticalSection();
469     interrupt_spin_lock.unlock();
470   }
471
472
473Alternatively you can use C++'s RAII helpers to ensure you always unlock.
474
475.. code-block:: cpp
476
477   #include <mutex>
478
479   #include "pw_sync/interrupt_spin_lock.h"
480
481   pw::sync::InterruptSpinLock interrupt_spin_lock;
482
483   void InterruptSafeCriticalSection() {
484     std::lock_guard lock(interrupt_spin_lock);
485     NotThreadSafeCriticalSection();
486   }
487
488
489C
490-
491The InterruptSpinLock must be created in C++, however it can be passed into C using the
492``pw_sync_InterruptSpinLock`` opaque struct alias.
493
494.. doxygenfunction:: pw_sync_InterruptSpinLock_Lock
495.. doxygenfunction:: pw_sync_InterruptSpinLock_TryLock
496.. doxygenfunction:: pw_sync_InterruptSpinLock_Unlock
497
498.. list-table::
499   :widths: 70 10 10 10
500   :header-rows: 1
501
502   * - Safe to use in context
503     - Thread
504     - Interrupt
505     - NMI
506   * - :cpp:func:`pw_sync_InterruptSpinLock_Lock`
507     - ✔
508     - ✔
509     -
510   * - :cpp:func:`pw_sync_InterruptSpinLock_TryLock`
511     - ✔
512     - ✔
513     -
514   * - :cpp:func:`pw_sync_InterruptSpinLock_Unlock`
515     - ✔
516     - ✔
517     -
518
519Example in C
520^^^^^^^^^^^^
521.. code-block:: cpp
522
523   #include "pw_chrono/system_clock.h"
524   #include "pw_sync/interrupt_spin_lock.h"
525
526   pw::sync::InterruptSpinLock interrupt_spin_lock;
527
528   extern pw_sync_InterruptSpinLock interrupt_spin_lock;  // This can only be created in C++.
529
530   void InterruptSafeCriticalSection(void) {
531     pw_sync_InterruptSpinLock_Lock(&interrupt_spin_lock);
532     NotThreadSafeCriticalSection();
533     pw_sync_InterruptSpinLock_Unlock(&interrupt_spin_lock);
534   }
535
536Thread Safety Lock Annotations
537==============================
538Pigweed's critical section lock primitives support Clang's thread safety
539analysis extension for C++. The analysis is completely static at compile-time.
540This is only supported when building with Clang. The annotations are no-ops when
541using different compilers.
542
543Pigweed provides the ``pw_sync/lock_annotations.h`` header file with macro
544definitions to allow developers to document the locking policies of
545multi-threaded code. The annotations can also help program analysis tools to
546identify potential thread safety issues.
547
548More information on Clang's thread safety analysis system can be found
549`here <https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>`_.
550
551Enabling Clang's Analysis
552-------------------------
553In order to enable the analysis, Clang requires that the ``-Wthread-safety``
554compilation flag be used. In addition, if any STL components like
555``std::lock_guard`` are used, the STL's built in annotations have to be manually
556enabled, typically by setting the ``_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS``
557macro.
558
559If using GN, the ``pw_build:clang_thread_safety_warnings`` config is provided
560to do this for you, when added to your clang toolchain definition's default
561configs.
562
563Why use lock annotations?
564-------------------------
565Lock annotations can help warn you about potential race conditions in your code
566when using locks: you have to remember to grab lock(s) before entering a
567critical section, yuou have to remember to unlock it when you leave, and you
568have to avoid deadlocks.
569
570Clang's lock annotations let you inform the compiler and anyone reading your
571code which variables are guarded by which locks, which locks should or cannot be
572held when calling which function, which order locks should be acquired in, etc.
573
574Using Lock Annotations
575----------------------
576When referring to locks in the arguments of the attributes, you should
577use variable names or more complex expressions (e.g. ``my_object->lock_``)
578that evaluate to a concrete lock object whenever possible. If the lock
579you want to refer to is not in scope, you may use a member pointer
580(e.g. ``&MyClass::lock_``) to refer to a lock in some (unknown) object.
581
582Annotating Lock Usage
583^^^^^^^^^^^^^^^^^^^^^
584.. doxygendefine:: PW_GUARDED_BY
585.. doxygendefine:: PW_PT_GUARDED_BY
586.. doxygendefine:: PW_ACQUIRED_AFTER
587.. doxygendefine:: PW_ACQUIRED_BEFORE
588.. doxygendefine:: PW_EXCLUSIVE_LOCKS_REQUIRED
589.. doxygendefine:: PW_SHARED_LOCKS_REQUIRED
590.. doxygendefine:: PW_LOCKS_EXCLUDED
591.. doxygendefine:: PW_LOCK_RETURNED
592.. doxygendefine:: PW_LOCKABLE
593.. doxygendefine:: PW_SCOPED_LOCKABLE
594.. doxygendefine:: PW_EXCLUSIVE_LOCK_FUNCTION
595.. doxygendefine:: PW_SHARED_LOCK_FUNCTION
596.. doxygendefine:: PW_UNLOCK_FUNCTION
597.. doxygendefine:: PW_EXCLUSIVE_TRYLOCK_FUNCTION
598.. doxygendefine:: PW_SHARED_TRYLOCK_FUNCTION
599.. doxygendefine:: PW_ASSERT_EXCLUSIVE_LOCK
600.. doxygendefine:: PW_ASSERT_SHARED_LOCK
601.. doxygendefine:: PW_NO_LOCK_SAFETY_ANALYSIS
602
603Annotating Lock Objects
604^^^^^^^^^^^^^^^^^^^^^^^
605In order of lock usage annotation to work, the lock objects themselves need to
606be annotated as well. In case you are providing your own lock or psuedo-lock
607object, you can use the macros in this section to annotate it.
608
609As an example we've annotated a Lock and a RAII ScopedLocker object for you, see
610the macro documentation after for more details:
611
612.. code-block:: cpp
613
614   class PW_LOCKABLE("Lock") Lock {
615    public:
616     void Lock() PW_EXCLUSIVE_LOCK_FUNCTION();
617
618     void ReaderLock() PW_SHARED_LOCK_FUNCTION();
619
620     void Unlock() PW_UNLOCK_FUNCTION();
621
622     void ReaderUnlock() PW_SHARED_TRYLOCK_FUNCTION();
623
624     bool TryLock() PW_EXCLUSIVE_TRYLOCK_FUNCTION(true);
625
626     bool ReaderTryLock() PW_SHARED_TRYLOCK_FUNCTION(true);
627
628     void AssertHeld() PW_ASSERT_EXCLUSIVE_LOCK();
629
630     void AssertReaderHeld() PW_ASSERT_SHARED_LOCK();
631   };
632
633
634   // Tag types for selecting a constructor.
635   struct adopt_lock_t {} inline constexpr adopt_lock = {};
636   struct defer_lock_t {} inline constexpr defer_lock = {};
637   struct shared_lock_t {} inline constexpr shared_lock = {};
638
639   class PW_SCOPED_LOCKABLE ScopedLocker {
640     // Acquire lock, implicitly acquire *this and associate it with lock.
641     ScopedLocker(Lock *lock) PW_EXCLUSIVE_LOCK_FUNCTION(lock)
642         : lock_(lock), locked(true) {
643       lock->Lock();
644     }
645
646     // Assume lock is held, implicitly acquire *this and associate it with lock.
647     ScopedLocker(Lock *lock, adopt_lock_t) PW_EXCLUSIVE_LOCKS_REQUIRED(lock)
648         : lock_(lock), locked(true) {}
649
650     // Acquire lock in shared mode, implicitly acquire *this and associate it
651     // with lock.
652     ScopedLocker(Lock *lock, shared_lock_t) PW_SHARED_LOCK_FUNCTION(lock)
653         : lock_(lock), locked(true) {
654       lock->ReaderLock();
655     }
656
657     // Assume lock is held in shared mode, implicitly acquire *this and associate
658     // it with lock.
659     ScopedLocker(Lock *lock, adopt_lock_t, shared_lock_t)
660         PW_SHARED_LOCKS_REQUIRED(lock) : lock_(lock), locked(true) {}
661
662     // Assume lock is not held, implicitly acquire *this and associate it with
663     // lock.
664     ScopedLocker(Lock *lock, defer_lock_t) PW_LOCKS_EXCLUDED(lock)
665         : lock_(lock), locked(false) {}
666
667     // Release *this and all associated locks, if they are still held.
668     // There is no warning if the scope was already unlocked before.
669     ~ScopedLocker() PW_UNLOCK_FUNCTION() {
670       if (locked)
671         lock_->GenericUnlock();
672     }
673
674     // Acquire all associated locks exclusively.
675     void Lock() PW_EXCLUSIVE_LOCK_FUNCTION() {
676       lock_->Lock();
677       locked = true;
678     }
679
680     // Try to acquire all associated locks exclusively.
681     bool TryLock() PW_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
682       return locked = lock_->TryLock();
683     }
684
685     // Acquire all associated locks in shared mode.
686     void ReaderLock() PW_SHARED_LOCK_FUNCTION() {
687       lock_->ReaderLock();
688       locked = true;
689     }
690
691     // Try to acquire all associated locks in shared mode.
692     bool ReaderTryLock() PW_SHARED_TRYLOCK_FUNCTION(true) {
693       return locked = lock_->ReaderTryLock();
694     }
695
696     // Release all associated locks. Warn on double unlock.
697     void Unlock() PW_UNLOCK_FUNCTION() {
698       lock_->Unlock();
699       locked = false;
700     }
701
702     // Release all associated locks. Warn on double unlock.
703     void ReaderUnlock() PW_UNLOCK_FUNCTION() {
704       lock_->ReaderUnlock();
705       locked = false;
706     }
707
708    private:
709     Lock* lock_;
710     bool locked_;
711   };
712
713-----------------------------
714Critical Section Lock Helpers
715-----------------------------
716
717Virtual Lock Interfaces
718=======================
719Virtual lock interfaces can be useful when lock selection cannot be templated.
720
721Why use virtual locks?
722----------------------
723Virtual locks enable depending on locks without templating implementation code
724on the type, while retaining flexibility with respect to the concrete lock type.
725Pigweed tries to avoid pushing policy on to users, and virtual locks are one way
726to accomplish that without templating everything.
727
728A case when virtual locks are useful is when the concrete lock type changes at
729run time. For example, access to flash may be protected at run time by an
730internal mutex, however at crash time we may want to switch to a no-op lock. A
731virtual lock interface could be used here to minimize the code-size cost that
732would occur otherwise if the flash driver were templated.
733
734VirtualBasicLockable
735--------------------
736The ``VirtualBasicLockable`` interface meets the
737`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_ C++
738named requirement. Our critical section lock primitives offer optional virtual
739versions, including:
740
741* :cpp:func:`pw::sync::VirtualMutex`
742* :cpp:func:`pw::sync::VirtualTimedMutex`
743* :cpp:func:`pw::sync::VirtualInterruptSpinLock`
744
745.. _module-pw_sync-genericbasiclockable:
746
747GenericBasicLockable
748--------------------
749``GenericBasicLockable`` is a helper construct that can be used to declare
750virtual versions of a critical section lock primitive that meets the
751`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_
752C++ named requirement. For example, given a ``Mutex`` type with ``lock()`` and
753``unlock()`` methods, a ``VirtualMutex`` type that derives from
754``VirtualBasicLockable`` can be declared as follows:
755
756.. code-block:: cpp
757
758   class VirtualMutex : public GenericBasicLockable<Mutex> {};
759
760Borrowable
761==========
762``Borrowable`` is a helper construct that enables callers to borrow an object
763which is guarded by a lock, enabling a containerized style of external locking.
764
765Users who need access to the guarded object can ask to acquire a
766``BorrowedPointer`` which permits access while the lock is held.
767
768This class is compatible with locks which comply with
769`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_,
770`Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_, and
771`TimedLockable <https://en.cppreference.com/w/cpp/named_req/TimedLockable>`_
772C++ named requirements.
773
774By default the selected lock type is a ``pw::sync::VirtualBasicLockable``. If
775this virtual interface is used, the templated lock parameter can be skipped.
776
777External vs Internal locking
778----------------------------
779Before we explain why Borrowable is useful, it's important to understand the
780trade-offs when deciding on using internal and/or external locking.
781
782Internal locking is when the lock is hidden from the caller entirely and is used
783internally to the API. For example:
784
785.. code-block:: cpp
786
787   class BankAccount {
788    public:
789     void Deposit(int amount) {
790       std::lock_guard lock(mutex_);
791       balance_ += amount;
792     }
793
794     void Withdraw(int amount) {
795       std::lock_guard lock(mutex_);
796       balance_ -= amount;
797     }
798
799     void Balance() const {
800       std::lock_guard lock(mutex_);
801       return balance_;
802     }
803
804    private:
805     int balance_ PW_GUARDED_BY(mutex_);
806     pw::sync::Mutex mutex_;
807   };
808
809Internal locking guarantees that any concurrent calls to its public member
810functions don't corrupt an instance of that class. This is typically ensured by
811having each member function acquire a lock on the object upon entry. This way,
812for any instance, there can only be one member function call active at any
813moment, serializing the operations.
814
815One common issue that pops up is that member functions may have to call other
816member functions which also require locks. This typically results in a
817duplication of the public API into an internal mirror where the lock is already
818held. This along with having to modify every thread-safe public member function
819may results in an increased code size.
820
821However, with the per-method locking approach, it is not possible to perform a
822multi-method thread-safe transaction. For example, what if we only wanted to
823withdraw money if the balance was high enough? With the current API there would
824be a risk that money is withdrawn after we've checked the balance.
825
826This is usually why external locking is used. This is when the lock is exposed
827to the caller and may be used externally to the public API. External locking
828can take may forms which may even include mixing internal and external locking.
829In its most simplistic form it is an external lock used along side each
830instance, e.g.:
831
832.. code-block:: cpp
833
834   class BankAccount {
835    public:
836     void Deposit(int amount) {
837       balance_ += amount;
838     }
839
840     void Withdraw(int amount) {
841       balance_ -= amount;
842     }
843
844     void Balance() const {
845       return balance_;
846     }
847
848    private:
849     int balance_;
850   };
851
852   pw::sync::Mutex bobs_account_mutex;
853   BankAccount bobs_account PW_GUARDED_BY(bobs_account_mutex);
854
855The lock is acquired before the bank account is used for a transaction. In
856addition, we do not have to modify every public function and its trivial to
857call other public member functions from a public member function. However, as
858you can imagine instantiating and passing around the instances and their locks
859can become error prone.
860
861This is why ``Borrowable`` exists.
862
863Why use Borrowable?
864-------------------
865``Borrowable`` offers code-size efficient way to enable external locking that is
866easy and safe to use. It is effectively a container which holds references to a
867protected instance and its lock which provides RAII-style access.
868
869.. code-block:: cpp
870
871   pw::sync::Mutex bobs_account_mutex;
872   BankAccount bobs_account PW_GUARDED_BY(bobs_account_mutex);
873   pw::sync::Borrowable<BankAccount, pw::sync::Mutex> bobs_acount(
874       bobs_account, bobs_account_mutex);
875
876This construct is useful when sharing objects or data which are transactional in
877nature where making individual operations threadsafe is insufficient. See the
878section on internal vs external locking tradeoffs above.
879
880It can also offer a code-size and stack-usage efficient way to separate timeout
881constraints between the acquiring of the shared object and timeouts used for the
882shared object's API. For example, imagine you have an I2c bus which is used by
883several threads and you'd like to specify an ACK timeout of 50ms. It'd be ideal
884if the duration it takes to gain exclusive access to the I2c bus does not eat
885into the ACK timeout you'd like to use for the transaction. Borrowable can help
886you do exactly this if you provide access to the I2c bus through a
887``Borrowable``.
888
889.. note::
890
891   ``Borrowable`` has semantics similar to a pointer and should be passed by
892   value. Furthermore, a ``Borrowable<U>`` can be assigned to a
893   ``Borrowable<T>`` if ``U`` is a subclass of ``T``.
894
895C++
896---
897.. doxygenclass:: pw::sync::BorrowedPointer
898   :members:
899
900.. doxygenclass:: pw::sync::Borrowable
901   :members:
902
903Example in C++
904^^^^^^^^^^^^^^
905
906.. code-block:: cpp
907
908   #include <chrono>
909
910   #include "pw_bytes/span.h"
911   #include "pw_i2c/initiator.h"
912   #include "pw_status/try.h"
913   #include "pw_status/result.h"
914   #include "pw_sync/borrow.h"
915   #include "pw_sync/mutex.h"
916
917   class ExampleI2c : public pw::i2c::Initiator;
918
919   pw::sync::VirtualMutex i2c_mutex;
920   ExampleI2c i2c;
921   pw::sync::Borrowable<ExampleI2c> borrowable_i2c(i2c, i2c_mutex);
922
923   pw::Result<ConstByteSpan> ReadI2cData(ByteSpan buffer) {
924     // Block indefinitely waiting to borrow the i2c bus.
925     pw::sync::BorrowedPointer<ExampleI2c> borrowed_i2c =
926         borrowable_i2c.acquire();
927
928     // Execute a sequence of transactions to get the needed data.
929     PW_TRY(borrowed_i2c->WriteFor(kFirstWrite, std::chrono::milliseconds(50)));
930     PW_TRY(borrowed_i2c->WriteReadFor(kSecondWrite, buffer,
931                                       std::chrono::milliseconds(10)));
932
933     // Borrowed i2c pointer is returned when the scope exits.
934     return buffer;
935   }
936
937InlineBorrowable
938=================
939``InlineBorrowable`` is a helper to simplify the common use case where an object
940is wrapped in a ``Borrowable`` for its entire lifetime. The InlineBorrowable
941owns the guarded object and the lock object.
942
943InlineBorrowable has a separate parameter for the concrete lock type
944that is instantiated and a (possibly virtual) lock interface type that is
945referenced by users of the guarded object. The default lock is
946``pw::sync::VirtualMutex`` and the default lock interface is
947``pw::sync::VirtualBasicLockable``.
948
949An InlineBorrowable is a Borrowable with the same guarded object and lock
950interface types, and it can be passed directly to APIs that expect a Borrowable
951reference.
952
953Why use InlineBorrowable?
954-------------------------
955It is a safer and simpler way to guard an object for its entire lifetime. The
956unguarded object is never exposed and doesn't need to be stored in a separate
957variable or data member. The guarded object and its lock are guaranteed to have
958the same lifetime, and the lock cannot be re-used for any other purpose.
959
960Constructing objects in-place
961-----------------------------
962The guarded object and its lock are constructed in-place by the
963InlineBorrowable, and any constructor parameters required by the object or
964its lock must be passed through the InlineBorrowable constructor. There are
965several ways to do this:
966
967* Pass the parameters for the guarded object inline to the constructor. This is
968  the recommended way to construct the object when the lock does not require any
969  constructor parameters. Use the ``std::in_place`` marker to invoke the inline
970  constructor.
971
972  .. code-block:: cpp
973
974     InlineBorrowable<Foo> foo(std::in_place, foo_arg1, foo_arg2);
975     InlineBorrowable<std::array<int, 2>> foo_array(std::in_place, 1, 2);
976
977* Pass the parameters inside tuples:
978
979  .. code-block:: cpp
980
981     InlineBorrowable<Foo> foo(std::forward_as_tuple(foo_arg1, foo_arg2));
982
983     InlineBorrowable<Foo, MyLock> foo_lock(
984         std::forward_as_tuple(foo_arg1, foo_arg2),
985         std::forward_as_tuple(lock_arg1, lock_arg2));
986
987  .. note:: This approach only supports list initialization starting with C++20.
988
989* Use callables to construct the guarded object and lock object:
990
991  .. code-block:: cpp
992
993     InlineBorrowable<Foo> foo([&]{ return Foo{foo_arg1, foo_arg2}; });
994
995     InlineBorrowable<Foo, MyLock> foo_lock(
996         [&]{ return Foo{foo_arg1, foo_arg2}; }
997         [&]{ return MyLock{lock_arg1, lock_arg2}; }
998
999  .. note:: It is possible to construct and return objects that are not copyable
1000    or movable, thanks to mandatory copy ellision (return value optimization).
1001
1002C++
1003---
1004.. doxygenclass:: pw::sync::InlineBorrowable
1005   :members:
1006
1007Example in C++
1008^^^^^^^^^^^^^^
1009.. code-block:: cpp
1010
1011   #include <utility>
1012
1013   #include "pw_bytes/span.h"
1014   #include "pw_i2c/initiator.h"
1015   #include "pw_status/result.h"
1016   #include "pw_sync/inline_borrowable.h"
1017
1018   struct I2cOptions;
1019
1020   class ExampleI2c : public pw::i2c::Initiator {
1021    public:
1022     ExampleI2c(int bus_id, I2cOptions options);
1023     // ...
1024   };
1025
1026   int kBusId;
1027   I2cOptions opts;
1028
1029   pw::sync::InlineBorrowable<ExampleI2c> i2c(std::in_place, kBusId, opts);
1030
1031   pw::Result<ConstByteSpan> ReadI2cData(
1032     pw::sync::Borrowable<pw::i2c::Initiator> initiator,
1033     ByteSpan buffer);
1034
1035   pw::Result<ConstByteSpan> ReadData(ByteSpan buffer) {
1036     return ReadI2cData(i2c, buffer);
1037   }
1038
1039--------------------
1040Signaling Primitives
1041--------------------
1042Native signaling primitives tend to vary more compared to critial section locks
1043across different platforms. For example, although common signaling primtives
1044like semaphores are in most if not all RTOSes and even POSIX, it was not in the
1045STL before C++20. Likewise many C++ developers are surprised that conditional
1046variables tend to not be natively supported on RTOSes. Although you can usually
1047build any signaling primitive based on other native signaling primitives, this
1048may come with non-trivial added overhead in ROM, RAM, and execution efficiency.
1049
1050For this reason, Pigweed intends to provide some simpler signaling primitives
1051which exist to solve a narrow programming need but can be implemented as
1052efficiently as possible for the platform that it is used on.
1053
1054This simpler but highly portable class of signaling primitives is intended to
1055ensure that a portability efficiency tradeoff does not have to be made up front.
1056Today this is class of simpler signaling primitives is limited to the
1057:cpp:class:`pw::sync::ThreadNotification` and
1058:cpp:class:`pw::sync::TimedThreadNotification`.
1059
1060ThreadNotification
1061==================
1062.. cpp:namespace-push:: pw::sync
1063
1064The :cpp:class:`ThreadNotification` is a synchronization primitive that can be used to
1065permit a SINGLE thread to block and consume a latching, saturating
1066notification from multiple notifiers.
1067
1068.. Note::
1069   Although only a single thread can block on a :cpp:class:`ThreadNotification`
1070   at a time, many instances may be used by a single thread just like binary
1071   semaphores.  This is in contrast to some native RTOS APIs, such as direct
1072   task notifications, which re-use the same state within a thread's context.
1073
1074.. Warning::
1075   This is a single consumer/waiter, multiple producer/notifier API!
1076   The acquire APIs must only be invoked by a single consuming thread. As a
1077   result, having multiple threads receiving notifications via the acquire API
1078   is unsupported.
1079
1080This is effectively a subset of the :cpp:class:`BinarySemaphore` API, except
1081that only a single thread can be notified and block at a time.
1082
1083The single consumer aspect of the API permits the use of a smaller and/or
1084faster native APIs such as direct thread signaling. This should be
1085backed by the most efficient native primitive for a target, regardless of
1086whether that is a semaphore, event flag group, condition variable, or something
1087else.
1088
1089The :cpp:class:`ThreadNotification` is initialized to being empty (latch is not
1090set).
1091
1092.. cpp:namespace-pop::
1093
1094Generic BinarySemaphore-based Backend
1095-------------------------------------
1096This module provides a generic backend for
1097:cpp:class:`pw::sync::ThreadNotification` via
1098``pw_sync:binary_semaphore_thread_notification`` which uses a
1099:cpp:class:`pw::sync::BinarySemaphore` as the backing primitive. See
1100:ref:`BinarySemaphore <module-pw_sync-binary-semaphore>` for backend
1101availability.
1102
1103Optimized Backend
1104-----------------
1105.. list-table::
1106   :header-rows: 1
1107
1108   * - Supported on
1109     - Optimized backend module
1110   * - FreeRTOS
1111     - ``pw_sync_freertos:thread_notification``
1112   * - ThreadX
1113     - Not possible, use ``pw_sync:binary_semaphore_thread_notification``
1114   * - embOS
1115     - Not needed, use ``pw_sync:binary_semaphore_thread_notification``
1116   * - STL
1117     - Not planned, use ``pw_sync:binary_semaphore_thread_notification``
1118   * - Baremetal
1119     - Planned
1120   * - Zephyr
1121     - Planned
1122   * - CMSIS-RTOS API v2 & RTX5
1123     - Planned
1124
1125C++
1126---
1127.. doxygenclass:: pw::sync::ThreadNotification
1128   :members:
1129
1130.. cpp:namespace-push:: pw::sync::ThreadNotification
1131
1132.. list-table::
1133   :widths: 70 10 10 10
1134   :header-rows: 1
1135
1136   * - Safe to use in context
1137     - Thread
1138     - Interrupt
1139     - NMI
1140   * - :cpp:class:`pw::sync::ThreadNotification::ThreadNotification`
1141     - ✔
1142     -
1143     -
1144   * - :cpp:func:`pw::sync::ThreadNotification::~ThreadNotification`
1145     - ✔
1146     -
1147     -
1148   * - :cpp:func:`acquire`
1149     - ✔
1150     -
1151     -
1152   * - :cpp:func:`try_acquire`
1153     - ✔
1154     -
1155     -
1156   * - :cpp:func:`release`
1157     - ✔
1158     - ✔
1159     -
1160
1161.. cpp:namespace-pop::
1162
1163
1164Examples in C++
1165^^^^^^^^^^^^^^^
1166.. code-block:: cpp
1167
1168   #include "pw_sync/thread_notification.h"
1169   #include "pw_thread/thread_core.h"
1170
1171   class FooHandler() : public pw::thread::ThreadCore {
1172    // Public API invoked by other threads and/or interrupts.
1173    void NewFooAvailable() {
1174      new_foo_notification_.release();
1175    }
1176
1177    private:
1178     pw::sync::ThreadNotification new_foo_notification_;
1179
1180     // Thread function.
1181     void Run() override {
1182       while (true) {
1183         new_foo_notification_.acquire();
1184         HandleFoo();
1185       }
1186     }
1187
1188     void HandleFoo();
1189   }
1190
1191TimedThreadNotification
1192=======================
1193The :cpp:class:`TimedThreadNotification` is an extension of the
1194:cpp:class:`ThreadNotification` which offers timeout and deadline based
1195semantics.
1196
1197The :cpp:class:`TimedThreadNotification` is initialized to being empty (latch is
1198not set).
1199
1200.. Warning::
1201   This is a single consumer/waiter, multiple producer/notifier API!  The
1202   acquire APIs must only be invoked by a single consuming thread. As a result,
1203   having multiple threads receiving notifications via the acquire API is
1204   unsupported.
1205
1206Generic BinarySemaphore-based Backend
1207-------------------------------------
1208This module provides a generic backend for
1209:cpp:class:`pw::sync::TimedThreadNotification` via
1210``pw_sync:binary_semaphore_timed_thread_notification`` which uses a
1211:cpp:class:`pw::sync::BinarySemaphore` as the backing primitive. See
1212:ref:`BinarySemaphore <module-pw_sync-binary-semaphore>` for backend
1213availability.
1214
1215Optimized Backend
1216-----------------
1217.. list-table::
1218   :header-rows: 1
1219
1220   * - Supported on
1221     - Backend module
1222   * - FreeRTOS
1223     - ``pw_sync_freertos:timed_thread_notification``
1224   * - ThreadX
1225     - Not possible, use ``pw_sync:binary_semaphore_timed_thread_notification``
1226   * - embOS
1227     - Not needed, use ``pw_sync:binary_semaphore_timed_thread_notification``
1228   * - STL
1229     - Not planned, use ``pw_sync:binary_semaphore_timed_thread_notification``
1230   * - Zephyr
1231     - Planned
1232   * - CMSIS-RTOS API v2 & RTX5
1233     - Planned
1234
1235C++
1236---
1237.. doxygenclass:: pw::sync::TimedThreadNotification
1238   :members:
1239
1240.. cpp:namespace-push:: pw::sync::TimedThreadNotification
1241
1242.. list-table::
1243   :widths: 70 10 10 10
1244   :header-rows: 1
1245
1246   * - Safe to use in context
1247     - Thread
1248     - Interrupt
1249     - NMI
1250   * - :cpp:class:`pw::sync::TimedThreadNotification::TimedThreadNotification`
1251     - ✔
1252     -
1253     -
1254   * - :cpp:func:`pw::sync::TimedThreadNotification::~TimedThreadNotification`
1255     - ✔
1256     -
1257     -
1258   * - :cpp:func:`acquire`
1259     - ✔
1260     -
1261     -
1262   * - :cpp:func:`try_acquire`
1263     - ✔
1264     -
1265     -
1266   * - :cpp:func:`try_acquire_for`
1267     - ✔
1268     -
1269     -
1270   * - :cpp:func:`try_acquire_until`
1271     - ✔
1272     -
1273     -
1274   * - :cpp:func:`release`
1275     - ✔
1276     - ✔
1277     -
1278
1279.. cpp:namespace-pop::
1280
1281Examples in C++
1282^^^^^^^^^^^^^^^
1283.. code-block:: cpp
1284
1285   #include "pw_sync/timed_thread_notification.h"
1286   #include "pw_thread/thread_core.h"
1287
1288   class FooHandler() : public pw::thread::ThreadCore {
1289    // Public API invoked by other threads and/or interrupts.
1290    void NewFooAvailable() {
1291      new_foo_notification_.release();
1292    }
1293
1294    private:
1295     pw::sync::TimedThreadNotification new_foo_notification_;
1296
1297     // Thread function.
1298     void Run() override {
1299       while (true) {
1300         if (new_foo_notification_.try_acquire_for(kNotificationTimeout)) {
1301           HandleFoo();
1302         }
1303         DoOtherStuff();
1304       }
1305     }
1306
1307     void HandleFoo();
1308     void DoOtherStuff();
1309   }
1310
1311CountingSemaphore
1312=================
1313.. cpp:namespace-push:: pw::sync
1314
1315The :cpp:class:`CountingSemaphore` is a synchronization primitive that can be
1316used for counting events and/or resource management where receiver(s) can block
1317on acquire until notifier(s) signal by invoking release.
1318
1319Note that unlike :cpp:class:`Mutex`, priority inheritance is not used by
1320semaphores meaning semaphores are subject to unbounded priority inversions. Due
1321to this, Pigweed does not recommend semaphores for mutual exclusion.
1322
1323The :cpp:class:`CountingSemaphore` is initialized to being empty or having no
1324tokens.
1325
1326The entire API is thread safe, but only a subset is interrupt safe.
1327
1328.. Note::
1329   If there is only a single consuming thread, use a
1330   :cpp:class:`ThreadNotification` instead which can be much more efficient on
1331   some RTOSes such as FreeRTOS.
1332
1333.. cpp:namespace-pop::
1334
1335.. Warning::
1336   Releasing multiple tokens is often not natively supported, meaning you may
1337   end up invoking the native kernel API many times, i.e. once per token you
1338   are releasing!
1339
1340.. list-table::
1341   :header-rows: 1
1342
1343   * - Supported on
1344     - Backend module
1345   * - FreeRTOS
1346     - :ref:`module-pw_sync_freertos`
1347   * - ThreadX
1348     - :ref:`module-pw_sync_threadx`
1349   * - embOS
1350     - :ref:`module-pw_sync_embos`
1351   * - STL
1352     - :ref:`module-pw_sync_stl`
1353   * - Zephyr
1354     - Planned
1355   * - CMSIS-RTOS API v2 & RTX5
1356     - Planned
1357
1358C++
1359---
1360.. doxygenclass:: pw::sync::CountingSemaphore
1361   :members:
1362
1363.. cpp:namespace-push:: pw::sync::CountingSemaphore
1364
1365.. list-table::
1366   :widths: 70 10 10 10
1367   :header-rows: 1
1368
1369   * - Safe to use in context
1370     - Thread
1371     - Interrupt
1372     - NMI
1373   * - :cpp:class:`pw::sync::CountingSemaphore::CountingSemaphore`
1374     - ✔
1375     -
1376     -
1377   * - :cpp:func:`pw::sync::CountingSemaphore::~CountingSemaphore`
1378     - ✔
1379     -
1380     -
1381   * - :cpp:func:`acquire`
1382     - ✔
1383     -
1384     -
1385   * - :cpp:func:`try_acquire`
1386     - ✔
1387     - ✔
1388     -
1389   * - :cpp:func:`try_acquire_for`
1390     - ✔
1391     -
1392     -
1393   * - :cpp:func:`try_acquire_until`
1394     - ✔
1395     -
1396     -
1397   * - :cpp:func:`release`
1398     - ✔
1399     - ✔
1400     -
1401   * - :cpp:func:`max`
1402     - ✔
1403     - ✔
1404     - ✔
1405
1406.. cpp:namespace-pop::
1407
1408Examples in C++
1409^^^^^^^^^^^^^^^
1410As an example, a counting sempahore can be useful to run periodic tasks at
1411frequencies near or higher than the system clock tick rate in a way which lets
1412you detect whether you ever fall behind.
1413
1414.. code-block:: cpp
1415
1416   #include "pw_sync/counting_semaphore.h"
1417   #include "pw_thread/thread_core.h"
1418
1419   class PeriodicWorker() : public pw::thread::ThreadCore {
1420    // Public API invoked by a higher frequency timer interrupt.
1421    void TimeToExecute() {
1422      periodic_run_semaphore_.release();
1423    }
1424
1425    private:
1426     pw::sync::CountingSemaphore periodic_run_semaphore_;
1427
1428     // Thread function.
1429     void Run() override {
1430       while (true) {
1431         size_t behind_by_n_cycles = 0;
1432         periodic_run_semaphore_.acquire(); // Wait to run until it's time.
1433         while (periodic_run_semaphore_.try_acquire()) {
1434           ++behind_by_n_cycles;
1435         }
1436         if (behind_by_n_cycles > 0) {
1437           PW_LOG_WARNING("Not keeping up, behind by %d cycles",
1438                          behind_by_n_cycles);
1439         }
1440         DoPeriodicWork();
1441       }
1442     }
1443
1444     void DoPeriodicWork();
1445   }
1446
1447.. _module-pw_sync-binary-semaphore:
1448
1449BinarySemaphore
1450===============
1451.. cpp:namespace-push:: pw::sync
1452
1453:cpp:class:`BinarySemaphore` is a specialization of CountingSemaphore with an
1454arbitrary token limit of 1. Note that that ``max()`` is >= 1, meaning it may be
1455released up to ``max()`` times but only acquired once for those N releases.
1456
1457Implementations of :cpp:class:`BinarySemaphore` are typically more
1458efficient than the default implementation of :cpp:class:`CountingSemaphore`.
1459
1460The :cpp:class:`BinarySemaphore` is initialized to being empty or having no
1461tokens.
1462
1463.. cpp:namespace-pop::
1464
1465The entire API is thread safe, but only a subset is interrupt safe.
1466
1467.. Note::
1468   If there is only a single consuming thread, use a
1469   :cpp:class:`ThreadNotification` instead which can be much more efficient on
1470   some RTOSes such as FreeRTOS.
1471
1472.. list-table::
1473   :header-rows: 1
1474
1475   * - Supported on
1476     - Backend module
1477   * - FreeRTOS
1478     - :ref:`module-pw_sync_freertos`
1479   * - ThreadX
1480     - :ref:`module-pw_sync_threadx`
1481   * - embOS
1482     - :ref:`module-pw_sync_embos`
1483   * - STL
1484     - :ref:`module-pw_sync_stl`
1485   * - Zephyr
1486     - Planned
1487   * - CMSIS-RTOS API v2 & RTX5
1488     - Planned
1489
1490C++
1491---
1492.. doxygenclass:: pw::sync::BinarySemaphore
1493   :members:
1494
1495.. cpp:namespace-push:: pw::sync::BinarySemaphore
1496
1497.. list-table::
1498   :widths: 70 10 10 10
1499   :header-rows: 1
1500
1501   * - Safe to use in context
1502     - Thread
1503     - Interrupt
1504     - NMI
1505   * - :cpp:class:`pw::sync::BinarySemaphore::BinarySemaphore`
1506     - ✔
1507     -
1508     -
1509   * - :cpp:func:`pw::sync::BinarySemaphore::~BinarySemaphore`
1510     - ✔
1511     -
1512     -
1513   * - :cpp:func:`acquire`
1514     - ✔
1515     -
1516     -
1517   * - :cpp:func:`try_acquire`
1518     - ✔
1519     - ✔
1520     -
1521   * - :cpp:func:`try_acquire_for`
1522     - ✔
1523     -
1524     -
1525   * - :cpp:func:`try_acquire_until`
1526     - ✔
1527     -
1528     -
1529   * - :cpp:func:`release`
1530     - ✔
1531     - ✔
1532     -
1533   * - :cpp:func:`max`
1534     - ✔
1535     - ✔
1536     - ✔
1537
1538.. cpp:namespace-pop::
1539
1540Examples in C++
1541^^^^^^^^^^^^^^^
1542.. code-block:: cpp
1543
1544   #include "pw_sync/binary_semaphore.h"
1545   #include "pw_thread/thread_core.h"
1546
1547   class FooHandler() : public pw::thread::ThreadCore {
1548    // Public API invoked by other threads and/or interrupts.
1549    void NewFooAvailable() {
1550      new_foo_semaphore_.release();
1551    }
1552
1553    private:
1554     pw::sync::BinarySemaphore new_foo_semaphore_;
1555
1556     // Thread function.
1557     void Run() override {
1558       while (true) {
1559         if (new_foo_semaphore_.try_acquire_for(kNotificationTimeout)) {
1560           HandleFoo();
1561         }
1562         DoOtherStuff();
1563       }
1564     }
1565
1566     void HandleFoo();
1567     void DoOtherStuff();
1568   }
1569
1570.. _module-pw_sync-condition-variables:
1571
1572Condition Variables
1573=====================
1574:cpp:class:`pw::sync::ConditionVariable` provides a condition variable
1575implementation that provides semantics and an API very similar to
1576`std::condition_variable
1577<https://en.cppreference.com/w/cpp/thread/condition_variable>`_ in the C++
1578Standard Library.
1579
1580.. warning::
1581   Condition variables are not a good abstraction for embedded due to spurious
1582   wakeups. As a result, the only ``pw_sync`` backend provided by Pigweed that
1583   supports condition variables is :ref:`module-pw_sync_stl`. Consider using
1584   a ``ThreadNotification`` instead, as these do not cause spurious wakeups and
1585   can be used in an interrupt context.
1586
1587Limitations
1588-----------
1589As a blocking operation, condition variables should not be waited on in an
1590interrupt context. Less intuitively, condition variables should not be notified
1591in an interrupt context. Notifying a condition variable involves checking the
1592corresponding condition to decide whether to resume waiting threads. This check
1593can happen either on the signaling thread or the waiting thread:
1594
1595- If the signaling thread checks the condition, it needs to exclusively access
1596  the waiters and their associated conditions. Access to this list must be
1597  synchronized with calls to wait on the variable. Additional state checked by
1598  the conditions may also need to be synchronized. As a result, checking the
1599  conditions on the signaling thread may involve blocking and is not suitable
1600  for a interrupt context.
1601- If the waiting threads check their conditions, access to the list of waiters
1602  still needs to be synchronized. Additionally, a thread may find that its
1603  condition is not satisfied, and that it needs to resume waiting. Waking
1604  threads only to resume waiting is costly in terms of both power and
1605  performance.
1606
1607The second approach leads to spurious wakeups in a thread context as well. The
1608first approach may also have spurious wakeups if the condition changes between
1609signaling the waiter and the waiter reacquiring its lock.
1610
1611.. toctree::
1612   :hidden:
1613   :maxdepth: 1
1614
1615   Backends <backends>
1616