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 [[nodiscard]] 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 [[nodiscard]] 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 [[nodiscard]] bool try_acquire_until( 93 chrono::SystemClock::time_point deadline); 94 95 /// Returns the internal counter's maximum possible value. max()96 [[nodiscard]] static constexpr ptrdiff_t max() noexcept { 97 return backend::kCountingSemaphoreMaxValue; 98 } 99 100 native_handle_type native_handle(); 101 102 private: 103 /// This may be a wrapper around a native type with additional members. 104 backend::NativeCountingSemaphore native_type_; 105 }; 106 107 } // namespace pw::sync 108 109 #include "pw_sync_backend/counting_semaphore_inline.h" 110 111 using pw_sync_CountingSemaphore = pw::sync::CountingSemaphore; 112 113 #else // !defined(__cplusplus) 114 115 typedef struct pw_sync_CountingSemaphore pw_sync_CountingSemaphore; 116 117 #endif // __cplusplus 118 119 PW_EXTERN_C_START 120 121 void pw_sync_CountingSemaphore_Release(pw_sync_CountingSemaphore* semaphore); 122 void pw_sync_CountingSemaphore_ReleaseNum(pw_sync_CountingSemaphore* semaphore, 123 ptrdiff_t update); 124 void pw_sync_CountingSemaphore_Acquire(pw_sync_CountingSemaphore* semaphore); 125 bool pw_sync_CountingSemaphore_TryAcquire(pw_sync_CountingSemaphore* semaphore); 126 bool pw_sync_CountingSemaphore_TryAcquireFor( 127 pw_sync_CountingSemaphore* semaphore, 128 pw_chrono_SystemClock_Duration timeout); 129 bool pw_sync_CountingSemaphore_TryAcquireUntil( 130 pw_sync_CountingSemaphore* semaphore, 131 pw_chrono_SystemClock_TimePoint deadline); 132 ptrdiff_t pw_sync_CountingSemaphore_Max(void); 133 134 PW_EXTERN_C_END 135