• 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 
15 #include "pw_sync/borrow.h"
16 
17 #include <chrono>
18 #include <ratio>
19 
20 #include "gtest/gtest.h"
21 #include "pw_assert/check.h"
22 #include "pw_sync/virtual_basic_lockable.h"
23 
24 namespace pw::sync {
25 namespace {
26 
27 template <typename Lock>
28 class BorrowableTest : public ::testing::Test {
29  protected:
30   static constexpr int kInitialValue = 42;
31 
BorrowableTest()32   BorrowableTest()
33       : foo_{.value = kInitialValue}, borrowable_foo_(foo_, lock_) {}
34 
SetUp()35   void SetUp() override {
36     EXPECT_FALSE(lock_.locked());  // Ensure it's not locked on construction.
37   }
38 
39   struct Foo {
40     int value;
41   };
42   Lock lock_;
43   Foo foo_;
44   Borrowable<Foo, Lock> borrowable_foo_;
45 };
46 
47 class BasicLockable : public VirtualBasicLockable {
48  public:
49   virtual ~BasicLockable() = default;
50 
locked() const51   bool locked() const { return locked_; }
52 
53  protected:
54   bool locked_ = false;
55 
56  private:
DoLockOperation(Operation operation)57   void DoLockOperation(Operation operation) override {
58     switch (operation) {
59       case Operation::kLock:
60         PW_CHECK(!locked_, "Recursive lock detected");
61         locked_ = true;
62         return;
63 
64       case Operation::kUnlock:
65       default:
66         PW_CHECK(locked_, "Unlock while unlocked detected");
67         locked_ = false;
68         return;
69     }
70   }
71 };
72 
73 using BorrowableBasicLockableTest = BorrowableTest<BasicLockable>;
74 
TEST_F(BorrowableBasicLockableTest,Acquire)75 TEST_F(BorrowableBasicLockableTest, Acquire) {
76   {
77     BorrowedPointer<Foo, BasicLockable> borrowed_foo =
78         borrowable_foo_.acquire();
79     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
80     EXPECT_EQ(borrowed_foo->value, kInitialValue);
81     borrowed_foo->value = 13;
82   }
83   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
84   EXPECT_EQ(foo_.value, 13);
85 }
86 
TEST_F(BorrowableBasicLockableTest,RepeatedAcquire)87 TEST_F(BorrowableBasicLockableTest, RepeatedAcquire) {
88   {
89     BorrowedPointer<Foo, BasicLockable> borrowed_foo =
90         borrowable_foo_.acquire();
91     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
92     EXPECT_EQ(borrowed_foo->value, kInitialValue);
93     borrowed_foo->value = 13;
94   }
95   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
96   {
97     BorrowedPointer<Foo, BasicLockable> borrowed_foo =
98         borrowable_foo_.acquire();
99     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
100     EXPECT_EQ(borrowed_foo->value, 13);
101   }
102   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
103 }
104 
TEST_F(BorrowableBasicLockableTest,Moveable)105 TEST_F(BorrowableBasicLockableTest, Moveable) {
106   Borrowable<Foo, BasicLockable> borrowable_foo = std::move(borrowable_foo_);
107   {
108     BorrowedPointer<Foo, BasicLockable> borrowed_foo = borrowable_foo.acquire();
109     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
110     EXPECT_EQ(borrowed_foo->value, kInitialValue);
111     borrowed_foo->value = 13;
112   }
113   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
114 }
115 
TEST_F(BorrowableBasicLockableTest,Copyable)116 TEST_F(BorrowableBasicLockableTest, Copyable) {
117   const Borrowable<Foo, BasicLockable>& other = borrowable_foo_;
118   Borrowable<Foo, BasicLockable> borrowable_foo(other);
119   {
120     BorrowedPointer<Foo, BasicLockable> borrowed_foo = borrowable_foo.acquire();
121     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
122     EXPECT_EQ(borrowed_foo->value, kInitialValue);
123     borrowed_foo->value = 13;
124   }
125   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
126 }
127 
128 class Lockable : public BasicLockable {
129  public:
try_lock()130   bool try_lock() {
131     if (locked()) {
132       return false;
133     }
134     locked_ = true;
135     return true;
136   }
137 };
138 
139 using BorrowableLockableTest = BorrowableTest<Lockable>;
140 
TEST_F(BorrowableLockableTest,Acquire)141 TEST_F(BorrowableLockableTest, Acquire) {
142   {
143     BorrowedPointer<Foo, Lockable> borrowed_foo = borrowable_foo_.acquire();
144     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
145     EXPECT_EQ(borrowed_foo->value, kInitialValue);
146     borrowed_foo->value = 13;
147   }
148   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
149   EXPECT_EQ(foo_.value, 13);
150 }
151 
TEST_F(BorrowableLockableTest,RepeatedAcquire)152 TEST_F(BorrowableLockableTest, RepeatedAcquire) {
153   {
154     BorrowedPointer<Foo, Lockable> borrowed_foo = borrowable_foo_.acquire();
155     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
156     EXPECT_EQ(borrowed_foo->value, kInitialValue);
157     borrowed_foo->value = 13;
158   }
159   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
160   {
161     BorrowedPointer<Foo, Lockable> borrowed_foo = borrowable_foo_.acquire();
162     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
163     EXPECT_EQ(borrowed_foo->value, 13);
164   }
165   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
166 }
167 
TEST_F(BorrowableLockableTest,TryAcquireSuccess)168 TEST_F(BorrowableLockableTest, TryAcquireSuccess) {
169   {
170     std::optional<BorrowedPointer<Foo, Lockable>> maybe_borrowed_foo =
171         borrowable_foo_.try_acquire();
172     ASSERT_TRUE(maybe_borrowed_foo.has_value());
173     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
174     EXPECT_EQ(maybe_borrowed_foo.value()->value, kInitialValue);
175   }
176   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
177 }
178 
TEST_F(BorrowableLockableTest,TryAcquireFailure)179 TEST_F(BorrowableLockableTest, TryAcquireFailure) {
180   lock_.lock();
181   EXPECT_TRUE(lock_.locked());
182   {
183     std::optional<BorrowedPointer<Foo, Lockable>> maybe_borrowed_foo =
184         borrowable_foo_.try_acquire();
185     EXPECT_FALSE(maybe_borrowed_foo.has_value());
186   }
187   EXPECT_TRUE(lock_.locked());
188   lock_.unlock();
189 }
190 
191 struct Clock {
192   using rep = int64_t;
193   using period = std::micro;
194   using duration = std::chrono::duration<rep, period>;
195   using time_point = std::chrono::time_point<Clock>;
196 };
197 
198 class TimedLockable : public Lockable {
199  public:
try_lock()200   bool try_lock() {
201     if (locked()) {
202       return false;
203     }
204     locked_ = true;
205     return true;
206   }
207 
try_lock_for(const Clock::duration &)208   bool try_lock_for(const Clock::duration&) { return try_lock(); }
try_lock_until(const Clock::time_point &)209   bool try_lock_until(const Clock::time_point&) { return try_lock(); }
210 };
211 
212 using BorrowableTimedLockableTest = BorrowableTest<TimedLockable>;
213 
TEST_F(BorrowableTimedLockableTest,Acquire)214 TEST_F(BorrowableTimedLockableTest, Acquire) {
215   {
216     BorrowedPointer<Foo, TimedLockable> borrowed_foo =
217         borrowable_foo_.acquire();
218     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
219     EXPECT_EQ(borrowed_foo->value, kInitialValue);
220     borrowed_foo->value = 13;
221   }
222   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
223   EXPECT_EQ(foo_.value, 13);
224 }
225 
TEST_F(BorrowableTimedLockableTest,RepeatedAcquire)226 TEST_F(BorrowableTimedLockableTest, RepeatedAcquire) {
227   {
228     BorrowedPointer<Foo, TimedLockable> borrowed_foo =
229         borrowable_foo_.acquire();
230     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
231     EXPECT_EQ(borrowed_foo->value, kInitialValue);
232     borrowed_foo->value = 13;
233   }
234   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
235   {
236     BorrowedPointer<Foo, TimedLockable> borrowed_foo =
237         borrowable_foo_.acquire();
238     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
239     EXPECT_EQ(borrowed_foo->value, 13);
240   }
241   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
242 }
243 
TEST_F(BorrowableTimedLockableTest,TryAcquireSuccess)244 TEST_F(BorrowableTimedLockableTest, TryAcquireSuccess) {
245   {
246     std::optional<BorrowedPointer<Foo, TimedLockable>> maybe_borrowed_foo =
247         borrowable_foo_.try_acquire();
248     ASSERT_TRUE(maybe_borrowed_foo.has_value());
249     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
250     EXPECT_EQ(maybe_borrowed_foo.value()->value, kInitialValue);
251   }
252   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
253 }
254 
TEST_F(BorrowableTimedLockableTest,TryAcquireFailure)255 TEST_F(BorrowableTimedLockableTest, TryAcquireFailure) {
256   lock_.lock();
257   EXPECT_TRUE(lock_.locked());
258   {
259     std::optional<BorrowedPointer<Foo, TimedLockable>> maybe_borrowed_foo =
260         borrowable_foo_.try_acquire();
261     EXPECT_FALSE(maybe_borrowed_foo.has_value());
262   }
263   EXPECT_TRUE(lock_.locked());
264   lock_.unlock();
265 }
266 
TEST_F(BorrowableTimedLockableTest,TryAcquireForSuccess)267 TEST_F(BorrowableTimedLockableTest, TryAcquireForSuccess) {
268   {
269     std::optional<BorrowedPointer<Foo, TimedLockable>> maybe_borrowed_foo =
270         borrowable_foo_.try_acquire_for(std::chrono::seconds(0));
271     ASSERT_TRUE(maybe_borrowed_foo.has_value());
272     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
273     EXPECT_EQ(maybe_borrowed_foo.value()->value, kInitialValue);
274   }
275   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
276 }
277 
TEST_F(BorrowableTimedLockableTest,TryAcquireForFailure)278 TEST_F(BorrowableTimedLockableTest, TryAcquireForFailure) {
279   lock_.lock();
280   EXPECT_TRUE(lock_.locked());
281   {
282     std::optional<BorrowedPointer<Foo, TimedLockable>> maybe_borrowed_foo =
283         borrowable_foo_.try_acquire_for(std::chrono::seconds(0));
284     EXPECT_FALSE(maybe_borrowed_foo.has_value());
285   }
286   EXPECT_TRUE(lock_.locked());
287   lock_.unlock();
288 }
289 
TEST_F(BorrowableTimedLockableTest,TryAcquireUntilSuccess)290 TEST_F(BorrowableTimedLockableTest, TryAcquireUntilSuccess) {
291   {
292     std::optional<BorrowedPointer<Foo, TimedLockable>> maybe_borrowed_foo =
293         borrowable_foo_.try_acquire_until(
294             Clock::time_point(std::chrono::seconds(0)));
295     ASSERT_TRUE(maybe_borrowed_foo.has_value());
296     EXPECT_TRUE(lock_.locked());  // Ensure the lock is held.
297     EXPECT_EQ(maybe_borrowed_foo.value()->value, kInitialValue);
298   }
299   EXPECT_FALSE(lock_.locked());  // Ensure the lock is released.
300 }
301 
TEST_F(BorrowableTimedLockableTest,TryAcquireUntilFailure)302 TEST_F(BorrowableTimedLockableTest, TryAcquireUntilFailure) {
303   lock_.lock();
304   EXPECT_TRUE(lock_.locked());
305   {
306     std::optional<BorrowedPointer<Foo, TimedLockable>> maybe_borrowed_foo =
307         borrowable_foo_.try_acquire_until(
308             Clock::time_point(std::chrono::seconds(0)));
309     EXPECT_FALSE(maybe_borrowed_foo.has_value());
310   }
311   EXPECT_TRUE(lock_.locked());
312   lock_.unlock();
313 }
314 
315 }  // namespace
316 }  // namespace pw::sync
317