1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "partition_alloc/partition_lock.h"
6
7 #include "build/build_config.h"
8 #include "partition_alloc/partition_alloc_base/debug/debugging_buildflags.h"
9 #include "partition_alloc/partition_alloc_base/thread_annotations.h"
10 #include "partition_alloc/partition_alloc_base/threading/platform_thread_for_testing.h"
11 #include "partition_alloc/partition_alloc_base/time/time.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13
14 namespace partition_alloc::internal {
15
TEST(PartitionAllocLockTest,Simple)16 TEST(PartitionAllocLockTest, Simple) {
17 Lock lock;
18 lock.Acquire();
19 lock.Release();
20 }
21
22 namespace {
23
24 Lock g_lock;
25
26 } // namespace
27
TEST(PartitionAllocLockTest,StaticLockStartsUnlocked)28 TEST(PartitionAllocLockTest, StaticLockStartsUnlocked) {
29 g_lock.Acquire();
30 g_lock.Release();
31 }
32
33 namespace {
34
35 class ThreadDelegateForContended
36 : public base::PlatformThreadForTesting::Delegate {
37 public:
ThreadDelegateForContended(Lock & start_lock,Lock & lock,int iterations,int & counter)38 explicit ThreadDelegateForContended(Lock& start_lock,
39 Lock& lock,
40 int iterations,
41 int& counter)
42 : start_lock_(start_lock),
43 lock_(lock),
44 iterations_(iterations),
45 counter_(counter) {}
46
ThreadMain()47 void ThreadMain() override {
48 start_lock_.Acquire();
49 start_lock_.Release();
50
51 for (int i = 0; i < iterations_; i++) {
52 lock_.Acquire();
53 ++counter_;
54 lock_.Release();
55 }
56 }
57
58 private:
59 Lock& start_lock_;
60 Lock& lock_;
61 const int iterations_;
62 int& counter_;
63 };
64
65 } // namespace
66
TEST(PartitionAllocLockTest,Contended)67 TEST(PartitionAllocLockTest, Contended) {
68 int counter = 0; // *Not* atomic.
69 std::vector<internal::base::PlatformThreadHandle> thread_handles;
70 constexpr int iterations_per_thread = 1000000;
71 constexpr int num_threads = 4;
72
73 Lock lock;
74 Lock start_lock;
75
76 ThreadDelegateForContended delegate(start_lock, lock, iterations_per_thread,
77 counter);
78
79 start_lock.Acquire(); // Make sure that the threads compete, by waiting until
80 // all of them have at least been created.
81 for (int i = 0; i < num_threads; ++i) {
82 base::PlatformThreadHandle handle;
83 base::PlatformThreadForTesting::Create(0, &delegate, &handle);
84 thread_handles.push_back(handle);
85 }
86
87 start_lock.Release();
88
89 for (int i = 0; i < num_threads; ++i) {
90 base::PlatformThreadForTesting::Join(thread_handles[i]);
91 }
92 EXPECT_EQ(iterations_per_thread * num_threads, counter);
93 }
94
95 namespace {
96
97 class ThreadDelegateForSlowThreads
98 : public base::PlatformThreadForTesting::Delegate {
99 public:
ThreadDelegateForSlowThreads(Lock & start_lock,Lock & lock,int iterations,int & counter)100 explicit ThreadDelegateForSlowThreads(Lock& start_lock,
101 Lock& lock,
102 int iterations,
103 int& counter)
104 : start_lock_(start_lock),
105 lock_(lock),
106 iterations_(iterations),
107 counter_(counter) {}
108
ThreadMain()109 void ThreadMain() override {
110 start_lock_.Acquire();
111 start_lock_.Release();
112
113 for (int i = 0; i < iterations_; i++) {
114 lock_.Acquire();
115 ++counter_;
116 // Hold the lock for a while, to force futex()-based locks to sleep.
117 base::PlatformThread::Sleep(base::Milliseconds(1));
118 lock_.Release();
119 }
120 }
121
122 private:
123 Lock& start_lock_;
124 Lock& lock_;
125 const int iterations_;
126 int& counter_;
127 };
128
129 } // namespace
130
TEST(PartitionAllocLockTest,SlowThreads)131 TEST(PartitionAllocLockTest, SlowThreads) {
132 int counter = 0; // *Not* atomic.
133 std::vector<base::PlatformThreadHandle> thread_handles;
134 constexpr int iterations_per_thread = 100;
135 constexpr int num_threads = 4;
136
137 Lock lock;
138 Lock start_lock;
139
140 ThreadDelegateForSlowThreads delegate(start_lock, lock, iterations_per_thread,
141 counter);
142
143 start_lock.Acquire(); // Make sure that the threads compete, by waiting until
144 // all of them have at least been created.
145 for (int i = 0; i < num_threads; i++) {
146 base::PlatformThreadHandle handle;
147 base::PlatformThreadForTesting::Create(0, &delegate, &handle);
148 thread_handles.push_back(handle);
149 }
150
151 start_lock.Release();
152
153 for (int i = 0; i < num_threads; i++) {
154 base::PlatformThreadForTesting::Join(thread_handles[i]);
155 }
156 EXPECT_EQ(iterations_per_thread * num_threads, counter);
157 }
158
TEST(PartitionAllocLockTest,AssertAcquired)159 TEST(PartitionAllocLockTest, AssertAcquired) {
160 Lock lock;
161 lock.Acquire();
162 lock.AssertAcquired();
163 lock.Release();
164 }
165
166 // AssertAcquired() is only enforced with DCHECK()s.
167 // DCHECKs don't work with EXPECT_DEATH on official builds.
168 #if defined(GTEST_HAS_DEATH_TEST) && BUILDFLAG(PA_DCHECK_IS_ON) && \
169 (!defined(OFFICIAL_BUILD) || !defined(NDEBUG))
170
TEST(PartitionAllocLockTest,AssertAcquiredDeathTest)171 TEST(PartitionAllocLockTest, AssertAcquiredDeathTest) {
172 Lock lock;
173 EXPECT_DEATH(lock.AssertAcquired(), "");
174 }
175
176 namespace {
177
178 class ThreadDelegateForAssertAcquiredAnotherThreadHoldsTheLock
179 : public base::PlatformThreadForTesting::Delegate {
180 public:
ThreadDelegateForAssertAcquiredAnotherThreadHoldsTheLock(Lock & lock)181 explicit ThreadDelegateForAssertAcquiredAnotherThreadHoldsTheLock(Lock& lock)
182 : lock_(lock) {}
183
ThreadMain()184 void ThreadMain() PA_NO_THREAD_SAFETY_ANALYSIS override { lock_.Acquire(); }
185
186 private:
187 Lock& lock_;
188 };
189
190 } // namespace
191
TEST(PartitionAllocLockTest,AssertAcquiredAnotherThreadHoldsTheLock)192 TEST(PartitionAllocLockTest, AssertAcquiredAnotherThreadHoldsTheLock) {
193 Lock lock;
194 // PA_NO_THREAD_SAFETY_ANALYSIS: The checker rightfully points out that the
195 // lock is still held at the end of the function, which is what we want here.
196 ThreadDelegateForAssertAcquiredAnotherThreadHoldsTheLock delegate(lock);
197 base::PlatformThreadHandle handle;
198 base::PlatformThreadForTesting::Create(0, &delegate, &handle);
199 // Join before the test, otherwise some platforms' gtest have trouble with
200 // EXPECT_DEATH() and multiple live threads.
201 base::PlatformThreadForTesting::Join(handle);
202
203 // DCHECKs don't work with EXPECT_DEATH on official builds.
204 #if BUILDFLAG(PA_DCHECK_IS_ON) && (!defined(OFFICIAL_BUILD) || !defined(NDEBUG))
205 EXPECT_DEATH(lock.AssertAcquired(), "");
206 #endif
207 }
208
209 #if BUILDFLAG(IS_APPLE)
210
211 namespace {
212
213 class ThreadDelegateForReinitInOtherThread
214 : public base::PlatformThreadForTesting::Delegate {
215 public:
ThreadDelegateForReinitInOtherThread(Lock & lock)216 explicit ThreadDelegateForReinitInOtherThread(Lock& lock) : lock_(lock) {}
217
ThreadMain()218 void ThreadMain() PA_NO_THREAD_SAFETY_ANALYSIS override {
219 lock_.Reinit();
220 lock_.Acquire();
221 lock_.Release();
222 }
223
224 private:
225 Lock& lock_;
226 };
227
228 } // namespace
229
230 // On Apple OSes, it is not allowed to unlock a lock from another thread, so
231 // we need to re-initialize it.
TEST(PartitionAllocLockTest,ReinitInOtherThread)232 TEST(PartitionAllocLockTest, ReinitInOtherThread) PA_NO_THREAD_SAFETY_ANALYSIS {
233 Lock lock;
234 lock.Acquire();
235
236 ThreadDelegateForReinitInOtherThread delegate(lock);
237 base::PlatformThreadHandle handle;
238 base::PlatformThreadForTesting::Create(0, &delegate, &handle);
239 base::PlatformThreadForTesting::Join(handle);
240 }
241 #endif // BUILDFLAG(IS_APPLE)
242
243 #endif // defined(GTEST_HAS_DEATH_TEST) && BUILDFLAG(PA_DCHECK_IS_ON)
244
245 } // namespace partition_alloc::internal
246