Thanks to visit codestin.com
Credit goes to chromium.googlesource.com

blob: 6941079184c5801832212ed523db1874c18d4d9d [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2012 The Chromium Authors
[email protected]5dd879a2010-07-30 05:50:422// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]44106182012-04-06 03:53:025#include "base/synchronization/lock.h"
6
Peter Kastinge7fb7942024-12-06 22:43:057#include <stdint.h>
[email protected]5dd879a2010-07-30 05:50:428
Anand Ravi9d185cd682025-03-27 16:41:529#include <algorithm>
10#include <atomic>
11#include <cmath>
12#include <cstdint>
Peter Kastinge7fb7942024-12-06 22:43:0513#include <memory>
14#include <utility>
François Dorayefe18b92024-05-31 21:44:5915
Anand Ravi06e44f92025-05-28 20:15:0316#include "base/android/background_thread_pool_field_trial.h"
Peter Kastinge7fb7942024-12-06 22:43:0517#include "base/check.h"
[email protected]44106182012-04-06 03:53:0218#include "base/compiler_specific.h"
François Dorayefe18b92024-05-31 21:44:5919#include "base/dcheck_is_on.h"
Anand Ravi9d185cd682025-03-27 16:41:5220#include "base/features.h"
21#include "base/functional/bind.h"
22#include "base/functional/callback.h"
23#include "base/functional/callback_helpers.h"
Peter Kastinge7fb7942024-12-06 22:43:0524#include "base/functional/function_ref.h"
25#include "base/location.h"
Keishi Hattori0e45c022021-11-27 09:25:5226#include "base/memory/raw_ptr.h"
Peter Kastinge7fb7942024-12-06 22:43:0527#include "base/memory/raw_ref.h"
Anand Ravi9d185cd682025-03-27 16:41:5228#include "base/profiler/thread_delegate.h"
Peter Kastinge7fb7942024-12-06 22:43:0529#include "base/rand_util.h"
Anand Ravi9d185cd682025-03-27 16:41:5230#include "base/synchronization/lock_impl.h"
François Dorayefe18b92024-05-31 21:44:5931#include "base/synchronization/lock_subtle.h"
Anand Ravi9d185cd682025-03-27 16:41:5232#include "base/system/sys_info.h"
33#include "base/test/bind.h"
Gabriel Charetted773f122019-03-19 22:06:3834#include "base/test/gtest_util.h"
Anand Ravi9d185cd682025-03-27 16:41:5235#include "base/test/scoped_feature_list.h"
Peter Kastinge7fb7942024-12-06 22:43:0536#include "base/thread_annotations.h"
[email protected]ce072a72010-12-31 20:02:1637#include "base/threading/platform_thread.h"
Anand Ravi9d185cd682025-03-27 16:41:5238#include "base/time/time.h"
39#include "base/timer/elapsed_timer.h"
François Dorayefe18b92024-05-31 21:44:5940#include "testing/gmock/include/gmock/gmock.h"
[email protected]5dd879a2010-07-30 05:50:4241#include "testing/gtest/include/gtest/gtest.h"
42
François Dorayefe18b92024-05-31 21:44:5943using testing::UnorderedElementsAre;
44using testing::UnorderedElementsAreArray;
45
[email protected]bc581a682011-01-01 23:16:2046namespace base {
[email protected]5dd879a2010-07-30 05:50:4247
48// Basic test to make sure that Acquire()/Release()/Try() don't crash ----------
49
50class BasicLockTestThread : public PlatformThread::Delegate {
51 public:
Peter Kasting811504a72025-01-09 03:18:5052 explicit BasicLockTestThread(Lock* lock) : lock_(lock) {}
[email protected]5dd879a2010-07-30 05:50:4253
Peter Boström75cd3c02021-09-28 15:23:1854 BasicLockTestThread(const BasicLockTestThread&) = delete;
55 BasicLockTestThread& operator=(const BasicLockTestThread&) = delete;
56
dcheng56488182014-10-21 10:54:5157 void ThreadMain() override {
[email protected]5dd879a2010-07-30 05:50:4258 for (int i = 0; i < 10; i++) {
59 lock_->Acquire();
60 acquired_++;
61 lock_->Release();
62 }
63 for (int i = 0; i < 10; i++) {
64 lock_->Acquire();
65 acquired_++;
Peter Kastinge7fb7942024-12-06 22:43:0566 PlatformThread::Sleep(RandTimeDeltaUpTo(Milliseconds(20)));
[email protected]5dd879a2010-07-30 05:50:4267 lock_->Release();
68 }
69 for (int i = 0; i < 10; i++) {
70 if (lock_->Try()) {
71 acquired_++;
Peter Kastinge7fb7942024-12-06 22:43:0572 PlatformThread::Sleep(RandTimeDeltaUpTo(Milliseconds(20)));
[email protected]5dd879a2010-07-30 05:50:4273 lock_->Release();
74 }
75 }
76 }
77
78 int acquired() const { return acquired_; }
79
80 private:
Keishi Hattori0e45c022021-11-27 09:25:5281 raw_ptr<Lock> lock_;
Peter Kasting811504a72025-01-09 03:18:5082 int acquired_ = 0;
[email protected]5dd879a2010-07-30 05:50:4283};
84
[email protected]bc581a682011-01-01 23:16:2085TEST(LockTest, Basic) {
[email protected]5dd879a2010-07-30 05:50:4286 Lock lock;
87 BasicLockTestThread thread(&lock);
[email protected]54e6ff82013-05-22 00:01:3888 PlatformThreadHandle handle;
[email protected]5dd879a2010-07-30 05:50:4289
90 ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
91
92 int acquired = 0;
93 for (int i = 0; i < 5; i++) {
94 lock.Acquire();
95 acquired++;
96 lock.Release();
97 }
98 for (int i = 0; i < 10; i++) {
99 lock.Acquire();
100 acquired++;
Peter Kastinge7fb7942024-12-06 22:43:05101 PlatformThread::Sleep(RandTimeDeltaUpTo(Milliseconds(20)));
[email protected]5dd879a2010-07-30 05:50:42102 lock.Release();
103 }
104 for (int i = 0; i < 10; i++) {
105 if (lock.Try()) {
106 acquired++;
Peter Kastinge7fb7942024-12-06 22:43:05107 PlatformThread::Sleep(RandTimeDeltaUpTo(Milliseconds(20)));
[email protected]5dd879a2010-07-30 05:50:42108 lock.Release();
109 }
110 }
111 for (int i = 0; i < 5; i++) {
112 lock.Acquire();
113 acquired++;
Peter Kastinge7fb7942024-12-06 22:43:05114 PlatformThread::Sleep(RandTimeDeltaUpTo(Milliseconds(20)));
[email protected]5dd879a2010-07-30 05:50:42115 lock.Release();
116 }
117
118 PlatformThread::Join(handle);
119
120 EXPECT_GE(acquired, 20);
121 EXPECT_GE(thread.acquired(), 20);
122}
123
124// Test that Try() works as expected -------------------------------------------
125
126class TryLockTestThread : public PlatformThread::Delegate {
127 public:
Peter Kasting811504a72025-01-09 03:18:50128 explicit TryLockTestThread(Lock* lock) : lock_(lock) {}
[email protected]5dd879a2010-07-30 05:50:42129
Peter Boström75cd3c02021-09-28 15:23:18130 TryLockTestThread(const TryLockTestThread&) = delete;
131 TryLockTestThread& operator=(const TryLockTestThread&) = delete;
132
dcheng56488182014-10-21 10:54:51133 void ThreadMain() override {
Benoît Lizéa38dbf5d2020-03-11 08:33:58134 // The local variable is required for the static analyzer to see that the
135 // lock is properly released.
136 bool got_lock = lock_->Try();
137 got_lock_ = got_lock;
Peter Kasting134ef9af2024-12-28 02:30:09138 if (got_lock) {
[email protected]5dd879a2010-07-30 05:50:42139 lock_->Release();
Peter Kasting134ef9af2024-12-28 02:30:09140 }
[email protected]5dd879a2010-07-30 05:50:42141 }
142
143 bool got_lock() const { return got_lock_; }
144
145 private:
Keishi Hattori0e45c022021-11-27 09:25:52146 raw_ptr<Lock> lock_;
Peter Kasting811504a72025-01-09 03:18:50147 bool got_lock_ = false;
[email protected]5dd879a2010-07-30 05:50:42148};
149
[email protected]bc581a682011-01-01 23:16:20150TEST(LockTest, TryLock) {
[email protected]5dd879a2010-07-30 05:50:42151 Lock lock;
152
153 ASSERT_TRUE(lock.Try());
Benoît Lizéa38dbf5d2020-03-11 08:33:58154 lock.AssertAcquired();
[email protected]5dd879a2010-07-30 05:50:42155
156 // This thread will not be able to get the lock.
157 {
158 TryLockTestThread thread(&lock);
[email protected]54e6ff82013-05-22 00:01:38159 PlatformThreadHandle handle;
[email protected]5dd879a2010-07-30 05:50:42160
161 ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
162
163 PlatformThread::Join(handle);
164
165 ASSERT_FALSE(thread.got_lock());
166 }
167
168 lock.Release();
169
170 // This thread will....
171 {
172 TryLockTestThread thread(&lock);
[email protected]54e6ff82013-05-22 00:01:38173 PlatformThreadHandle handle;
[email protected]5dd879a2010-07-30 05:50:42174
175 ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
176
177 PlatformThread::Join(handle);
178
179 ASSERT_TRUE(thread.got_lock());
180 // But it released it....
181 ASSERT_TRUE(lock.Try());
Benoît Lizéa38dbf5d2020-03-11 08:33:58182 lock.AssertAcquired();
[email protected]5dd879a2010-07-30 05:50:42183 }
184
185 lock.Release();
186}
187
188// Tests that locks actually exclude -------------------------------------------
189
190class MutexLockTestThread : public PlatformThread::Delegate {
191 public:
192 MutexLockTestThread(Lock* lock, int* value) : lock_(lock), value_(value) {}
193
Peter Boström75cd3c02021-09-28 15:23:18194 MutexLockTestThread(const MutexLockTestThread&) = delete;
195 MutexLockTestThread& operator=(const MutexLockTestThread&) = delete;
196
[email protected]5dd879a2010-07-30 05:50:42197 // Static helper which can also be called from the main thread.
198 static void DoStuff(Lock* lock, int* value) {
199 for (int i = 0; i < 40; i++) {
200 lock->Acquire();
201 int v = *value;
Peter Kastinge7fb7942024-12-06 22:43:05202 PlatformThread::Sleep(RandTimeDeltaUpTo(Milliseconds(10)));
[email protected]5dd879a2010-07-30 05:50:42203 *value = v + 1;
204 lock->Release();
205 }
206 }
207
dcheng56488182014-10-21 10:54:51208 void ThreadMain() override { DoStuff(lock_, value_); }
[email protected]5dd879a2010-07-30 05:50:42209
210 private:
Keishi Hattori0e45c022021-11-27 09:25:52211 raw_ptr<Lock> lock_;
212 raw_ptr<int> value_;
[email protected]5dd879a2010-07-30 05:50:42213};
214
[email protected]bc581a682011-01-01 23:16:20215TEST(LockTest, MutexTwoThreads) {
[email protected]5dd879a2010-07-30 05:50:42216 Lock lock;
217 int value = 0;
218
219 MutexLockTestThread thread(&lock, &value);
[email protected]54e6ff82013-05-22 00:01:38220 PlatformThreadHandle handle;
[email protected]5dd879a2010-07-30 05:50:42221
222 ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
223
224 MutexLockTestThread::DoStuff(&lock, &value);
225
226 PlatformThread::Join(handle);
227
228 EXPECT_EQ(2 * 40, value);
229}
230
[email protected]bc581a682011-01-01 23:16:20231TEST(LockTest, MutexFourThreads) {
[email protected]5dd879a2010-07-30 05:50:42232 Lock lock;
233 int value = 0;
234
235 MutexLockTestThread thread1(&lock, &value);
236 MutexLockTestThread thread2(&lock, &value);
237 MutexLockTestThread thread3(&lock, &value);
[email protected]54e6ff82013-05-22 00:01:38238 PlatformThreadHandle handle1;
239 PlatformThreadHandle handle2;
240 PlatformThreadHandle handle3;
[email protected]5dd879a2010-07-30 05:50:42241
242 ASSERT_TRUE(PlatformThread::Create(0, &thread1, &handle1));
243 ASSERT_TRUE(PlatformThread::Create(0, &thread2, &handle2));
244 ASSERT_TRUE(PlatformThread::Create(0, &thread3, &handle3));
245
246 MutexLockTestThread::DoStuff(&lock, &value);
247
248 PlatformThread::Join(handle1);
249 PlatformThread::Join(handle2);
250 PlatformThread::Join(handle3);
251
252 EXPECT_EQ(4 * 40, value);
253}
[email protected]bc581a682011-01-01 23:16:20254
Peter Kastinge7fb7942024-12-06 22:43:05255// Test invariant checking -----------------------------------------------------
256
257TEST(LockTest, InvariantIsCalled) {
258 // This test should compile and execute safely regardless of invariant
259 // checking, but if `kInvariantsActive` is false, we don't expect the
260 // invariant to be checked when the lock state changes.
261 constexpr bool kInvariantsActive = DCHECK_IS_ON();
262
263 class InvariantChecker {
264 public:
265 explicit InvariantChecker(const Lock& lock LIFETIME_BOUND) : lock(lock) {}
266 void Check() ASSERT_EXCLUSIVE_LOCK(lock) {
267 lock->AssertAcquired();
268 invariant_called = true;
269 }
270 bool TestAndReset() { return std::exchange(invariant_called, false); }
271
272 private:
273 const raw_ref<const Lock> lock;
274 bool invariant_called = false;
275 };
276
277 // Awkward construction order here allows `checker` to refer to `lock`, which
278 // refers to `check_ref`, which refers to `check`, which refers to `checker`.
279 std::unique_ptr<InvariantChecker> checker;
280 auto check = [&] { checker->Check(); };
281 auto check_ref = base::FunctionRef<void()>(check);
Hans Wennborg2b66b032025-03-11 13:00:04282 Lock lock([&](Lock* lp) {
283 checker = std::make_unique<InvariantChecker>(*lp);
Peter Kastinge7fb7942024-12-06 22:43:05284 return check_ref;
Hans Wennborg2b66b032025-03-11 13:00:04285 }(&lock));
Peter Kastinge7fb7942024-12-06 22:43:05286
287 EXPECT_FALSE(checker->TestAndReset());
288
289 lock.Acquire();
290 EXPECT_EQ(kInvariantsActive, checker->TestAndReset());
291
292 lock.Release();
293 EXPECT_EQ(kInvariantsActive, checker->TestAndReset());
294}
295
296// AutoLock tests --------------------------------------------------------------
297
Gabriel Charetted773f122019-03-19 22:06:38298TEST(LockTest, AutoLockMaybe) {
299 Lock lock;
300 {
301 AutoLockMaybe auto_lock(&lock);
302 lock.AssertAcquired();
303 }
304 EXPECT_DCHECK_DEATH(lock.AssertAcquired());
305}
306
307TEST(LockTest, AutoLockMaybeNull) {
308 AutoLockMaybe auto_lock(nullptr);
309}
310
311TEST(LockTest, ReleasableAutoLockExplicitRelease) {
312 Lock lock;
313 ReleasableAutoLock auto_lock(&lock);
314 lock.AssertAcquired();
315 auto_lock.Release();
316 EXPECT_DCHECK_DEATH(lock.AssertAcquired());
317}
318
319TEST(LockTest, ReleasableAutoLockImplicitRelease) {
320 Lock lock;
321 {
322 ReleasableAutoLock auto_lock(&lock);
323 lock.AssertAcquired();
324 }
325 EXPECT_DCHECK_DEATH(lock.AssertAcquired());
326}
327
Dan McArdlef97ab602024-05-16 15:44:59328class TryLockTest : public testing::Test {
329 protected:
330 Lock lock_;
331 int x_ GUARDED_BY(lock_) = 0;
332};
333
334// Verifies thread safety annotations do not prevent correct `AutoTryLock` usage
335// from compiling. A dual of this test exists in lock_nocompile.nc. For more
336// context, see <https://crbug.com/340196356>.
337TEST_F(TryLockTest, CorrectlyCheckIsAcquired) {
338 AutoTryLock maybe(lock_);
339 // Should compile because we correctly check whether the lock is acquired
340 // before writing to `x_`.
341 if (maybe.is_acquired()) {
342 x_ = 5;
343 }
344}
345
François Dorayefe18b92024-05-31 21:44:59346#if DCHECK_IS_ON()
347
François Doray6671c402024-06-21 17:47:39348TEST(LockTest, GetTrackedLocksHeldByCurrentThread) {
François Dorayefe18b92024-05-31 21:44:59349 Lock lock_a;
350 Lock lock_b;
351 Lock lock_c;
352 const uintptr_t lock_a_ptr = reinterpret_cast<uintptr_t>(&lock_a);
353 const uintptr_t lock_b_ptr = reinterpret_cast<uintptr_t>(&lock_b);
354 const uintptr_t lock_c_ptr = reinterpret_cast<uintptr_t>(&lock_c);
355
François Doray6671c402024-06-21 17:47:39356 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
357 UnorderedElementsAre());
358 ReleasableAutoLock auto_lock_a(&lock_a, subtle::LockTracking::kEnabled);
359 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
François Dorayefe18b92024-05-31 21:44:59360 UnorderedElementsAre(lock_a_ptr));
François Doray6671c402024-06-21 17:47:39361 ReleasableAutoLock auto_lock_b(&lock_b, subtle::LockTracking::kEnabled);
362 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
François Dorayefe18b92024-05-31 21:44:59363 UnorderedElementsAre(lock_a_ptr, lock_b_ptr));
364 auto_lock_a.Release();
François Doray6671c402024-06-21 17:47:39365 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
François Dorayefe18b92024-05-31 21:44:59366 UnorderedElementsAre(lock_b_ptr));
François Doray6671c402024-06-21 17:47:39367 ReleasableAutoLock auto_lock_c(&lock_c, subtle::LockTracking::kEnabled);
368 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
François Dorayefe18b92024-05-31 21:44:59369 UnorderedElementsAre(lock_b_ptr, lock_c_ptr));
370 auto_lock_c.Release();
François Doray6671c402024-06-21 17:47:39371 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
François Dorayefe18b92024-05-31 21:44:59372 UnorderedElementsAre(lock_b_ptr));
373 auto_lock_b.Release();
François Doray6671c402024-06-21 17:47:39374 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
375 UnorderedElementsAre());
François Dorayefe18b92024-05-31 21:44:59376}
377
François Doray6671c402024-06-21 17:47:39378TEST(LockTest, GetTrackedLocksHeldByCurrentThread_AutoLock) {
379 Lock lock;
380 const uintptr_t lock_ptr = reinterpret_cast<uintptr_t>(&lock);
381 AutoLock auto_lock(lock, subtle::LockTracking::kEnabled);
382 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
383 UnorderedElementsAre(lock_ptr));
384}
385
386TEST(LockTest, GetTrackedLocksHeldByCurrentThread_MovableAutoLock) {
387 Lock lock;
388 const uintptr_t lock_ptr = reinterpret_cast<uintptr_t>(&lock);
389 MovableAutoLock auto_lock(lock, subtle::LockTracking::kEnabled);
390 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
391 UnorderedElementsAre(lock_ptr));
392}
393
394TEST(LockTest, GetTrackedLocksHeldByCurrentThread_AutoTryLock) {
395 Lock lock;
396 const uintptr_t lock_ptr = reinterpret_cast<uintptr_t>(&lock);
397 AutoTryLock auto_lock(lock, subtle::LockTracking::kEnabled);
398 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
399 UnorderedElementsAre(lock_ptr));
400}
401
402TEST(LockTest, GetTrackedLocksHeldByCurrentThread_AutoLockMaybe) {
403 Lock lock;
404 const uintptr_t lock_ptr = reinterpret_cast<uintptr_t>(&lock);
405 AutoLockMaybe auto_lock(&lock, subtle::LockTracking::kEnabled);
406 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
407 UnorderedElementsAre(lock_ptr));
408}
409
410TEST(LockTest, GetTrackedLocksHeldByCurrentThreadOverCapacity)
François Dorayefe18b92024-05-31 21:44:59411// Thread-safety analysis doesn't handle the array of locks properly.
412NO_THREAD_SAFETY_ANALYSIS {
413 constexpr size_t kHeldLocksCapacity = 10;
414 std::array<Lock, kHeldLocksCapacity + 1> locks;
415
416 for (size_t i = 0; i < kHeldLocksCapacity; ++i) {
François Doray6671c402024-06-21 17:47:39417 locks[i].Acquire(subtle::LockTracking::kEnabled);
François Dorayefe18b92024-05-31 21:44:59418 }
419
Peter Boström59a246e82025-03-17 03:55:33420 EXPECT_CHECK_DEATH({
François Doray6671c402024-06-21 17:47:39421 locks[kHeldLocksCapacity].Acquire(subtle::LockTracking::kEnabled);
François Dorayefe18b92024-05-31 21:44:59422 locks[kHeldLocksCapacity].Release();
423 });
424
425 for (size_t i = 0; i < kHeldLocksCapacity; ++i) {
426 locks[i].Release();
427
428 std::vector<uintptr_t> expected_locks;
429 for (size_t j = i + 1; j < kHeldLocksCapacity; ++j) {
430 expected_locks.push_back(reinterpret_cast<uintptr_t>(&locks[j]));
431 }
432
François Doray6671c402024-06-21 17:47:39433 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
François Dorayefe18b92024-05-31 21:44:59434 UnorderedElementsAreArray(expected_locks));
435 }
436}
437
François Doray6671c402024-06-21 17:47:39438TEST(LockTest, TrackingDisabled) {
François Dorayefe18b92024-05-31 21:44:59439 Lock lock;
François Doray6671c402024-06-21 17:47:39440 AutoLock auto_lock(lock, subtle::LockTracking::kDisabled);
441 EXPECT_TRUE(subtle::GetTrackedLocksHeldByCurrentThread().empty());
François Dorayefe18b92024-05-31 21:44:59442}
443
Anand Ravi9d185cd682025-03-27 16:41:52444// Priority Inheritance Tests --------------------------------------------------
445
446#if BUILDFLAG(ENABLE_MUTEX_PRIORITY_INHERITANCE)
447namespace {
448class PriorityInheritanceTest {
449 public:
450 // The average value of MeasureRunTime() over |num_samples| iterations.
451 static TimeDelta MeasureAverageRunTime(int num_samples = 10) {
452 TimeDelta total_runtime;
453 for (int i = 0; i < num_samples; i++) {
454 total_runtime += MeasureRunTime();
455 }
456
457 return total_runtime / num_samples;
458 }
459
460 // Measure the time taken for a low-priority thread (kBackground) to perform
461 // CPU bound work when it holds a lock that is awaited by a high-priority
462 // thread (kRealtimeAudio).
463 static TimeDelta MeasureRunTime() {
464 Lock lock;
465 TimeDelta test_run_time;
466 std::atomic<bool> signal_cpu_bound_worker_threads_shutdown{false},
467 signal_thread_a_will_lock{false};
468
469 // Keep all the cores busy with a workload of CPU bound thread to reduce
470 // flakiness in the test by skewing the CPU time between the high-priority
471 // and low-priority measurement threads.
472 std::vector<TestThread> cpu_bound_worker_threads;
473 for (int i = 0; i < 15; i++) {
474 cpu_bound_worker_threads.emplace_back(
475 ThreadType::kDefault, base::BindLambdaForTesting([&]() {
476 while (!signal_cpu_bound_worker_threads_shutdown.load(
477 std::memory_order_relaxed)) {
478 BusyLoop(10);
479 }
480 }));
481 }
482
483 for (auto& worker_thread : cpu_bound_worker_threads) {
484 worker_thread.Create();
485 }
486
487 TestThread thread_a(
488 ThreadType::kRealtimeAudio, base::BindLambdaForTesting([&]() {
489 // Signal to thread B that the current thread will acquire the lock
490 // next, so that it can to start its CPU bound work.
491 signal_thread_a_will_lock.store(true, std::memory_order_relaxed);
492
493 // Wait on the lock to be released once the low-priority thread is
494 // done. In the case when priority inheritance mutexes are enabled,
495 // this should boost the priority of the low-priority thread to the
496 // priority of the highest priority waiter (i.e. the current thread).
497 AutoLock auto_lock(lock);
498 BusyLoop(10);
499 }));
500
501 TestThread thread_b(
502 ThreadType::kBackground, base::BindLambdaForTesting([&]() {
503 // Acquire the lock before creating the high-priority thread, so that
504 // the higher priority thread is blocked on the current thread while
505 // the current thread performs CPU-bound work.
506 AutoLock auto_lock(lock);
507 thread_a.Create();
508
509 // Before performing the CPU bound work, wait for the thread A to
510 // signal that it has started running and will acquire the lock next.
511 // While it is not a perfectly reliable signal (thread A may get
512 // descheduled immediately after signalling), given the relative
513 // priorities of the two threads it is good enough to reduce large
514 // variations due to latencies in thread bring up.
515 while (!signal_thread_a_will_lock.load(std::memory_order_relaxed)) {
516 usleep(10);
517 }
518
519 ElapsedTimer timer;
520 BusyLoop(1000000);
521 test_run_time = timer.Elapsed();
522 }));
523
524 // Create the low-priority thread which is responsible for creating the
525 // high-priority thread. Wait for both threads to finish before recording
526 // the elapsed time.
527 thread_b.Create();
528 thread_b.Join();
529 thread_a.Join();
530
531 signal_cpu_bound_worker_threads_shutdown.store(true,
532 std::memory_order_relaxed);
533 for (auto& worker_thread : cpu_bound_worker_threads) {
534 worker_thread.Join();
535 }
536
537 return test_run_time;
538 }
539
540 private:
541 // CPU bound work for the threads to eat up CPU cycles.
542 static void BusyLoop(size_t n) {
543 __unused int sum = 0;
544 for (int i = 0; i < n; i++) {
545 if (base::ShouldRecordSubsampledMetric(0.5)) {
546 sum += 1;
547 }
548 }
549 }
550
551 class TestThread : public PlatformThread::Delegate {
552 public:
553 explicit TestThread(ThreadType thread_type, base::OnceClosure body)
554 : thread_type_(thread_type), body_(std::move(body)) {}
555
556 void Create() {
557 ASSERT_TRUE(
558 PlatformThread::CreateWithType(0, this, &handle_, thread_type_));
559 }
560
561 void ThreadMain() override { std::move(body_).Run(); }
562
563 void Join() { PlatformThread::Join(handle_); }
564
565 private:
566 ThreadType thread_type_;
567 PlatformThreadHandle handle_;
568 base::OnceClosure body_;
569 };
570};
571
Anand Ravi9d185cd682025-03-27 16:41:52572} // namespace
573
574// Tests that the time taken by a higher-priority thread to acquire a lock held
575// by a lower-priority thread is indeed reduced by priority inheritance.
576TEST(LockTest, PriorityIsInherited) {
577 TimeDelta avg_test_run_time_with_pi, avg_test_run_time_without_pi;
578
Anand Ravi06e44f92025-05-28 20:15:03579 // Priority inheritance mutexes are not supported on Android kernels < 6.1
580 if (!base::KernelSupportsPriorityInheritanceFutex()) {
581 GTEST_SKIP() << "base::Lock does not handle multiple thread priorities "
582 << "(Kernel version: "
583 << base::SysInfo::KernelVersionNumber::Current() << ")";
584 }
585
Anand Ravi9d185cd682025-03-27 16:41:52586 {
Anand Ravi06e44f92025-05-28 20:15:03587 base::android::ScopedUsePriorityInheritanceLocksForTesting use_pi_locks;
588 ASSERT_TRUE(base::android::BackgroundThreadPoolFieldTrial::
589 ShouldUsePriorityInheritanceLocks());
Anand Ravi9d185cd682025-03-27 16:41:52590 avg_test_run_time_with_pi =
591 PriorityInheritanceTest::MeasureAverageRunTime();
592 }
593
594 {
Anand Ravi06e44f92025-05-28 20:15:03595 ASSERT_TRUE(!base::android::BackgroundThreadPoolFieldTrial::
596 ShouldUsePriorityInheritanceLocks());
Anand Ravi9d185cd682025-03-27 16:41:52597 avg_test_run_time_without_pi =
598 PriorityInheritanceTest::MeasureAverageRunTime();
599 }
600
601 // During the time in which the thread A is waiting on the lock to be released
602 // by the thread B, the thread B runs at kBackground priority in the non-PI
603 // case and at kRealtimeAudio priority in the PI case.
604 //
605 // Based on the Linux kernel's allocation of CPU shares documented in
606 // https://elixir.bootlin.com/linux/v6.12.5/source/kernel/sched/core.c#L9998,
607 // a thread running at kRealtimeAudio (nice value = -16) gets 36291 shares
608 // of the CPU, a thread at kDefault (nice value = 0) get 1024 shares and a
609 // thread at kBackground (nice value = 10) gets 110 shares of the CPU.
610 //
611 // Assuming no other threads except the ones created by this test are running,
612 // during the time in which thread A is waiting on the lock to be released by
613 // thread B, thread B gets 110/(15*1024 + 110) ≈ 0.7% of the CPU time in the
614 // non-PI case and 36291/(36291 + 15*1024) ≈ 70% of the CPU time in the PI
615 // case. This is approximately a 100x difference in CPU shares allocated to
616 // the thread B when it is doing CPU-bound work.
617 //
618 // The test is thus designed such that the measured run time is thread B's CPU
619 // bound work. While there are other factors at play that determine the
620 // measured run time such as the frequency at which the CPU is running, we can
621 // expect that there will be at least an order of magnitude of disparity in
622 // the test run times with and without PI.
623 //
624 // In order to reduce test flakiness while still eliminating the possibility
625 // of variance in measurements accounting for the test results, we
626 // conservatively expect a 3x improvement.
627 EXPECT_GT(avg_test_run_time_without_pi, 3 * avg_test_run_time_with_pi);
628}
629#endif // BUILDFLAG(ENABLE_MUTEX_PRIORITY_INHERITANCE)
630
François Dorayefe18b92024-05-31 21:44:59631#endif // DCHECK_IS_ON()
632
[email protected]bc581a682011-01-01 23:16:20633} // namespace base