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