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