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 "tests/scudo_unit_test.h" |
10 | |
11 | #include "mutex.h" |
12 | |
13 | #include <pthread.h> |
14 | #include <string.h> |
15 | |
16 | class TestData { |
17 | public: |
18 | explicit TestData(scudo::HybridMutex &M) : Mutex(M) { |
19 | for (scudo::u32 I = 0; I < Size; I++) |
20 | Data[I] = 0; |
21 | } |
22 | |
23 | void write() { |
24 | scudo::ScopedLock L(Mutex); |
25 | T V0 = Data[0]; |
26 | for (scudo::u32 I = 0; I < Size; I++) { |
27 | EXPECT_EQ(Data[I], V0); |
28 | Data[I]++; |
29 | } |
30 | } |
31 | |
32 | void tryWrite() { |
33 | if (!Mutex.tryLock()) |
34 | return; |
35 | T V0 = Data[0]; |
36 | for (scudo::u32 I = 0; I < Size; I++) { |
37 | EXPECT_EQ(Data[I], V0); |
38 | Data[I]++; |
39 | } |
40 | Mutex.unlock(); |
41 | } |
42 | |
43 | void backoff() { |
44 | volatile T LocalData[Size] = {}; |
45 | for (scudo::u32 I = 0; I < Size; I++) { |
46 | LocalData[I] = LocalData[I] + 1; |
47 | EXPECT_EQ(LocalData[I], 1U); |
48 | } |
49 | } |
50 | |
51 | private: |
52 | static const scudo::u32 Size = 64U; |
53 | typedef scudo::u64 T; |
54 | scudo::HybridMutex &Mutex; |
55 | alignas(SCUDO_CACHE_LINE_SIZE) T Data[Size]; |
56 | }; |
57 | |
58 | const scudo::u32 NumberOfThreads = 8; |
59 | #if SCUDO_DEBUG |
60 | const scudo::u32 NumberOfIterations = 4 * 1024; |
61 | #else |
62 | const scudo::u32 NumberOfIterations = 16 * 1024; |
63 | #endif |
64 | |
65 | static void *lockThread(void *Param) { |
66 | TestData *Data = reinterpret_cast<TestData *>(Param); |
67 | for (scudo::u32 I = 0; I < NumberOfIterations; I++) { |
68 | Data->write(); |
69 | Data->backoff(); |
70 | } |
71 | return 0; |
72 | } |
73 | |
74 | static void *tryThread(void *Param) { |
75 | TestData *Data = reinterpret_cast<TestData *>(Param); |
76 | for (scudo::u32 I = 0; I < NumberOfIterations; I++) { |
77 | Data->tryWrite(); |
78 | Data->backoff(); |
79 | } |
80 | return 0; |
81 | } |
82 | |
83 | TEST(ScudoMutexTest, Mutex) { |
84 | scudo::HybridMutex M; |
85 | TestData Data(M); |
86 | pthread_t Threads[NumberOfThreads]; |
87 | for (scudo::u32 I = 0; I < NumberOfThreads; I++) |
88 | pthread_create(newthread: &Threads[I], attr: 0, start_routine: lockThread, arg: &Data); |
89 | for (scudo::u32 I = 0; I < NumberOfThreads; I++) |
90 | pthread_join(th: Threads[I], thread_return: 0); |
91 | } |
92 | |
93 | TEST(ScudoMutexTest, MutexTry) { |
94 | scudo::HybridMutex M; |
95 | TestData Data(M); |
96 | pthread_t Threads[NumberOfThreads]; |
97 | for (scudo::u32 I = 0; I < NumberOfThreads; I++) |
98 | pthread_create(newthread: &Threads[I], attr: 0, start_routine: tryThread, arg: &Data); |
99 | for (scudo::u32 I = 0; I < NumberOfThreads; I++) |
100 | pthread_join(th: Threads[I], thread_return: 0); |
101 | } |
102 | |
103 | TEST(ScudoMutexTest, MutexAssertHeld) { |
104 | scudo::HybridMutex M; |
105 | M.lock(); |
106 | M.assertHeld(); |
107 | M.unlock(); |
108 | } |
109 | |