1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <audio_utils/mutex.h>
18 #include <gtest/gtest.h>
19
20 #include <thread>
21
22 using namespace std::chrono_literals;
23
24 // Currently tests mutex priority-inheritance (or not) based on flag
25 // adb shell setprop \
26 // persist.device_config.aconfig_flags.media_audio.\
27 // com.android.media.audio.flags.mutex_priority_inheritance true
28 //
29 // TODO(b/209491695) enable both PI/non-PI mutex tests regardless of flag.
30
31 namespace android {
32
33 namespace audio_locks {
34
35 // Audio capabilities are typically assigned a partial order -
36 // but to furnish a proof of deadlock free audio, we create a global order
37 // (which isn't unique, just used to prove deadlock free access).
38
39 // Clang thread-safety can assigns capabilities to mutex-like objects.
40 // Here we use pointers to audio_utils::mutex*, they can be nullptr (never allocated)
41 // because they are used solely as a capability declaration, not as a mutex
42 // instance.
43 //
44 // In this example, we have 4 capabilities, labeled cap1, cap2, cap3, cap4.
45 // We'll show later in the IContainer.h example how we assign
46 // capabilities to actual mutexes.
47
48 // Add highest priority here
49 inline audio_utils::mutex* cap1;
50 inline audio_utils::mutex* cap2 ACQUIRED_AFTER(cap1);
51 inline audio_utils::mutex* cap3 ACQUIRED_AFTER(cap2);
52 inline audio_utils::mutex* cap4 ACQUIRED_AFTER(cap3);
53 // Add lowest priority here
54
55 // Now ACQUIRED_AFTER() isn't implemented (yet) in Clang Thread-Safety.
56 // As a solution, we define priority exclusion for lock acquisition.
57 // if you lock at a capability n, then you cannot hold
58 // locks at a lower capability (n+1, n+2, ...) otherwise
59 // there is lock inversion.
60
61 #define EXCLUDES_BELOW_4 // no capability below 4.
62 #define EXCLUDES_4 EXCLUDES(cap4) EXCLUDES_BELOW_4
63
64 #define EXCLUDES_BELOW_3 EXCLUDES_4
65 #define EXCLUDES_3 EXCLUDES(cap3) EXCLUDES_BELOW_3
66
67 #define EXCLUDES_BELOW_2 EXCLUDES_3
68 #define EXCLUDES_2 EXCLUDES(cap2) EXCLUDES_BELOW_2
69
70 #define EXCLUDES_BELOW_1 EXCLUDES_2
71 #define EXCLUDES_1 EXCLUDES(cap1) EXCLUDES_BELOW_1
72
73 #define EXCLUDES_ALL EXCLUDES_1
74
75 } // namespace audio_locks
76
77 using namespace audio_locks;
78 // -----------------------------------------
79
80 // Example IContainer.h
81 //
82 // This is a Container interface with multiple exposed mutexes.
83 // Since the capabilities file audio_locks.h is global,
84 // this interface can be used in projects outside of the implementing
85 // project.
86
87 // Here RETURN_CAPABILITY is used to assign a capability to a mutex.
88
89 class IContainer {
90 public:
91 virtual ~IContainer() = default;
92
93 // This is an example of returning many mutexes for test purposes.
94 // In AudioFlinger interfaces, we may return mutexes for locking
95 // AudioFlinger_Mutex, AudioFlinger_ClientMutex, ThreadBase_Mutex, etc. for interface methods.
96 //
97 // We do not allow access to the mutex when holding a mutex of lower priority, so
98 // we use EXCLUDES_....
99 virtual audio_utils::mutex& mutex1() const RETURN_CAPABILITY(cap1) EXCLUDES_BELOW_1 = 0;
100 virtual audio_utils::mutex& mutex2() const RETURN_CAPABILITY(cap2) EXCLUDES_BELOW_2 = 0;
101 virtual audio_utils::mutex& mutex3() const RETURN_CAPABILITY(cap3) EXCLUDES_BELOW_3 = 0;
102
103 // acquires capability 1, can't hold cap1 or lower level locks
104 virtual int value1() const EXCLUDES_1 = 0;
105
106 virtual int value1_l() const REQUIRES(cap1) = 0;
107 virtual int value2_l() const REQUIRES(cap2) = 0;
108 virtual int value3_l() const REQUIRES(cap3) = 0;
109
110 virtual int combo12_l() const REQUIRES(cap1) EXCLUDES_2 = 0;
111
112 // As value1, value2, value3 access requires cap1, cap2, cap3 individually,
113 // combo123_lll() requires all three.
114 virtual int combo123_lll() const REQUIRES(cap1, cap2, cap3) = 0;
115
116 // We cannot use any of mutex1, mutex2, mutex3, as they are acquired
117 // within the combo123() method.
118 // If we happen to know EXCLUDES_1 > EXCLUDES2 > EXCLUDES_3
119 // we could just use EXCLUDES_1. We include all 3 here.
120 virtual int combo123() const EXCLUDES_1 EXCLUDES_2 EXCLUDES_3 = 0;
121 };
122
123 // -----------------------------------------
124 //
125 // Example Container.cpp
126 //
127 // The container implemented the IContainer interface.
128 //
129 // We see here how the RETURN_CAPABILITY() is used to assign capability to mutexes.
130
131 class Container : public IContainer {
132 public:
133 // Here we implement the mutexes that has the global capabilities.
mutex1() const134 audio_utils::mutex& mutex1() const override RETURN_CAPABILITY(cap1) EXCLUDES_BELOW_1 {
135 return mMutex1;
136 }
137
mutex2() const138 audio_utils::mutex& mutex2() const override RETURN_CAPABILITY(cap2) EXCLUDES_BELOW_2 {
139 return mMutex2;
140 }
141
mutex3() const142 audio_utils::mutex& mutex3() const override RETURN_CAPABILITY(cap3) EXCLUDES_BELOW_3 {
143 return mMutex3;
144 }
145
146 // We use EXCLUDES_1 (which is prevents cap1 and all lesser priority mutexes)
147 // EXCLUDES(cap1) is not sufficient because if we acquire cap1,
148 // we ALSO can't hold lower level locks.
value1() const149 int value1() const override EXCLUDES_1 {
150
151 // Locking is through mutex1() to get the proper capability.
152 audio_utils::lock_guard l(mutex1());
153 return value1_l();
154 }
value1_l() const155 int value1_l() const override REQUIRES(cap1) {
156 return v1_;
157 }
value2_l() const158 int value2_l() const override REQUIRES(cap2) {
159 return v2_;
160 }
value3_l() const161 int value3_l() const override REQUIRES(cap3) {
162 return v3_;
163 }
164
165 // This requires cap1, acquires cap2.
combo12_l() const166 int combo12_l() const override REQUIRES(cap1) EXCLUDES_2 {
167
168 // Fails return value1_l() + value2_l() + value3_l();
169 audio_utils::lock_guard l(mutex2());
170 return value1_l() + value2_l();
171 }
172
173 #if 0
174 // fails!
175 int combo123_lll() REQUIRES(cap1, cap2, cap2) {
176 return value1_l() + value2_l() + value3_l();
177 }
178 #endif
179
180 // We can use REQUIRES(cap1, cap2, cap3)
181 // or REQUIRES(cap1, cap2, mutex3())
182 //
183 // The REQUIRES expression must be valid as the first line in the function body.
184 // This means that if the expression has visibility issues (accesses
185 // a private base class member from a derived class, etc.) it won't work.
186 // Quick test: Does a decltype(expr) work as the first line?
187 // If not, then REQUIRES(expr) won't either.
188 //
combo123_lll() const189 int combo123_lll() const override REQUIRES(cap1, cap2, mutex3()) {
190 return value1_l() + value2_l() + value3_l();
191 }
192
193 #if 1
194 // In this example, the 3 exclusions are needed because we acquire
195 // 3 locks. If we happen to know EXCLUDES_1 > EXCLUDES2 > EXCLUDES_3
196 // we could just use EXCLUDES_1. We include all 3 here.
combo123() const197 int combo123() const override EXCLUDES_1 EXCLUDES_2 EXCLUDES_3 {
198 audio_utils::lock_guard l1(mutex1());
199 audio_utils::lock_guard l2(mutex2());
200 audio_utils::lock_guard l3(mutex3());
201
202 return value1_l() + value2_l() + value3_l();
203 }
204
205 #else
206 // THIS FAILS
combo123() const207 int combo123() const override EXCLUDES_1 EXCLUDES_2 EXCLUDES_3 {
208 audio_utils::lock_guard l2(mutex2());
209 audio_utils::lock_guard l1(mutex1()); // 2 is acquired before 1.
210 audio_utils::lock_guard l3(mutex3());
211
212 return value1_l() + value2_l() + value3_l();
213 }
214 #endif
215
216 private:
217 // The actual implementation mutexes are never directly exposed.
218 mutable audio_utils::mutex mMutex1;
219 mutable audio_utils::mutex mMutex2;
220 mutable audio_utils::mutex mMutex3;
221
222 int v1_ GUARDED_BY(cap1) = 1;
223 int v2_ GUARDED_BY(mutex2()) = 2;
224 int v3_ GUARDED_BY(cap3) = 3;
225 };
226
TEST(audio_mutex_tests,Container)227 TEST(audio_mutex_tests, Container) {
228 Container c;
229
230 EXPECT_EQ(1, c.value1()); // success
231
232 #if 0
233 // fails
234 {
235 audio_utils::lock_guard(c.mutex1());
236 EXPECT_EQ(3, c.combo12_l());
237 }
238 #endif
239
240 {
241 audio_utils::lock_guard l(c.mutex1());
242 EXPECT_EQ(3, c.combo12_l()); // success
243 }
244 }
245
246 // Test access through the IContainer interface -
247 // see that mutex checking is done without knowledge of
248 // the actual implementation.
249
TEST(audio_mutex_tests,Interface)250 TEST(audio_mutex_tests, Interface) {
251 Container c;
252 IContainer *i = static_cast<IContainer*>(&c);
253
254 EXPECT_EQ(1, i->value1()); // success
255
256 #if 0
257 // fails
258 {
259 audio_utils::lock_guard(c.mutex1());
260 EXPECT_EQ(3, c.combo12_l());
261 }
262 #endif
263
264 {
265 audio_utils::lock_guard l(i->mutex1());
266 EXPECT_EQ(3, i->combo12_l()); // success
267 }
268
269 ALOGD("%s: %s", __func__, audio_utils::mutex::all_stats_to_string().c_str());
270 }
271
TEST(audio_mutex_tests,Stack)272 TEST(audio_mutex_tests, Stack) {
273 android::audio_utils::atomic_stack<int, int, 2> as;
274
275 // set up stack
276 EXPECT_EQ(0UL, as.size());
277 as.push(1, 10);
278 EXPECT_EQ(1UL, as.size());
279 as.push(2, 20);
280 EXPECT_EQ(2UL, as.size());
281
282 // 3rd item exceeds the stack capacity.
283 as.push(3, 30);
284 // 2 items tracked (subset)
285 EXPECT_EQ(2UL, as.size());
286 // 3 items total.
287 EXPECT_EQ(3UL, as.true_size());
288
289 const auto& bot = as.bottom();
290 const auto& top = as.top();
291
292 // these are the 2 items tracked:
293 EXPECT_EQ(1, bot.first.load());
294 EXPECT_EQ(10, bot.second.load());
295
296 EXPECT_EQ(3, top.first.load());
297 EXPECT_EQ(30, top.second.load());
298
299 // remove the bottom item.
300 EXPECT_EQ(true, as.remove(1));
301 EXPECT_EQ(1UL, as.size());
302 EXPECT_EQ(2UL, as.true_size());
303
304 // now remove the "virtual" item.
305 // (actually any non-existing item value works).
306 EXPECT_EQ(true, as.remove(2));
307 EXPECT_EQ(1UL, as.size());
308 EXPECT_EQ(1UL, as.true_size());
309
310 // now an invalid removal
311 EXPECT_EQ(false, as.remove(5));
312 EXPECT_EQ(1UL, as.size());
313 EXPECT_EQ(1UL, as.true_size());
314
315 // now remove the final item.
316 EXPECT_EQ(true, as.remove(3));
317 EXPECT_EQ(0UL, as.size());
318 EXPECT_EQ(0UL, as.true_size());
319 }
320
TEST(audio_mutex_tests,RecursiveLockDetection)321 TEST(audio_mutex_tests, RecursiveLockDetection) {
322 constexpr pid_t pid = 0; // avoid registry shutdown.
323 android::audio_utils::thread_mutex_info<int, int, 8 /* stack depth */> tmi(pid);
324
325 // set up stack
326 tmi.push_held(50, 1);
327 tmi.push_held(40, 2);
328 tmi.push_held(30, 3);
329 EXPECT_EQ(3UL, tmi.stack().size());
330
331 // remove bottom.
332 tmi.remove_held(50);
333 EXPECT_EQ(2UL, tmi.stack().size());
334
335 // test recursive lock detection.
336
337 // same order, same item is recursive.
338 const auto& recursive = tmi.check_held(30, 3);
339 EXPECT_EQ(30, recursive.first.load());
340 EXPECT_EQ(3, recursive.second.load());
341
342 // same order but different item (10 != 30) is OK.
343 const auto& nil = tmi.check_held(10, 3);
344 EXPECT_EQ(0, nil.first.load());
345 EXPECT_EQ(0, nil.second.load());
346 }
347
TEST(audio_mutex_tests,OrderDetection)348 TEST(audio_mutex_tests, OrderDetection) {
349 constexpr pid_t pid = 0; // avoid registry shutdown.
350 android::audio_utils::thread_mutex_info<int, int, 8 /* stack depth */> tmi(pid);
351
352 // set up stack
353 tmi.push_held(50, 1);
354 tmi.push_held(40, 2);
355 tmi.push_held(30, 3);
356 EXPECT_EQ(3UL, tmi.stack().size());
357
358 // remove middle
359 tmi.remove_held(40);
360 EXPECT_EQ(2UL, tmi.stack().size());
361
362 // test inversion detection.
363
364 // lower order is a problem 1 < 3.
365 const auto& inversion = tmi.check_held(1, 1);
366 EXPECT_EQ(30, inversion.first.load());
367 EXPECT_EQ(3, inversion.second.load());
368
369 // higher order is OK.
370 const auto& nil2 = tmi.check_held(4, 4);
371 EXPECT_EQ(0, nil2.first.load());
372 EXPECT_EQ(0, nil2.second.load());
373 }
374
375
TEST(audio_mutex_tests,StdTimedLock)376 TEST(audio_mutex_tests, StdTimedLock) {
377 using ConditionVariable = std::condition_variable;
378 using Mutex = std::mutex;
379 using UniqueLock = android::audio_utils::unique_lock<Mutex>;
380
381 Mutex m, m1;
382 ConditionVariable cv;
383 bool quit = false; // GUARDED_BY(m)
384
385 std::atomic<pid_t> tid1{};
386
387 // launch thread.
388 std::thread t1([&]() {
389 UniqueLock ul1(m1);
390 UniqueLock ul(m);
391 tid1 = android::audio_utils::gettid_wrapper();
392 while (!quit) {
393 cv.wait(ul, [&]{ return quit; });
394 if (quit) break;
395 }
396 });
397
398 // ensure thread tid1 has acquired all locks.
399 while (tid1 == 0) { usleep(1000); }
400
401 // try lock for 500ms.
402 // (don't make this too large otherwise the successful timeout will take > 500ms).
403 constexpr int64_t kTimeoutNs = 500'000'000;
404 {
405 // verify timed out state.
406 const int64_t beginNs = systemTime();
407 const bool success = audio_utils::std_mutex_timed_lock(m1, kTimeoutNs);
408 const int64_t endNs = systemTime();
409 const int64_t diffNs = endNs - beginNs;
410
411 if (success) m1.unlock();
412 EXPECT_GT(diffNs, kTimeoutNs);
413 EXPECT_FALSE(success);
414 }
415
416 // exit the thread
417 {
418 UniqueLock ul(m);
419
420 quit = true;
421 cv.notify_one();
422 }
423
424 t1.join();
425
426 {
427 // verify success state.
428 const int64_t beginNs = systemTime();
429 const bool success = audio_utils::std_mutex_timed_lock(m1, kTimeoutNs);
430 const int64_t endNs = systemTime();
431 const int64_t diffNs = endNs - beginNs;
432
433 if (success) m1.unlock();
434
435 // we're expecting to lock within 250ms (should be efficient).
436 constexpr int64_t kSuccessLockNs = 250'000'000;
437 EXPECT_LT(diffNs, kSuccessLockNs);
438 EXPECT_TRUE(success);
439 }
440 }
441 // The following tests are evaluated for the android::audio_utils::mutex
442 // Non-Priority Inheritance and Priority Inheritance cases.
443
444 class MutexTestSuite : public testing::TestWithParam<bool> {};
445
446 // Test that the mutex aborts on recursion (if the abort flag is set).
447
TEST_P(MutexTestSuite,FatalRecursiveMutex)448 TEST_P(MutexTestSuite, FatalRecursiveMutex)
449 NO_THREAD_SAFETY_ANALYSIS {
450 if (!android::audio_utils::AudioMutexAttributes::abort_on_recursion_check_
451 || !audio_utils::mutex_get_enable_flag()) {
452 ALOGD("Test FatalRecursiveMutex skipped");
453 return;
454 }
455
456 using Mutex = android::audio_utils::mutex;
457 using LockGuard = android::audio_utils::lock_guard;
458 const bool priority_inheritance = GetParam();
459
460 Mutex m(priority_inheritance);
461 LockGuard lg(m);
462
463 // Can't lock ourselves again.
464 ASSERT_DEATH(m.lock(), ".*recursive mutex.*");
465 }
466
467 // Test that the mutex aborts on lock order inversion (if the abort flag is set).
468
TEST_P(MutexTestSuite,FatalLockOrder)469 TEST_P(MutexTestSuite, FatalLockOrder)
470 NO_THREAD_SAFETY_ANALYSIS {
471 if (!android::audio_utils::AudioMutexAttributes::abort_on_order_check_
472 || !audio_utils::mutex_get_enable_flag()) {
473 ALOGD("Test FatalLockOrder skipped");
474 return;
475 }
476
477 using Mutex = android::audio_utils::mutex;
478 using LockGuard = android::audio_utils::lock_guard;
479 const bool priority_inheritance = GetParam();
480
481 Mutex m1{priority_inheritance, (android::audio_utils::AudioMutexAttributes::order_t)1};
482 Mutex m2{priority_inheritance, (android::audio_utils::AudioMutexAttributes::order_t)2};
483
484 LockGuard lg2(m2);
485 // m1 must be locked before m2 as 1 < 2.
486 ASSERT_DEATH(m1.lock(), ".*mutex order.*");
487 }
488
489 // Test that the mutex aborts on lock order inversion (if the abort flag is set).
490
TEST_P(MutexTestSuite,UnexpectedUnlock)491 TEST_P(MutexTestSuite, UnexpectedUnlock)
492 NO_THREAD_SAFETY_ANALYSIS {
493 if (!android::audio_utils::AudioMutexAttributes::abort_on_invalid_unlock_
494 || !audio_utils::mutex_get_enable_flag()) {
495 ALOGD("Test UnexpectedUnlock skipped");
496 return;
497 }
498
499 using Mutex = android::audio_utils::mutex;
500 const bool priority_inheritance = GetParam();
501
502 Mutex m1{priority_inheritance, (android::audio_utils::AudioMutexAttributes::order_t)1};
503 ASSERT_DEATH(m1.unlock(), ".*mutex unlock.*");
504 }
505
TEST_P(MutexTestSuite,TimedLock)506 TEST_P(MutexTestSuite, TimedLock) {
507 using ConditionVariable = android::audio_utils::condition_variable;
508 using Mutex = android::audio_utils::mutex;
509 using UniqueLock = android::audio_utils::unique_lock<Mutex>;
510 const bool priority_inheritance = GetParam();
511
512 Mutex m{priority_inheritance}, m1{priority_inheritance};
513 ConditionVariable cv;
514 bool quit = false; // GUARDED_BY(m)
515
516 std::atomic<pid_t> tid1{};
517
518 // launch thread.
519 std::thread t1([&]() {
520 UniqueLock ul1(m1);
521 UniqueLock ul(m);
522 tid1 = android::audio_utils::gettid_wrapper();
523 while (!quit) {
524 cv.wait(ul, [&]{ return quit; });
525 if (quit) break;
526 }
527 });
528
529 // ensure thread tid1 has acquired all locks.
530 while (tid1 == 0) { usleep(1000); }
531
532 // try lock for 1s
533 constexpr int64_t kTimeoutNs = 1'000'000'000;
534 {
535 // verify timed out state.
536 const int64_t beginNs = systemTime();
537 const bool success = m1.try_lock(kTimeoutNs);
538 const int64_t endNs = systemTime();
539 const int64_t diffNs = endNs - beginNs;
540
541 if (success) m1.unlock();
542 EXPECT_GT(diffNs, kTimeoutNs);
543 EXPECT_FALSE(success);
544 }
545
546 // exit the thread
547 {
548 UniqueLock ul(m);
549
550 quit = true;
551 cv.notify_one();
552 }
553
554 t1.join();
555
556 {
557 // verify success state.
558 const int64_t beginNs = systemTime();
559 const bool success = m1.try_lock(kTimeoutNs);
560 const int64_t endNs = systemTime();
561 const int64_t diffNs = endNs - beginNs;
562
563 if (success) m1.unlock();
564 constexpr int64_t kSuccessLockNs = kTimeoutNs / 4;
565 EXPECT_LT(diffNs, kSuccessLockNs);
566 EXPECT_TRUE(success);
567 }
568 }
569
570 // Test the deadlock detection algorithm for a single wait chain
571 // (no cycle).
572
TEST_P(MutexTestSuite,DeadlockDetection)573 TEST_P(MutexTestSuite, DeadlockDetection) {
574 using Mutex = android::audio_utils::mutex;
575 using UniqueLock = android::audio_utils::unique_lock<Mutex>;
576 using ConditionVariable = android::audio_utils::condition_variable;
577 const bool priority_inheritance = GetParam();
578
579 // order checked below.
580 constexpr size_t kOrder1 = 1;
581 constexpr size_t kOrder2 = 2;
582 constexpr size_t kOrder3 = 3;
583 static_assert(Mutex::attributes_t::order_size_ > kOrder3);
584
585 Mutex m1{priority_inheritance, static_cast<Mutex::attributes_t::order_t>(kOrder1)};
586 Mutex m2{priority_inheritance, static_cast<Mutex::attributes_t::order_t>(kOrder2)};
587 Mutex m3{priority_inheritance, static_cast<Mutex::attributes_t::order_t>(kOrder3)};
588 Mutex m4{priority_inheritance};
589 Mutex m{priority_inheritance};
590 ConditionVariable cv;
591 bool quit = false; // GUARDED_BY(m)
592 std::atomic<pid_t> tid1{}, tid2{}, tid3{}, tid4{};
593
594 std::thread t4([&]() {
595 UniqueLock ul4(m4);
596 UniqueLock ul(m);
597 tid4 = android::audio_utils::gettid_wrapper();
598 while (!quit) {
599 cv.wait(ul, [&]{ return quit; });
600 if (quit) break;
601 }
602 });
603
604 while (tid4 == 0) { usleep(1000); }
605
606 std::thread t3([&]() {
607 UniqueLock ul3(m3);
608 tid3 = android::audio_utils::gettid_wrapper();
609 UniqueLock ul4(m4);
610 });
611
612 while (tid3 == 0) { usleep(1000); }
613
614 std::thread t2([&]() {
615 UniqueLock ul2(m2);
616 tid2 = android::audio_utils::gettid_wrapper();
617 UniqueLock ul3(m3);
618 });
619
620 while (tid2 == 0) { usleep(1000); }
621
622 std::thread t1([&]() {
623 UniqueLock ul1(m1);
624 tid1 = android::audio_utils::gettid_wrapper();
625 UniqueLock ul2(m2);
626 });
627
628 while (tid1 == 0) { usleep(1000); }
629
630 // we know that the threads will now block in the proper order.
631 // however, we need to wait for the block to happen.
632 // this part is racy unless we check the thread state or use
633 // futexes directly in our mutex (which allows atomic accuracy of wait).
634 usleep(20000);
635
636 const auto deadlockInfo = android::audio_utils::mutex::deadlock_detection(tid1);
637
638 // no cycle.
639 EXPECT_EQ(false, deadlockInfo.has_cycle);
640
641 // no condition_variable detected
642 EXPECT_EQ(audio_utils::other_wait_reason_t::none, deadlockInfo.other_wait_reason);
643
644 // thread1 is waiting on a chain of 3 other threads.
645 const auto chain = deadlockInfo.chain;
646 const size_t chain_size = chain.size();
647 EXPECT_EQ(3u, chain_size);
648
649 const auto default_idx = static_cast<size_t>(Mutex::attributes_t::order_default_);
650 if (chain_size > 0) {
651 EXPECT_EQ(tid2, chain[0].first);
652 EXPECT_EQ(Mutex::attributes_t::order_names_[kOrder2], chain[0].second);
653 }
654 if (chain_size > 1) {
655 EXPECT_EQ(tid3, chain[1].first);
656 EXPECT_EQ(Mutex::attributes_t::order_names_[kOrder3], chain[1].second);
657 }
658 if (chain_size > 2) {
659 EXPECT_EQ(tid4, chain[2].first);
660 EXPECT_EQ(Mutex::attributes_t::order_names_[default_idx], chain[2].second);
661 }
662
663 ALOGD("%s", android::audio_utils::mutex::all_threads_to_string().c_str());
664
665 {
666 UniqueLock ul(m);
667
668 quit = true;
669 cv.notify_one();
670 }
671
672 t4.join();
673 t3.join();
674 t2.join();
675 t1.join();
676 }
677
TEST_P(MutexTestSuite,DeadlockConditionVariableDetection)678 TEST_P(MutexTestSuite, DeadlockConditionVariableDetection) {
679 using Mutex = android::audio_utils::mutex;
680 using UniqueLock = android::audio_utils::unique_lock<Mutex>;
681 using ConditionVariable = android::audio_utils::condition_variable;
682 const bool priority_inheritance = GetParam();
683
684 // order checked below.
685 constexpr size_t kOrder1 = 1;
686 constexpr size_t kOrder2 = 2;
687 constexpr size_t kOrder3 = 3;
688 static_assert(Mutex::attributes_t::order_size_ > kOrder3);
689
690 Mutex m1{priority_inheritance, static_cast<Mutex::attributes_t::order_t>(kOrder1)};
691 Mutex m2{priority_inheritance, static_cast<Mutex::attributes_t::order_t>(kOrder2)};
692 Mutex m3{priority_inheritance, static_cast<Mutex::attributes_t::order_t>(kOrder3)};
693 Mutex m4{priority_inheritance};
694 Mutex m{priority_inheritance};
695 ConditionVariable cv, cv2, cv4;
696 bool quit = false; // GUARDED_BY(m)
697 std::atomic<pid_t> tid1{}, tid2{}, tid3{}, tid4{};
698
699 std::thread t4([&]() {
700 // UniqueLock ul4(m4);
701 UniqueLock ul(m);
702 tid4 = android::audio_utils::gettid_wrapper();
703 while (!quit) {
704 cv.wait(ul, [&]{ return quit; });
705 if (quit) break;
706 }
707 });
708
709 while (tid4 == 0) { usleep(1000); }
710
711 std::thread t3([&]() {
712 UniqueLock ul3(m3);
713 tid3 = android::audio_utils::gettid_wrapper();
714 UniqueLock ul4(m4);
715 while (!quit) {
716 // use the wait method with the notifier_tid metadata.
717 cv4.wait(ul4, [&]{ return quit; }, tid4);
718 if (quit) break;
719 }
720 });
721
722 while (tid3 == 0) { usleep(1000); }
723
724 std::thread t2([&]() {
725 // UniqueLock ul2(m2);
726 tid2 = android::audio_utils::gettid_wrapper();
727 UniqueLock ul3(m3);
728 });
729
730 while (tid2 == 0) { usleep(1000); }
731
732 std::thread t1([&]() {
733 UniqueLock ul1(m1);
734 tid1 = android::audio_utils::gettid_wrapper();
735 UniqueLock ul2(m2);
736 while (!quit) {
737 // use the wait method with the notifier_tid metadata.
738 cv2.wait(ul2, [&]{ return quit; }, tid2);
739 if (quit) break;
740 }
741 });
742
743 while (tid1 == 0) { usleep(1000); }
744
745 // we know that the threads will now block in the proper order.
746 // however, we need to wait for the block to happen.
747 // this part is racy unless we check the thread state or use
748 // futexes directly in our mutex (which allows atomic accuracy of wait).
749 usleep(20000);
750
751 const auto deadlockInfo = android::audio_utils::mutex::deadlock_detection(tid1);
752
753 // no cycle.
754 EXPECT_EQ(false, deadlockInfo.has_cycle);
755
756 // condition_variable detected
757 EXPECT_EQ(audio_utils::other_wait_reason_t::cv, deadlockInfo.other_wait_reason);
758
759 // thread1 is waiting on a chain of 3 other threads.
760 const auto chain = deadlockInfo.chain;
761 const size_t chain_size = chain.size();
762 EXPECT_EQ(3u, chain_size);
763
764 const auto default_idx = static_cast<size_t>(Mutex::attributes_t::order_default_);
765 if (chain_size > 0) {
766 EXPECT_EQ(tid2, chain[0].first);
767 EXPECT_EQ(std::string("cv-").append(Mutex::attributes_t::order_names_[kOrder2]).c_str(),
768 chain[0].second);
769 }
770 if (chain_size > 1) {
771 EXPECT_EQ(tid3, chain[1].first);
772 EXPECT_EQ(Mutex::attributes_t::order_names_[kOrder3], chain[1].second);
773 }
774 if (chain_size > 2) {
775 EXPECT_EQ(tid4, chain[2].first);
776 EXPECT_EQ(std::string("cv-").append(
777 Mutex::attributes_t::order_names_[default_idx]).c_str(), chain[2].second);
778 }
779
780 ALOGD("with cv all_threads_to_string():\n%s",
781 android::audio_utils::mutex::all_threads_to_string().c_str());
782 ALOGD("with cv deadlock_info_t:\n%s",
783 android::audio_utils::mutex::deadlock_detection(
784 tid1).to_string().c_str());
785
786 {
787 UniqueLock ul(m);
788 quit = true;
789 cv.notify_one();
790 }
791 {
792 UniqueLock ul2(m);
793 cv2.notify_one();
794 }
795 {
796 UniqueLock ul4(m);
797 cv4.notify_one();
798 }
799
800 t4.join();
801 t3.join();
802 t2.join();
803 t1.join();
804 }
805
TEST_P(MutexTestSuite,DeadlockJoinDetection)806 TEST_P(MutexTestSuite, DeadlockJoinDetection) {
807 using Mutex = android::audio_utils::mutex;
808 using UniqueLock = android::audio_utils::unique_lock<Mutex>;
809 using ConditionVariable = android::audio_utils::condition_variable;
810 const bool priority_inheritance = GetParam();
811
812 Mutex m{priority_inheritance};
813 ConditionVariable cv;
814 bool quit = false; // GUARDED_BY(m)
815 std::atomic<pid_t> tid1{}, tid2{}, tid3{}, tid4{};
816
817 std::thread t4([&]() {
818 UniqueLock ul(m);
819 tid4 = android::audio_utils::gettid_wrapper();
820 while (!quit) {
821 cv.wait(ul, [&]{ return quit; });
822 if (quit) break;
823 }
824 });
825
826 while (tid4 == 0) { std::this_thread::sleep_for(1ms); }
827
828 std::thread t3([&]() {
829 tid3 = android::audio_utils::gettid_wrapper();
830 audio_utils::mutex::scoped_join_wait_check sjw(tid4);
831 t4.join();
832 });
833
834 while (tid3 == 0) { std::this_thread::sleep_for(1ms); }
835
836 std::thread t2([&]() {
837 tid2 = android::audio_utils::gettid_wrapper();
838 audio_utils::mutex::scoped_join_wait_check sjw(tid3);
839 t3.join();
840 });
841
842 while (tid2 == 0) { std::this_thread::sleep_for(1ms); }
843
844 std::thread t1([&]() {
845 tid1 = android::audio_utils::gettid_wrapper();
846 audio_utils::mutex::scoped_join_wait_check sjw(tid2);
847 t2.join();
848 });
849
850 while (tid1 == 0) { std::this_thread::sleep_for(1ms); }
851
852 // we know that the threads will now block in the proper order.
853 // however, we need to wait for the block to happen.
854 // this part is racy unless we check the thread state or use
855 // futexes directly in our mutex (which allows atomic accuracy of wait).
856 std::this_thread::sleep_for(20ms);
857
858 const auto deadlockInfo = android::audio_utils::mutex::deadlock_detection(tid1);
859
860 // no cycle.
861 EXPECT_EQ(false, deadlockInfo.has_cycle);
862
863 // thread join detected
864 EXPECT_EQ(audio_utils::other_wait_reason_t::join, deadlockInfo.other_wait_reason);
865
866 // thread1 is attempting to join, in a chain of 3 other threads.
867 const auto chain = deadlockInfo.chain;
868 const size_t chain_size = chain.size();
869 EXPECT_EQ(3u, chain_size);
870
871 const auto default_idx = static_cast<size_t>(Mutex::attributes_t::order_default_);
872 if (chain_size > 0) {
873 EXPECT_EQ(tid2, chain[0].first);
874 EXPECT_EQ("join", chain[0].second);
875 }
876 if (chain_size > 1) {
877 EXPECT_EQ(tid3, chain[1].first);
878 EXPECT_EQ("join", chain[1].second);
879 }
880 if (chain_size > 2) {
881 EXPECT_EQ(tid4, chain[2].first);
882 EXPECT_EQ("join", chain[2].second);
883 }
884
885 ALOGD("with join all_threads_to_string():\n%s",
886 android::audio_utils::mutex::all_threads_to_string().c_str());
887 ALOGD("with join deadlock_info_t:\n%s",
888 android::audio_utils::mutex::deadlock_detection(
889 tid1).to_string().c_str());
890
891 {
892 UniqueLock ul(m);
893 quit = true;
894 cv.notify_one();
895 }
896 t1.join();
897 }
898
899 INSTANTIATE_TEST_SUITE_P(Mutex_NonPI_and_PI, MutexTestSuite, testing::Values(false, true));
900
901 } // namespace android
902