1 //===-- mutex_test.cpp ------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "gwp_asan/mutex.h"
10 #include "gtest/gtest.h"
11 
12 #include <atomic>
13 #include <mutex>
14 #include <thread>
15 #include <vector>
16 
17 using gwp_asan::Mutex;
18 using gwp_asan::ScopedLock;
19 
TEST(GwpAsanMutexTest,LockUnlockTest)20 TEST(GwpAsanMutexTest, LockUnlockTest) {
21   Mutex Mu;
22 
23   ASSERT_TRUE(Mu.tryLock());
24   ASSERT_FALSE(Mu.tryLock());
25   Mu.unlock();
26 
27   Mu.lock();
28   Mu.unlock();
29 
30   // Ensure that the mutex actually unlocked.
31   ASSERT_TRUE(Mu.tryLock());
32   Mu.unlock();
33 }
34 
TEST(GwpAsanMutexTest,ScopedLockUnlockTest)35 TEST(GwpAsanMutexTest, ScopedLockUnlockTest) {
36   Mutex Mu;
37   { ScopedLock L(Mu); }
38   // Locking will fail here if the scoped lock failed to unlock.
39   EXPECT_TRUE(Mu.tryLock());
40   Mu.unlock();
41 
42   {
43     ScopedLock L(Mu);
44     EXPECT_FALSE(Mu.tryLock()); // Check that the c'tor did lock.
45 
46     // Manually unlock and check that this succeeds.
47     Mu.unlock();
48     EXPECT_TRUE(Mu.tryLock()); // Manually lock.
49   }
50   EXPECT_TRUE(Mu.tryLock()); // Assert that the scoped destructor did unlock.
51   Mu.unlock();
52 }
53 
synchronousIncrementTask(std::atomic<bool> * StartingGun,Mutex * Mu,unsigned * Counter,unsigned NumIterations)54 static void synchronousIncrementTask(std::atomic<bool> *StartingGun, Mutex *Mu,
55                                      unsigned *Counter,
56                                      unsigned NumIterations) {
57   while (!StartingGun) {
58     // Wait for starting gun.
59   }
60   for (unsigned i = 0; i < NumIterations; ++i) {
61     ScopedLock L(*Mu);
62     (*Counter)++;
63   }
64 }
65 
runSynchronisedTest(unsigned NumThreads,unsigned CounterMax)66 static void runSynchronisedTest(unsigned NumThreads, unsigned CounterMax) {
67   std::vector<std::thread> Threads;
68 
69   ASSERT_TRUE(CounterMax % NumThreads == 0);
70 
71   std::atomic<bool> StartingGun{false};
72   Mutex Mu;
73   unsigned Counter = 0;
74 
75   for (unsigned i = 0; i < NumThreads; ++i)
76     Threads.emplace_back(synchronousIncrementTask, &StartingGun, &Mu, &Counter,
77                          CounterMax / NumThreads);
78 
79   StartingGun = true;
80   for (auto &T : Threads)
81     T.join();
82 
83   EXPECT_EQ(CounterMax, Counter);
84 }
85 
TEST(GwpAsanMutexTest,SynchronisedCounterTest)86 TEST(GwpAsanMutexTest, SynchronisedCounterTest) {
87   runSynchronisedTest(4, 1000000);
88   runSynchronisedTest(100, 1000000);
89 }
90