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