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 18 #include "pw_chrono/system_clock.h" 19 #include "pw_preprocessor/util.h" 20 #include "pw_sync/lock_annotations.h" 21 #include "pw_sync/mutex.h" 22 23 #ifdef __cplusplus 24 25 #include "pw_sync/virtual_basic_lockable.h" 26 27 namespace pw::sync { 28 29 // The TimedMutex is a synchronization primitive that can be used to protect 30 // shared data from being simultaneously accessed by multiple threads with 31 // timeouts and deadlines, extending the Mutex. 32 // It offers exclusive, non-recursive ownership semantics where priority 33 // inheritance is used to solve the classic priority-inversion problem. 34 // This is thread safe, but NOT IRQ safe. 35 // 36 // WARNING: In order to support global statically constructed TimedMutexes, the 37 // user and/or backend MUST ensure that any initialization required in your 38 // environment is done prior to the creation and/or initialization of the native 39 // synchronization primitives (e.g. kernel initialization). 40 class TimedMutex : public Mutex { 41 public: 42 TimedMutex() = default; 43 ~TimedMutex() = default; 44 TimedMutex(const TimedMutex&) = delete; 45 TimedMutex(TimedMutex&&) = delete; 46 TimedMutex& operator=(const TimedMutex&) = delete; 47 TimedMutex& operator=(TimedMutex&&) = delete; 48 49 // Tries to lock the mutex. Blocks until specified the timeout has elapsed or 50 // the lock is acquired, whichever comes first. 51 // Returns true if the mutex was successfully acquired. 52 // 53 // PRECONDITION: 54 // The lock isn't already held by this thread. Recursive locking is 55 // undefined behavior. 56 bool try_lock_for(chrono::SystemClock::duration timeout) 57 PW_EXCLUSIVE_TRYLOCK_FUNCTION(true); 58 59 // Tries to lock the mutex. Blocks until specified deadline has been reached 60 // or the lock is acquired, whichever comes first. 61 // Returns true if the mutex was successfully acquired. 62 // 63 // PRECONDITION: 64 // The lock isn't already held by this thread. Recursive locking is 65 // undefined behavior. 66 bool try_lock_until(chrono::SystemClock::time_point deadline) 67 PW_EXCLUSIVE_TRYLOCK_FUNCTION(true); 68 }; 69 70 class PW_LOCKABLE("pw::sync::VirtualTimedMutex") VirtualTimedMutex final 71 : public VirtualBasicLockable { 72 public: 73 VirtualTimedMutex() = default; 74 75 VirtualTimedMutex(const VirtualTimedMutex&) = delete; 76 VirtualTimedMutex(VirtualTimedMutex&&) = delete; 77 VirtualTimedMutex& operator=(const VirtualTimedMutex&) = delete; 78 VirtualTimedMutex& operator=(VirtualTimedMutex&&) = delete; 79 timed_mutex()80 TimedMutex& timed_mutex() { return timed_mutex_; } 81 82 private: DoLockOperation(Operation operation)83 void DoLockOperation(Operation operation) override 84 PW_NO_LOCK_SAFETY_ANALYSIS { 85 switch (operation) { 86 case Operation::kLock: 87 return timed_mutex_.lock(); 88 89 case Operation::kUnlock: 90 default: 91 return timed_mutex_.unlock(); 92 } 93 } 94 95 TimedMutex timed_mutex_; 96 }; 97 98 } // namespace pw::sync 99 100 #include "pw_sync_backend/timed_mutex_inline.h" 101 102 using pw_sync_TimedMutex = pw::sync::TimedMutex; 103 104 #else // !defined(__cplusplus) 105 106 typedef struct pw_sync_TimedMutex pw_sync_TimedMutex; 107 108 #endif // __cplusplus 109 110 PW_EXTERN_C_START 111 112 void pw_sync_TimedMutex_Lock(pw_sync_TimedMutex* mutex) 113 PW_NO_LOCK_SAFETY_ANALYSIS; 114 bool pw_sync_TimedMutex_TryLock(pw_sync_TimedMutex* mutex) 115 PW_NO_LOCK_SAFETY_ANALYSIS; 116 bool pw_sync_TimedMutex_TryLockFor(pw_sync_TimedMutex* mutex, 117 pw_chrono_SystemClock_Duration timeout) 118 PW_NO_LOCK_SAFETY_ANALYSIS; 119 bool pw_sync_TimedMutex_TryLockUntil(pw_sync_TimedMutex* mutex, 120 pw_chrono_SystemClock_TimePoint deadline) 121 PW_NO_LOCK_SAFETY_ANALYSIS; 122 void pw_sync_TimedMutex_Unlock(pw_sync_TimedMutex* mutex) 123 PW_NO_LOCK_SAFETY_ANALYSIS; 124 125 PW_EXTERN_C_END 126