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 "gwp_asan/tests/harness.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 | |
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 | |
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 | |
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 | |
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 | |
86 | TEST(GwpAsanMutexTest, SynchronisedCounterTest) { |
87 | runSynchronisedTest(NumThreads: 4, CounterMax: 1000000); |
88 | runSynchronisedTest(NumThreads: 100, CounterMax: 1000000); |
89 | } |
90 | |