1 // Copyright 2021 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 "pw_polyfill/language_feature_macros.h" 17 #include "pw_sync/lock_annotations.h" 18 19 namespace pw::sync { 20 21 /// The `VirtualBasicLockable` is a virtual lock abstraction for locks which 22 /// meet the C++ named BasicLockable requirements of lock() and unlock(). 23 /// 24 /// This virtual indirection is useful in case you need configurable lock 25 /// selection in a portable module where the final type is not defined upstream 26 /// and ergo module configuration cannot be used or in case the lock type is not 27 /// fixed at compile time, for example to support run time and crash time use of 28 /// an object without incurring the code size hit for templating the object. 29 class PW_LOCKABLE("pw::sync::VirtualBasicLockable") VirtualBasicLockable { 30 public: lock()31 void lock() PW_EXCLUSIVE_LOCK_FUNCTION() { 32 DoLockOperation(Operation::kLock); 33 } 34 unlock()35 void unlock() PW_UNLOCK_FUNCTION() { DoLockOperation(Operation::kUnlock); } 36 37 protected: 38 ~VirtualBasicLockable() = default; 39 40 enum class Operation { 41 kLock, 42 kUnlock, 43 }; 44 45 private: 46 /// Uses a single virtual method with an enum to minimize the vtable cost per 47 /// implementation of `VirtualBasicLockable`. 48 virtual void DoLockOperation(Operation operation) = 0; 49 }; 50 51 /// The `NoOpLock` is a type of `VirtualBasicLockable` that does nothing, i.e. 52 /// lock operations are no-ops. 53 class PW_LOCKABLE("pw::sync::NoOpLock") NoOpLock final 54 : public VirtualBasicLockable { 55 public: NoOpLock()56 constexpr NoOpLock() {} 57 NoOpLock(const NoOpLock&) = delete; 58 NoOpLock(NoOpLock&&) = delete; 59 NoOpLock& operator=(const NoOpLock&) = delete; 60 NoOpLock& operator=(NoOpLock&&) = delete; 61 62 /// Gives access to a global NoOpLock instance. It is not necessary to have 63 /// multiple NoOpLock instances since they have no state and do nothing. Instance()64 static NoOpLock& Instance() { 65 PW_CONSTINIT static NoOpLock lock; 66 return lock; 67 } 68 69 private: DoLockOperation(Operation)70 void DoLockOperation(Operation) override {} 71 }; 72 73 /// Templated base class to facilitate making "Virtual{LockType}" from a 74 /// "LockType" class that provides `lock()` and `unlock()` methods. 75 /// The resulting classes will derive from `VirtualBasicLockable`. 76 template <typename LockType> 77 class GenericBasicLockable : public VirtualBasicLockable { 78 public: 79 virtual ~GenericBasicLockable() = default; 80 81 protected: impl()82 LockType& impl() { return impl_; } 83 84 private: DoLockOperation(Operation operation)85 void DoLockOperation(Operation operation) override 86 PW_NO_LOCK_SAFETY_ANALYSIS { 87 switch (operation) { 88 case Operation::kLock: 89 return impl_.lock(); 90 91 case Operation::kUnlock: 92 default: 93 return impl_.unlock(); 94 } 95 } 96 97 LockType impl_; 98 }; 99 100 /// Templated base class to facilitate making "Virtual{LockType}" from a 101 /// "LockType" class that derives from `VirtualBasicLockable`, and also meets 102 /// the C++ named Lockable requirements by providing try_lock(). 103 /// 104 /// Example: 105 /// class VirtualMutex : public GenericLockable<Mutex> {}; 106 template <typename LockType> 107 class PW_LOCKABLE("pw::sync::GenericLockable") GenericLockable 108 : public GenericBasicLockable<LockType> { 109 public: try_lock()110 [[nodiscard]] bool try_lock() PW_EXCLUSIVE_TRYLOCK_FUNCTION(true) { 111 return GenericBasicLockable<LockType>::impl().try_lock(); 112 } 113 }; 114 115 } // namespace pw::sync 116