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 <chrono> 17 #include <optional> 18 #include <type_traits> 19 20 #include "pw_assert/assert.h" 21 #include "pw_sync/lock_annotations.h" 22 #include "pw_sync/virtual_basic_lockable.h" 23 24 namespace pw::sync { 25 26 /// The `BorrowedPointer` is an RAII handle which wraps a pointer to a borrowed 27 /// object along with a held lock which is guarding the object. When destroyed, 28 /// the lock is released. 29 template <typename GuardedType, typename Lock = pw::sync::VirtualBasicLockable> 30 class BorrowedPointer { 31 public: 32 /// Release the lock on destruction. ~BorrowedPointer()33 ~BorrowedPointer() { 34 if (lock_ != nullptr) { 35 lock_->unlock(); 36 } 37 } 38 39 /// This object is moveable, but not copyable. 40 /// 41 /// @b Postcondition: The other BorrowedPointer is no longer valid and will 42 /// assert if the GuardedType is accessed. BorrowedPointer(BorrowedPointer && other)43 BorrowedPointer(BorrowedPointer&& other) 44 : lock_(other.lock_), object_(other.object_) { 45 other.lock_ = nullptr; 46 other.object_ = nullptr; 47 } 48 BorrowedPointer& operator=(BorrowedPointer&& other) { 49 lock_ = other.lock_; 50 object_ = other.object_; 51 other.lock_ = nullptr; 52 other.object_ = nullptr; 53 return *this; 54 } 55 BorrowedPointer(const BorrowedPointer&) = delete; 56 BorrowedPointer& operator=(const BorrowedPointer&) = delete; 57 58 /// Provides access to the borrowed object's members. 59 GuardedType* operator->() { 60 PW_ASSERT(object_ != nullptr); // Ensure this isn't a stale moved instance. 61 return object_; 62 } 63 64 /// Const overload 65 const GuardedType* operator->() const { 66 PW_ASSERT(object_ != nullptr); // Ensure this isn't a stale moved instance. 67 return object_; 68 } 69 70 /// Provides access to the borrowed object directly. 71 /// 72 /// @rst 73 /// .. note:: 74 /// The member of pointer member access operator, ``operator->()``, is 75 /// recommended over this API as this is prone to leaking references. 76 /// However, this is sometimes necessary. 77 /// 78 /// .. warning: 79 /// Be careful not to leak references to the borrowed object! 80 /// @endrst 81 GuardedType& operator*() { 82 PW_ASSERT(object_ != nullptr); // Ensure this isn't a stale moved instance. 83 return *object_; 84 } 85 86 /// Const overload 87 const GuardedType& operator*() const { 88 PW_ASSERT(object_ != nullptr); // Ensure this isn't a stale moved instance. 89 return *object_; 90 } 91 92 private: 93 /// Allow BorrowedPointer creation inside of Borrowable's acquire methods. 94 template <typename G, typename L> 95 friend class Borrowable; 96 BorrowedPointer(Lock & lock,GuardedType & object)97 constexpr BorrowedPointer(Lock& lock, GuardedType& object) 98 : lock_(&lock), object_(&object) {} 99 100 Lock* lock_; 101 GuardedType* object_; 102 }; 103 104 /// The `Borrowable` is a helper construct that enables callers to borrow an 105 /// object which is guarded by a lock. 106 /// 107 /// Users who need access to the guarded object can ask to acquire a 108 /// `BorrowedPointer` which permits access while the lock is held. 109 /// 110 /// This class is compatible with locks which comply with `BasicLockable`, 111 /// `Lockable`, and `TimedLockable` C++ named requirements. 112 template <typename GuardedType, typename Lock = pw::sync::VirtualBasicLockable> 113 class Borrowable { 114 public: Borrowable(GuardedType & object,Lock & lock)115 constexpr Borrowable(GuardedType& object, Lock& lock) noexcept 116 : lock_(&lock), object_(&object) {} 117 118 Borrowable(const Borrowable&) = default; 119 Borrowable& operator=(const Borrowable&) = default; 120 Borrowable(Borrowable&& other) = default; 121 Borrowable& operator=(Borrowable&& other) = default; 122 123 /// Blocks indefinitely until the object can be borrowed. Failures are fatal. acquire()124 BorrowedPointer<GuardedType, Lock> acquire() PW_NO_LOCK_SAFETY_ANALYSIS { 125 lock_->lock(); 126 return BorrowedPointer<GuardedType, Lock>(*lock_, *object_); 127 } 128 129 /// Tries to borrow the object in a non-blocking manner. Returns a 130 /// BorrowedPointer on success, otherwise `std::nullopt` (nothing). try_acquire()131 std::optional<BorrowedPointer<GuardedType, Lock>> try_acquire() { 132 if (!lock_->try_lock()) { 133 return std::nullopt; 134 } 135 return BorrowedPointer<GuardedType, Lock>(*lock_, *object_); 136 } 137 138 /// Tries to borrow the object. Blocks until the specified timeout has elapsed 139 /// or the object has been borrowed, whichever comes first. Returns a 140 /// `BorrowedPointer` on success, otherwise `std::nullopt` (nothing). 141 template <class Rep, class Period> try_acquire_for(std::chrono::duration<Rep,Period> timeout)142 std::optional<BorrowedPointer<GuardedType, Lock>> try_acquire_for( 143 std::chrono::duration<Rep, Period> timeout) { 144 if (!lock_->try_lock_for(timeout)) { 145 return std::nullopt; 146 } 147 return BorrowedPointer<GuardedType, Lock>(*lock_, *object_); 148 } 149 150 /// Tries to borrow the object. Blocks until the specified deadline has passed 151 /// or the object has been borrowed, whichever comes first. Returns a 152 /// `BorrowedPointer` on success, otherwise `std::nullopt` (nothing). 153 template <class Clock, class Duration> try_acquire_until(std::chrono::time_point<Clock,Duration> deadline)154 std::optional<BorrowedPointer<GuardedType, Lock>> try_acquire_until( 155 std::chrono::time_point<Clock, Duration> deadline) { 156 if (!lock_->try_lock_until(deadline)) { 157 return std::nullopt; 158 } 159 return BorrowedPointer<GuardedType, Lock>(*lock_, *object_); 160 } 161 162 private: 163 Lock* lock_; 164 GuardedType* object_; 165 }; 166 167 } // namespace pw::sync 168