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