• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
734VirtualBasicLock
735----------------
736The ``VirtualBasicLock`` 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
745Borrowable
746==========
747The Borrowable is a helper construct that enables callers to borrow an object
748which is guarded by a lock, enabling a containerized style of external locking.
749
750Users who need access to the guarded object can ask to acquire a
751``BorrowedPointer`` which permits access while the lock is held.
752
753This class is compatible with locks which comply with
754`BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_,
755`Lockable <https://en.cppreference.com/w/cpp/named_req/Lockable>`_, and
756`TimedLockable <https://en.cppreference.com/w/cpp/named_req/TimedLockable>`_
757C++ named requirements.
758
759By default the selected lock type is a ``pw::sync::VirtualBasicLockable``. If
760this virtual interface is used, the templated lock parameter can be skipped.
761
762External vs Internal locking
763----------------------------
764Before we explain why Borrowable is useful, it's important to understand the
765trade-offs when deciding on using internal and/or external locking.
766
767Internal locking is when the lock is hidden from the caller entirely and is used
768internally to the API. For example:
769
770.. code-block:: cpp
771
772   class BankAccount {
773    public:
774     void Deposit(int amount) {
775       std::lock_guard lock(mutex_);
776       balance_ += amount;
777     }
778
779     void Withdraw(int amount) {
780       std::lock_guard lock(mutex_);
781       balance_ -= amount;
782     }
783
784     void Balance() const {
785       std::lock_guard lock(mutex_);
786       return balance_;
787     }
788
789    private:
790     int balance_ PW_GUARDED_BY(mutex_);
791     pw::sync::Mutex mutex_;
792   };
793
794Internal locking guarantees that any concurrent calls to its public member
795functions don't corrupt an instance of that class. This is typically ensured by
796having each member function acquire a lock on the object upon entry. This way,
797for any instance, there can only be one member function call active at any
798moment, serializing the operations.
799
800One common issue that pops up is that member functions may have to call other
801member functions which also require locks. This typically results in a
802duplication of the public API into an internal mirror where the lock is already
803held. This along with having to modify every thread-safe public member function
804may results in an increased code size.
805
806However, with the per-method locking approach, it is not possible to perform a
807multi-method thread-safe transaction. For example, what if we only wanted to
808withdraw money if the balance was high enough? With the current API there would
809be a risk that money is withdrawn after we've checked the balance.
810
811This is usually why external locking is used. This is when the lock is exposed
812to the caller and may be used externally to the public API. External locking
813can take may forms which may even include mixing internal and external locking.
814In its most simplistic form it is an external lock used along side each
815instance, e.g.:
816
817.. code-block:: cpp
818
819   class BankAccount {
820    public:
821     void Deposit(int amount) {
822       balance_ += amount;
823     }
824
825     void Withdraw(int amount) {
826       balance_ -= amount;
827     }
828
829     void Balance() const {
830       return balance_;
831     }
832
833    private:
834     int balance_;
835   };
836
837   pw::sync::Mutex bobs_account_mutex;
838   BankAccount bobs_account PW_GUARDED_BY(bobs_account_mutex);
839
840The lock is acquired before the bank account is used for a transaction. In
841addition, we do not have to modify every public function and its trivial to
842call other public member functions from a public member function. However, as
843you can imagine instantiating and passing around the instances and their locks
844can become error prone.
845
846This is why ``Borrowable`` exists.
847
848Why use Borrowable?
849-------------------
850``Borrowable`` offers code-size efficient way to enable external locking that is
851easy and safe to use. It is effectively a container which holds references to a
852protected instance and its lock which provides RAII-style access.
853
854.. code-block:: cpp
855
856   pw::sync::Mutex bobs_account_mutex;
857   BankAccount bobs_account PW_GUARDED_BY(bobs_account_mutex);
858   pw::sync::Borrowable<BankAccount, pw::sync::Mutex> bobs_acount(
859       bobs_account, bobs_account_mutex);
860
861This construct is useful when sharing objects or data which are transactional in
862nature where making individual operations threadsafe is insufficient. See the
863section on internal vs external locking tradeoffs above.
864
865It can also offer a code-size and stack-usage efficient way to separate timeout
866constraints between the acquiring of the shared object and timeouts used for the
867shared object's API. For example, imagine you have an I2c bus which is used by
868several threads and you'd like to specify an ACK timeout of 50ms. It'd be ideal
869if the duration it takes to gain exclusive access to the I2c bus does not eat
870into the ACK timeout you'd like to use for the transaction. Borrowable can help
871you do exactly this if you provide access to the I2c bus through a
872``Borrowable``.
873
874C++
875---
876.. doxygenclass:: pw::sync::BorrowedPointer
877   :members:
878
879.. doxygenclass:: pw::sync::Borrowable
880   :members:
881
882Example in C++
883^^^^^^^^^^^^^^
884
885.. code-block:: cpp
886
887   #include <chrono>
888
889   #include "pw_bytes/span.h"
890   #include "pw_i2c/initiator.h"
891   #include "pw_status/try.h"
892   #include "pw_status/result.h"
893   #include "pw_sync/borrow.h"
894   #include "pw_sync/mutex.h"
895
896   class ExampleI2c : public pw::i2c::Initiator;
897
898   pw::sync::VirtualMutex i2c_mutex;
899   ExampleI2c i2c;
900   pw::sync::Borrowable<ExampleI2c> borrowable_i2c(i2c, i2c_mutex);
901
902   pw::Result<ConstByteSpan> ReadI2cData(ByteSpan buffer) {
903     // Block indefinitely waiting to borrow the i2c bus.
904     pw::sync::BorrowedPointer<ExampleI2c> borrowed_i2c =
905         borrowable_i2c.acquire();
906
907     // Execute a sequence of transactions to get the needed data.
908     PW_TRY(borrowed_i2c->WriteFor(kFirstWrite, std::chrono::milliseconds(50)));
909     PW_TRY(borrowed_i2c->WriteReadFor(kSecondWrite, buffer,
910                                       std::chrono::milliseconds(10)));
911
912     // Borrowed i2c pointer is returned when the scope exits.
913     return buffer;
914   }
915
916InlineBorrowable
917=================
918``InlineBorrowable`` is a helper to simplify the common use case where an object
919is wrapped in a ``Borrowable`` for its entire lifetime. The InlineBorrowable
920owns the guarded object and the lock object.
921
922InlineBorrowable has a separate parameter for the concrete lock type
923that is instantiated and a (possibly virtual) lock interface type that is
924referenced by users of the guarded object. The default lock is
925``pw::sync::VirtualMutex`` and the default lock interface is
926``pw::sync::VirtualBasicLockable``.
927
928An InlineBorrowable is a Borrowable with the same guarded object and lock
929interface types, and it can be passed directly to APIs that expect a Borrowable
930reference.
931
932Why use InlineBorrowable?
933-------------------------
934It is a safer and simpler way to guard an object for its entire lifetime. The
935unguarded object is never exposed and doesn't need to be stored in a separate
936variable or data member. The guarded object and its lock are guaranteed to have
937the same lifetime, and the lock cannot be re-used for any other purpose.
938
939Constructing objects in-place
940-----------------------------
941The guarded object and its lock are constructed in-place by the
942InlineBorrowable, and any constructor parameters required by the object or
943its lock must be passed through the InlineBorrowable constructor. There are
944several ways to do this:
945
946* Pass the parameters for the guarded object inline to the constructor. This is
947  the recommended way to construct the object when the lock does not require any
948  constructor parameters. Use the ``std::in_place`` marker to invoke the inline
949  constructor.
950
951  .. code-block:: cpp
952
953     InlineBorrowable<Foo> foo(std::in_place, foo_arg1, foo_arg2);
954     InlineBorrowable<std::array<int, 2>> foo_array(std::in_place, 1, 2);
955
956* Pass the parameters inside tuples:
957
958  .. code-block:: cpp
959
960     InlineBorrowable<Foo> foo(std::forward_as_tuple(foo_arg1, foo_arg2));
961
962     InlineBorrowable<Foo, MyLock> foo_lock(
963         std::forward_as_tuple(foo_arg1, foo_arg2),
964         std::forward_as_tuple(lock_arg1, lock_arg2));
965
966  .. note:: This approach only supports list initialization starting with C++20.
967
968* Use callables to construct the guarded object and lock object:
969
970  .. code-block:: cpp
971
972     InlineBorrowable<Foo> foo([&]{ return Foo{foo_arg1, foo_arg2}; });
973
974     InlineBorrowable<Foo, MyLock> foo_lock(
975         [&]{ return Foo{foo_arg1, foo_arg2}; }
976         [&]{ return MyLock{lock_arg1, lock_arg2}; }
977
978  .. note:: It is possible to construct and return objects that are not copyable
979    or movable, thanks to mandatory copy ellision (return value optimization).
980
981C++
982---
983.. doxygenclass:: pw::sync::InlineBorrowable
984   :members:
985
986Example in C++
987^^^^^^^^^^^^^^
988.. code-block:: cpp
989
990   #include <utility>
991
992   #include "pw_bytes/span.h"
993   #include "pw_i2c/initiator.h"
994   #include "pw_status/result.h"
995   #include "pw_sync/inline_borrowable.h"
996
997   struct I2cOptions;
998
999   class ExampleI2c : public pw::i2c::Initiator {
1000    public:
1001     ExampleI2c(int bus_id, I2cOptions options);
1002     // ...
1003   };
1004
1005   int kBusId;
1006   I2cOptions opts;
1007
1008   pw::sync::InlineBorrowable<ExampleI2c> i2c(std::in_place, kBusId, opts);
1009
1010   pw::Result<ConstByteSpan> ReadI2cData(
1011     pw::sync::Borrowable<pw::i2c::Initiator>& initiator,
1012     ByteSpan buffer);
1013
1014   pw::Result<ConstByteSpan> ReadData(ByteSpan buffer) {
1015     return ReadI2cData(i2c, buffer);
1016   }
1017
1018--------------------
1019Signaling Primitives
1020--------------------
1021Native signaling primitives tend to vary more compared to critial section locks
1022across different platforms. For example, although common signaling primtives
1023like semaphores are in most if not all RTOSes and even POSIX, it was not in the
1024STL before C++20. Likewise many C++ developers are surprised that conditional
1025variables tend to not be natively supported on RTOSes. Although you can usually
1026build any signaling primitive based on other native signaling primitives, this
1027may come with non-trivial added overhead in ROM, RAM, and execution efficiency.
1028
1029For this reason, Pigweed intends to provide some simpler signaling primitives
1030which exist to solve a narrow programming need but can be implemented as
1031efficiently as possible for the platform that it is used on.
1032
1033This simpler but highly portable class of signaling primitives is intended to
1034ensure that a portability efficiency tradeoff does not have to be made up front.
1035Today this is class of simpler signaling primitives is limited to the
1036:cpp:class:`pw::sync::ThreadNotification` and
1037:cpp:class:`pw::sync::TimedThreadNotification`.
1038
1039ThreadNotification
1040==================
1041.. cpp:namespace-push:: pw::sync
1042
1043The :cpp:class:`ThreadNotification` is a synchronization primitive that can be used to
1044permit a SINGLE thread to block and consume a latching, saturating
1045notification from multiple notifiers.
1046
1047.. Note::
1048   Although only a single thread can block on a :cpp:class:`ThreadNotification`
1049   at a time, many instances may be used by a single thread just like binary
1050   semaphores.  This is in contrast to some native RTOS APIs, such as direct
1051   task notifications, which re-use the same state within a thread's context.
1052
1053.. Warning::
1054   This is a single consumer/waiter, multiple producer/notifier API!
1055   The acquire APIs must only be invoked by a single consuming thread. As a
1056   result, having multiple threads receiving notifications via the acquire API
1057   is unsupported.
1058
1059This is effectively a subset of the :cpp:class:`BinarySemaphore` API, except
1060that only a single thread can be notified and block at a time.
1061
1062The single consumer aspect of the API permits the use of a smaller and/or
1063faster native APIs such as direct thread signaling. This should be
1064backed by the most efficient native primitive for a target, regardless of
1065whether that is a semaphore, event flag group, condition variable, or something
1066else.
1067
1068The :cpp:class:`ThreadNotification` is initialized to being empty (latch is not
1069set).
1070
1071.. cpp:namespace-pop::
1072
1073Generic BinarySemaphore-based Backend
1074-------------------------------------
1075This module provides a generic backend for
1076:cpp:class:`pw::sync::ThreadNotification` via
1077``pw_sync:binary_semaphore_thread_notification`` which uses a
1078:cpp:class:`pw::sync::BinarySemaphore` as the backing primitive. See
1079:ref:`BinarySemaphore <module-pw_sync-binary-semaphore>` for backend
1080availability.
1081
1082Optimized Backend
1083-----------------
1084.. list-table::
1085   :header-rows: 1
1086
1087   * - Supported on
1088     - Optimized backend module
1089   * - FreeRTOS
1090     - ``pw_sync_freertos:thread_notification``
1091   * - ThreadX
1092     - Not possible, use ``pw_sync:binary_semaphore_thread_notification``
1093   * - embOS
1094     - Not needed, use ``pw_sync:binary_semaphore_thread_notification``
1095   * - STL
1096     - Not planned, use ``pw_sync:binary_semaphore_thread_notification``
1097   * - Baremetal
1098     - Planned
1099   * - Zephyr
1100     - Planned
1101   * - CMSIS-RTOS API v2 & RTX5
1102     - Planned
1103
1104C++
1105---
1106.. doxygenclass:: pw::sync::ThreadNotification
1107   :members:
1108
1109.. cpp:namespace-push:: pw::sync::ThreadNotification
1110
1111.. list-table::
1112   :widths: 70 10 10 10
1113   :header-rows: 1
1114
1115   * - Safe to use in context
1116     - Thread
1117     - Interrupt
1118     - NMI
1119   * - :cpp:class:`pw::sync::ThreadNotification::ThreadNotification`
1120     - ✔
1121     -
1122     -
1123   * - :cpp:func:`pw::sync::ThreadNotification::~ThreadNotification`
1124     - ✔
1125     -
1126     -
1127   * - :cpp:func:`acquire`
1128     - ✔
1129     -
1130     -
1131   * - :cpp:func:`try_acquire`
1132     - ✔
1133     -
1134     -
1135   * - :cpp:func:`release`
1136     - ✔
1137     - ✔
1138     -
1139
1140.. cpp:namespace-pop::
1141
1142
1143Examples in C++
1144^^^^^^^^^^^^^^^
1145.. code-block:: cpp
1146
1147   #include "pw_sync/thread_notification.h"
1148   #include "pw_thread/thread_core.h"
1149
1150   class FooHandler() : public pw::thread::ThreadCore {
1151    // Public API invoked by other threads and/or interrupts.
1152    void NewFooAvailable() {
1153      new_foo_notification_.release();
1154    }
1155
1156    private:
1157     pw::sync::ThreadNotification new_foo_notification_;
1158
1159     // Thread function.
1160     void Run() override {
1161       while (true) {
1162         new_foo_notification_.acquire();
1163         HandleFoo();
1164       }
1165     }
1166
1167     void HandleFoo();
1168   }
1169
1170TimedThreadNotification
1171=======================
1172The :cpp:class:`TimedThreadNotification` is an extension of the
1173:cpp:class:`ThreadNotification` which offers timeout and deadline based
1174semantics.
1175
1176The :cpp:class:`TimedThreadNotification` is initialized to being empty (latch is
1177not set).
1178
1179.. Warning::
1180   This is a single consumer/waiter, multiple producer/notifier API!  The
1181   acquire APIs must only be invoked by a single consuming thread. As a result,
1182   having multiple threads receiving notifications via the acquire API is
1183   unsupported.
1184
1185Generic BinarySemaphore-based Backend
1186-------------------------------------
1187This module provides a generic backend for
1188:cpp:class:`pw::sync::TimedThreadNotification` via
1189``pw_sync:binary_semaphore_timed_thread_notification`` which uses a
1190:cpp:class:`pw::sync::BinarySemaphore` as the backing primitive. See
1191:ref:`BinarySemaphore <module-pw_sync-binary-semaphore>` for backend
1192availability.
1193
1194Optimized Backend
1195-----------------
1196.. list-table::
1197   :header-rows: 1
1198
1199   * - Supported on
1200     - Backend module
1201   * - FreeRTOS
1202     - ``pw_sync_freertos:timed_thread_notification``
1203   * - ThreadX
1204     - Not possible, use ``pw_sync:binary_semaphore_timed_thread_notification``
1205   * - embOS
1206     - Not needed, use ``pw_sync:binary_semaphore_timed_thread_notification``
1207   * - STL
1208     - Not planned, use ``pw_sync:binary_semaphore_timed_thread_notification``
1209   * - Zephyr
1210     - Planned
1211   * - CMSIS-RTOS API v2 & RTX5
1212     - Planned
1213
1214C++
1215---
1216.. doxygenclass:: pw::sync::TimedThreadNotification
1217   :members:
1218
1219.. cpp:namespace-push:: pw::sync::TimedThreadNotification
1220
1221.. list-table::
1222   :widths: 70 10 10 10
1223   :header-rows: 1
1224
1225   * - Safe to use in context
1226     - Thread
1227     - Interrupt
1228     - NMI
1229   * - :cpp:class:`pw::sync::TimedThreadNotification::TimedThreadNotification`
1230     - ✔
1231     -
1232     -
1233   * - :cpp:func:`pw::sync::TimedThreadNotification::~TimedThreadNotification`
1234     - ✔
1235     -
1236     -
1237   * - :cpp:func:`acquire`
1238     - ✔
1239     -
1240     -
1241   * - :cpp:func:`try_acquire`
1242     - ✔
1243     -
1244     -
1245   * - :cpp:func:`try_acquire_for`
1246     - ✔
1247     -
1248     -
1249   * - :cpp:func:`try_acquire_until`
1250     - ✔
1251     -
1252     -
1253   * - :cpp:func:`release`
1254     - ✔
1255     - ✔
1256     -
1257
1258.. cpp:namespace-pop::
1259
1260Examples in C++
1261^^^^^^^^^^^^^^^
1262.. code-block:: cpp
1263
1264   #include "pw_sync/timed_thread_notification.h"
1265   #include "pw_thread/thread_core.h"
1266
1267   class FooHandler() : public pw::thread::ThreadCore {
1268    // Public API invoked by other threads and/or interrupts.
1269    void NewFooAvailable() {
1270      new_foo_notification_.release();
1271    }
1272
1273    private:
1274     pw::sync::TimedThreadNotification new_foo_notification_;
1275
1276     // Thread function.
1277     void Run() override {
1278       while (true) {
1279         if (new_foo_notification_.try_acquire_for(kNotificationTimeout)) {
1280           HandleFoo();
1281         }
1282         DoOtherStuff();
1283       }
1284     }
1285
1286     void HandleFoo();
1287     void DoOtherStuff();
1288   }
1289
1290CountingSemaphore
1291=================
1292.. cpp:namespace-push:: pw::sync
1293
1294The :cpp:class:`CountingSemaphore` is a synchronization primitive that can be
1295used for counting events and/or resource management where receiver(s) can block
1296on acquire until notifier(s) signal by invoking release.
1297
1298Note that unlike :cpp:class:`Mutex`, priority inheritance is not used by
1299semaphores meaning semaphores are subject to unbounded priority inversions. Due
1300to this, Pigweed does not recommend semaphores for mutual exclusion.
1301
1302The :cpp:class:`CountingSemaphore` is initialized to being empty or having no
1303tokens.
1304
1305The entire API is thread safe, but only a subset is interrupt safe.
1306
1307.. Note::
1308   If there is only a single consuming thread, we recommend using a
1309   :cpp:class:`ThreadNotification` instead which can be much more efficient on
1310   some RTOSes such as FreeRTOS.
1311
1312.. cpp:namespace-pop::
1313
1314.. Warning::
1315   Releasing multiple tokens is often not natively supported, meaning you may
1316   end up invoking the native kernel API many times, i.e. once per token you
1317   are releasing!
1318
1319.. list-table::
1320   :header-rows: 1
1321
1322   * - Supported on
1323     - Backend module
1324   * - FreeRTOS
1325     - :ref:`module-pw_sync_freertos`
1326   * - ThreadX
1327     - :ref:`module-pw_sync_threadx`
1328   * - embOS
1329     - :ref:`module-pw_sync_embos`
1330   * - STL
1331     - :ref:`module-pw_sync_stl`
1332   * - Zephyr
1333     - Planned
1334   * - CMSIS-RTOS API v2 & RTX5
1335     - Planned
1336
1337C++
1338---
1339.. doxygenclass:: pw::sync::CountingSemaphore
1340   :members:
1341
1342.. cpp:namespace-push:: pw::sync::CountingSemaphore
1343
1344.. list-table::
1345   :widths: 70 10 10 10
1346   :header-rows: 1
1347
1348   * - Safe to use in context
1349     - Thread
1350     - Interrupt
1351     - NMI
1352   * - :cpp:class:`pw::sync::CountingSemaphore::CountingSemaphore`
1353     - ✔
1354     -
1355     -
1356   * - :cpp:func:`pw::sync::CountingSemaphore::~CountingSemaphore`
1357     - ✔
1358     -
1359     -
1360   * - :cpp:func:`acquire`
1361     - ✔
1362     -
1363     -
1364   * - :cpp:func:`try_acquire`
1365     - ✔
1366     - ✔
1367     -
1368   * - :cpp:func:`try_acquire_for`
1369     - ✔
1370     -
1371     -
1372   * - :cpp:func:`try_acquire_until`
1373     - ✔
1374     -
1375     -
1376   * - :cpp:func:`release`
1377     - ✔
1378     - ✔
1379     -
1380   * - :cpp:func:`max`
1381     - ✔
1382     - ✔
1383     - ✔
1384
1385.. cpp:namespace-pop::
1386
1387Examples in C++
1388^^^^^^^^^^^^^^^
1389As an example, a counting sempahore can be useful to run periodic tasks at
1390frequencies near or higher than the system clock tick rate in a way which lets
1391you detect whether you ever fall behind.
1392
1393.. code-block:: cpp
1394
1395   #include "pw_sync/counting_semaphore.h"
1396   #include "pw_thread/thread_core.h"
1397
1398   class PeriodicWorker() : public pw::thread::ThreadCore {
1399    // Public API invoked by a higher frequency timer interrupt.
1400    void TimeToExecute() {
1401      periodic_run_semaphore_.release();
1402    }
1403
1404    private:
1405     pw::sync::CountingSemaphore periodic_run_semaphore_;
1406
1407     // Thread function.
1408     void Run() override {
1409       while (true) {
1410         size_t behind_by_n_cycles = 0;
1411         periodic_run_semaphore_.acquire(); // Wait to run until it's time.
1412         while (periodic_run_semaphore_.try_acquire()) {
1413           ++behind_by_n_cycles;
1414         }
1415         if (behind_by_n_cycles > 0) {
1416           PW_LOG_WARNING("Not keeping up, behind by %d cycles",
1417                          behind_by_n_cycles);
1418         }
1419         DoPeriodicWork();
1420       }
1421     }
1422
1423     void DoPeriodicWork();
1424   }
1425
1426.. _module-pw_sync-binary-semaphore:
1427
1428BinarySemaphore
1429===============
1430.. cpp:namespace-push:: pw::sync
1431
1432:cpp:class:`BinarySemaphore` is a specialization of CountingSemaphore with an
1433arbitrary token limit of 1. Note that that ``max()`` is >= 1, meaning it may be
1434released up to ``max()`` times but only acquired once for those N releases.
1435
1436Implementations of :cpp:class:`BinarySemaphore` are typically more
1437efficient than the default implementation of :cpp:class:`CountingSemaphore`.
1438
1439The :cpp:class:`BinarySemaphore` is initialized to being empty or having no
1440tokens.
1441
1442.. cpp:namespace-pop::
1443
1444The entire API is thread safe, but only a subset is interrupt safe.
1445
1446.. Note::
1447   If there is only a single consuming thread, we recommend using a
1448   ThreadNotification instead which can be much more efficient on some RTOSes
1449   such as FreeRTOS.
1450
1451.. list-table::
1452   :header-rows: 1
1453
1454   * - Supported on
1455     - Backend module
1456   * - FreeRTOS
1457     - :ref:`module-pw_sync_freertos`
1458   * - ThreadX
1459     - :ref:`module-pw_sync_threadx`
1460   * - embOS
1461     - :ref:`module-pw_sync_embos`
1462   * - STL
1463     - :ref:`module-pw_sync_stl`
1464   * - Zephyr
1465     - Planned
1466   * - CMSIS-RTOS API v2 & RTX5
1467     - Planned
1468
1469C++
1470---
1471.. doxygenclass:: pw::sync::BinarySemaphore
1472   :members:
1473
1474.. cpp:namespace-push:: pw::sync::BinarySemaphore
1475
1476.. list-table::
1477   :widths: 70 10 10 10
1478   :header-rows: 1
1479
1480   * - Safe to use in context
1481     - Thread
1482     - Interrupt
1483     - NMI
1484   * - :cpp:class:`pw::sync::BinarySemaphore::BinarySemaphore`
1485     - ✔
1486     -
1487     -
1488   * - :cpp:func:`pw::sync::BinarySemaphore::~BinarySemaphore`
1489     - ✔
1490     -
1491     -
1492   * - :cpp:func:`acquire`
1493     - ✔
1494     -
1495     -
1496   * - :cpp:func:`try_acquire`
1497     - ✔
1498     - ✔
1499     -
1500   * - :cpp:func:`try_acquire_for`
1501     - ✔
1502     -
1503     -
1504   * - :cpp:func:`try_acquire_until`
1505     - ✔
1506     -
1507     -
1508   * - :cpp:func:`release`
1509     - ✔
1510     - ✔
1511     -
1512   * - :cpp:func:`max`
1513     - ✔
1514     - ✔
1515     - ✔
1516
1517.. cpp:namespace-pop::
1518
1519Examples in C++
1520^^^^^^^^^^^^^^^
1521.. code-block:: cpp
1522
1523   #include "pw_sync/binary_semaphore.h"
1524   #include "pw_thread/thread_core.h"
1525
1526   class FooHandler() : public pw::thread::ThreadCore {
1527    // Public API invoked by other threads and/or interrupts.
1528    void NewFooAvailable() {
1529      new_foo_semaphore_.release();
1530    }
1531
1532    private:
1533     pw::sync::BinarySemaphore new_foo_semaphore_;
1534
1535     // Thread function.
1536     void Run() override {
1537       while (true) {
1538         if (new_foo_semaphore_.try_acquire_for(kNotificationTimeout)) {
1539           HandleFoo();
1540         }
1541         DoOtherStuff();
1542       }
1543     }
1544
1545     void HandleFoo();
1546     void DoOtherStuff();
1547   }
1548
1549Conditional Variables
1550=====================
1551:cpp:class:`pw::sync::ConditionVariable` provides a condition variable
1552implementation that provides semantics and an API very similar to
1553`std::condition_variable
1554<https://en.cppreference.com/w/cpp/thread/condition_variable>`_ in the C++
1555Standard Library.
1556