1 // Copyright 2020 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <stdbool.h> 17 #include <stddef.h> 18 19 #include "pw_chrono/system_clock.h" 20 #include "pw_preprocessor/util.h" 21 22 #ifdef __cplusplus 23 24 #include "pw_sync_backend/counting_semaphore_native.h" 25 26 namespace pw::sync { 27 28 /// The `CountingSemaphore` is a synchronization primitive that can be used for 29 /// counting events and/or resource management where receiver(s) can block on 30 /// acquire until notifier(s) signal by invoking release. 31 /// Note that unlike Mutexes, priority inheritance is not used by semaphores 32 /// meaning semaphores are subject to unbounded priority inversions. 33 /// Pigweed does not recommend semaphores for mutual exclusion. The entire API 34 /// is thread safe but only a subset is IRQ safe. 35 /// 36 /// @rst 37 /// .. WARNING:: 38 /// In order to support global statically constructed ``CountingSemaphores`` 39 /// the user and/or backend MUST ensure that any initialization required in 40 /// your environment is done prior to the creation and/or initialization of 41 /// the native synchronization primitives (e.g. kernel initialization). 42 /// @endrst 43 /// 44 /// The `CountingSemaphore` is initialized to being empty or having no tokens. 45 class CountingSemaphore { 46 public: 47 using native_handle_type = backend::NativeCountingSemaphoreHandle; 48 49 CountingSemaphore(); 50 ~CountingSemaphore(); 51 CountingSemaphore(const CountingSemaphore&) = delete; 52 CountingSemaphore(CountingSemaphore&&) = delete; 53 CountingSemaphore& operator=(const CountingSemaphore&) = delete; 54 CountingSemaphore& operator=(CountingSemaphore&&) = delete; 55 56 /// Atomically increments the internal counter by the value of update. 57 /// Any thread(s) waiting for the counter to be greater than 0, i.e. blocked 58 /// in acquire, will subsequently be unblocked. 59 /// This is IRQ safe. 60 /// 61 /// @b Precondition: update >= 0 62 /// 63 /// @b Precondition: update <= max() - counter 64 void release(ptrdiff_t update = 1); 65 66 /// Decrements the internal counter by 1 or blocks indefinitely until it can. 67 /// 68 /// This is thread safe, but not IRQ safe. 69 void acquire(); 70 71 /// Tries to decrement by the internal counter by 1 without blocking. 72 /// Returns true if the internal counter was decremented successfully. 73 /// 74 /// This is IRQ safe. 75 bool try_acquire() noexcept; 76 77 /// Tries to decrement the internal counter by 1. Blocks until the specified 78 /// timeout has elapsed or the counter was decremented by 1, whichever comes 79 /// first. 80 /// 81 /// Returns true if the internal counter was decremented successfully. 82 /// This is thread safe, but not IRQ safe. 83 bool try_acquire_for(chrono::SystemClock::duration timeout); 84 85 /// Tries to decrement the internal counter by 1. Blocks until the specified 86 /// deadline has been reached or the counter was decremented by 1, whichever 87 /// comes first. 88 /// 89 /// Returns true if the internal counter was decremented successfully. 90 /// 91 /// This is thread safe, but not IRQ safe. 92 bool try_acquire_until(chrono::SystemClock::time_point deadline); 93 94 /// Returns the internal counter's maximum possible value. max()95 static constexpr ptrdiff_t max() noexcept { 96 return backend::kCountingSemaphoreMaxValue; 97 } 98 99 native_handle_type native_handle(); 100 101 private: 102 /// This may be a wrapper around a native type with additional members. 103 backend::NativeCountingSemaphore native_type_; 104 }; 105 106 } // namespace pw::sync 107 108 #include "pw_sync_backend/counting_semaphore_inline.h" 109 110 using pw_sync_CountingSemaphore = pw::sync::CountingSemaphore; 111 112 #else // !defined(__cplusplus) 113 114 typedef struct pw_sync_CountingSemaphore pw_sync_CountingSemaphore; 115 116 #endif // __cplusplus 117 118 PW_EXTERN_C_START 119 120 void pw_sync_CountingSemaphore_Release(pw_sync_CountingSemaphore* semaphore); 121 void pw_sync_CountingSemaphore_ReleaseNum(pw_sync_CountingSemaphore* semaphore, 122 ptrdiff_t update); 123 void pw_sync_CountingSemaphore_Acquire(pw_sync_CountingSemaphore* semaphore); 124 bool pw_sync_CountingSemaphore_TryAcquire(pw_sync_CountingSemaphore* semaphore); 125 bool pw_sync_CountingSemaphore_TryAcquireFor( 126 pw_sync_CountingSemaphore* semaphore, 127 pw_chrono_SystemClock_Duration timeout); 128 bool pw_sync_CountingSemaphore_TryAcquireUntil( 129 pw_sync_CountingSemaphore* semaphore, 130 pw_chrono_SystemClock_TimePoint deadline); 131 ptrdiff_t pw_sync_CountingSemaphore_Max(void); 132 133 PW_EXTERN_C_END 134