• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   // Postcondition: The other BorrowedPointer is no longer valid and will assert
42   //     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   // Provides access to the borrowed object directly.
65   //
66   // NOTE: The member of pointer member access operator, operator->(), is
67   // recommended over this API as this is prone to leaking references. However,
68   // this is sometimes necessary.
69   //
70   // WARNING: Be careful not to leak references to the borrowed object!
71   GuardedType& operator*() {
72     PW_ASSERT(object_ != nullptr);  // Ensure this isn't a stale moved instance.
73     return *object_;
74   }
75 
76  private:
77   // Allow BorrowedPointer creation inside of Borrowable's acquire methods.
78   template <typename G, typename L>
79   friend class Borrowable;
80 
BorrowedPointer(Lock & lock,GuardedType & object)81   constexpr BorrowedPointer(Lock& lock, GuardedType& object)
82       : lock_(&lock), object_(&object) {}
83 
84   Lock* lock_;
85   GuardedType* object_;
86 };
87 
88 // The Borrowable is a helper construct that enables callers to borrow an object
89 // which is guarded by a lock.
90 //
91 // Users who need access to the guarded object can ask to acquire a
92 // BorrowedPointer which permits access while the lock is held.
93 //
94 // This class is compatible with locks which comply with BasicLockable,
95 // Lockable, and TimedLockable C++ named requirements.
96 template <typename GuardedType, typename Lock = pw::sync::VirtualBasicLockable>
97 class Borrowable {
98  public:
Borrowable(GuardedType & object,Lock & lock)99   constexpr Borrowable(GuardedType& object, Lock& lock) noexcept
100       : lock_(&lock), object_(&object) {}
101 
102   Borrowable(const Borrowable&) = default;
103   Borrowable& operator=(const Borrowable&) = default;
104   Borrowable(Borrowable&& other) = default;
105   Borrowable& operator=(Borrowable&& other) = default;
106 
107   // Blocks indefinitely until the object can be borrowed. Failures are fatal.
acquire()108   BorrowedPointer<GuardedType, Lock> acquire() PW_NO_LOCK_SAFETY_ANALYSIS {
109     lock_->lock();
110     return BorrowedPointer<GuardedType, Lock>(*lock_, *object_);
111   }
112 
113   // Tries to borrow the object in a non-blocking manner. Returns a
114   // BorrowedPointer on success, otherwise std::nullopt (nothing).
try_acquire()115   std::optional<BorrowedPointer<GuardedType, Lock>> try_acquire() {
116     if (!lock_->try_lock()) {
117       return std::nullopt;
118     }
119     return BorrowedPointer<GuardedType, Lock>(*lock_, *object_);
120   }
121 
122   // Tries to borrow the object. Blocks until the specified timeout has elapsed
123   // or the object has been borrowed, whichever comes first. Returns a
124   // BorrowedPointer on success, otherwise std::nullopt (nothing).
125   template <class Rep, class Period>
try_acquire_for(std::chrono::duration<Rep,Period> timeout)126   std::optional<BorrowedPointer<GuardedType, Lock>> try_acquire_for(
127       std::chrono::duration<Rep, Period> timeout) {
128     if (!lock_->try_lock_for(timeout)) {
129       return std::nullopt;
130     }
131     return BorrowedPointer<GuardedType, Lock>(*lock_, *object_);
132   }
133 
134   // Tries to borrow the object. Blocks until the specified deadline has passed
135   // or the object has been borrowed, whichever comes first. Returns a
136   // BorrowedPointer on success, otherwise std::nullopt (nothing).
137   template <class Clock, class Duration>
try_acquire_until(std::chrono::time_point<Clock,Duration> deadline)138   std::optional<BorrowedPointer<GuardedType, Lock>> try_acquire_until(
139       std::chrono::time_point<Clock, Duration> deadline) {
140     if (!lock_->try_lock_until(deadline)) {
141       return std::nullopt;
142     }
143     return BorrowedPointer<GuardedType, Lock>(*lock_, *object_);
144   }
145 
146  private:
147   Lock* lock_;
148   GuardedType* object_;
149 };
150 
151 }  // namespace pw::sync
152