22
22
23
23
#include " gtest/gtest.h"
24
24
25
+ #include < Mocks/Death_Test.h>
26
+
25
27
#include " Basics/Guarded.h"
26
28
#include " Basics/Mutex.h"
27
29
#include " Basics/MutexLocker.h"
@@ -51,7 +53,11 @@ class GuardedTest : public ::testing::Test {
51
53
using Guarded = Guarded<V, Mutex, Lock>;
52
54
};
53
55
56
+ template <typename T>
57
+ class GuardedDeathTest : public GuardedTest <T> {};
58
+
54
59
TYPED_TEST_CASE_P (GuardedTest);
60
+ TYPED_TEST_CASE_P (GuardedDeathTest);
55
61
56
62
// Test helper that acquires a lock;
57
63
// then executes a callback that tries to acquire the same lock;
@@ -160,6 +166,46 @@ TYPED_TEST_P(GuardedTest, test_guard_waits_for_access) {
160
166
testWaitForLock<GuardedType>(acquireGuard);
161
167
}
162
168
169
+ TYPED_TEST_P (GuardedTest, test_guard_unlock_releases_mutex) {
170
+ auto guardedObj = typename TestFixture::template Guarded<UnderGuard>{1 };
171
+ EXPECT_EQ (1 , guardedObj.copy ().val );
172
+ auto guard = guardedObj.getLockedGuard ();
173
+ EXPECT_EQ (1 , guard.get ().val );
174
+ guard.get ().val = 2 ;
175
+ EXPECT_EQ (2 , guard.get ().val );
176
+ guard.unlock ();
177
+
178
+ auto threadStarted = std::atomic<bool >{false };
179
+ auto couldAcquireLock = std::atomic<bool >{false };
180
+ auto thr = std::thread ([&] {
181
+ threadStarted.store (true , std::memory_order_release);
182
+ guardedObj.doUnderLock ([&](auto &&) {
183
+ couldAcquireLock.store (true , std::memory_order_release);
184
+ });
185
+ });
186
+
187
+ EXPECT_EQ (2 , guardedObj.copy ().val );
188
+ while (!threadStarted.load (std::memory_order_acquire)) {
189
+ // busy wait
190
+ }
191
+ // wait generously for the thread to try to get the lock and do something
192
+ std::this_thread::sleep_for (std::chrono::milliseconds{1 });
193
+ EXPECT_TRUE (couldAcquireLock.load (std::memory_order_acquire));
194
+ thr.join ();
195
+ }
196
+
197
+ TYPED_TEST_P (GuardedDeathTest, test_guard_unlock_releases_value) {
198
+ auto guardedObj = typename TestFixture::template Guarded<UnderGuard>{1 };
199
+ EXPECT_EQ (1 , guardedObj.copy ().val );
200
+ auto guard = guardedObj.getLockedGuard ();
201
+ EXPECT_EQ (1 , guard.get ().val );
202
+ guard.get ().val = 2 ;
203
+ EXPECT_EQ (2 , guard.get ().val );
204
+ guard.unlock ();
205
+
206
+ ASSERT_DEATH_CORE_FREE ({ ASSERT_NE (2 , guard.get ().val ); }, " " );
207
+ }
208
+
163
209
TYPED_TEST_P (GuardedTest, test_do_allows_access) {
164
210
auto guardedObj = typename TestFixture::template Guarded<UnderGuard>{1 };
165
211
{
@@ -252,6 +298,32 @@ TYPED_TEST_P(GuardedTest, test_try_allows_access) {
252
298
}
253
299
}
254
300
301
+ TYPED_TEST_P (GuardedTest, test_try_guard_allows_access) {
302
+ auto guardedObj = typename TestFixture::template Guarded<UnderGuard>{1 };
303
+ {
304
+ // try is allowed to spuriously fail for no reason. But we expect it to
305
+ // succeed at some point when noone holds the lock.
306
+ bool didExecute = false ;
307
+ while (!didExecute) {
308
+ if (auto guard = guardedObj.tryLockedGuard ()) {
309
+ EXPECT_EQ (1 , guard->get ().val );
310
+ guard->get ().val = 2 ;
311
+ didExecute = true ;
312
+ EXPECT_EQ (2 , guard->get ().val );
313
+ }
314
+
315
+ {
316
+ auto guard = guardedObj.getLockedGuard ();
317
+ if (didExecute) {
318
+ EXPECT_EQ (guard->val , 2 );
319
+ } else {
320
+ EXPECT_EQ (guard->val , 1 );
321
+ }
322
+ }
323
+ }
324
+ }
325
+ }
326
+
255
327
TYPED_TEST_P (GuardedTest, test_try_fails_access) {
256
328
auto guardedObj = typename TestFixture::template Guarded<UnderGuard>{1 };
257
329
{
@@ -279,11 +351,40 @@ TYPED_TEST_P(GuardedTest, test_try_fails_access) {
279
351
}
280
352
}
281
353
282
- REGISTER_TYPED_TEST_CASE_P (GuardedTest, test_copy_allows_access, test_copy_waits_for_access,
283
- test_assign_allows_access, test_assign_waits_for_access,
284
- test_guard_allows_access, test_guard_waits_for_access,
354
+ TYPED_TEST_P (GuardedTest, test_try_guard_fails_access) {
355
+ auto guardedObj = typename TestFixture::template Guarded<UnderGuard>{1 };
356
+ {
357
+ auto guard = guardedObj.getLockedGuard ();
358
+ bool threadStarted = false ;
359
+ bool threadFinished = false ;
360
+ auto thr = std::thread ([&] {
361
+ threadStarted = true ;
362
+ bool didExecute = false ;
363
+ if (auto guard = guardedObj.tryLockedGuard ()) {
364
+ EXPECT_EQ (1 , guard->get ().val );
365
+ guard->get ().val = 2 ;
366
+ didExecute = true ;
367
+ EXPECT_EQ (2 , guard->get ().val );
368
+ }
369
+ EXPECT_FALSE (didExecute);
370
+ threadFinished = true ;
371
+ });
372
+ thr.join ();
373
+ EXPECT_TRUE (threadStarted);
374
+ EXPECT_TRUE (threadFinished);
375
+ EXPECT_EQ (guard->val , 1 );
376
+ }
377
+ }
378
+
379
+ REGISTER_TYPED_TEST_CASE_P (GuardedTest, test_copy_allows_access,
380
+ test_copy_waits_for_access, test_assign_allows_access,
381
+ test_assign_waits_for_access, test_guard_allows_access,
382
+ test_guard_waits_for_access, test_guard_unlock_releases_mutex,
285
383
test_do_allows_access, test_do_waits_for_access,
286
- test_try_allows_access, test_try_fails_access);
384
+ test_try_allows_access, test_try_guard_allows_access,
385
+ test_try_fails_access, test_try_guard_fails_access);
386
+
387
+ REGISTER_TYPED_TEST_CASE_P (GuardedDeathTest, test_guard_unlock_releases_value);
287
388
288
389
template <template <typename > typename T>
289
390
struct ParamT {
@@ -297,5 +398,6 @@ using TestedTypes =
297
398
std::pair<arangodb::Mutex, ParamT<std::unique_lock>>>;
298
399
299
400
INSTANTIATE_TYPED_TEST_CASE_P (GuardedTestInstantiation, GuardedTest, TestedTypes);
401
+ INSTANTIATE_TYPED_TEST_CASE_P (GuardedDeathTestInstantiation, GuardedDeathTest, TestedTypes);
300
402
301
403
} // namespace arangodb::tests
0 commit comments