diff options
author | Andy Hung <hunga@google.com> | 2024-01-22 17:25:25 -0800 |
---|---|---|
committer | Andy Hung <hunga@google.com> | 2024-03-11 15:06:15 -0700 |
commit | 08902efe629b02511f0923625b3850420da76a3e (patch) | |
tree | 709e4740a86f0101746090146ef9089dc070fed8 | |
parent | 21d7c296eb7381bbbd65489d96844486341f07b6 (diff) | |
download | media-08902efe629b02511f0923625b3850420da76a3e.tar.gz |
audio mutex: Track waiting through condition_variable
Add tid metadata when we know the notification thread
of a condition_variable wait.
Test: atest audio_mutex_benchmark
Test: atest audio_mutex_tests
Bug: 322424558
Merged-In: I6e04529c7be7da16819dc5b9ab046b9416e1bb9a
Change-Id: I6e04529c7be7da16819dc5b9ab046b9416e1bb9a
-rw-r--r-- | audio_utils/benchmarks/audio_mutex_benchmark.cpp | 156 | ||||
-rw-r--r-- | audio_utils/include/audio_utils/mutex.h | 116 | ||||
-rw-r--r-- | audio_utils/tests/audio_mutex_tests.cpp | 128 |
3 files changed, 292 insertions, 108 deletions
diff --git a/audio_utils/benchmarks/audio_mutex_benchmark.cpp b/audio_utils/benchmarks/audio_mutex_benchmark.cpp index 216e8a99..b366700c 100644 --- a/audio_utils/benchmarks/audio_mutex_benchmark.cpp +++ b/audio_utils/benchmarks/audio_mutex_benchmark.cpp @@ -34,84 +34,84 @@ $ atest audio_mutex_benchmark Benchmark Time CPU Iteration audio_mutex_benchmark: - #BM_atomic_add_equals<int32_t> 6.490418995069907 ns 6.4717376481357896 ns 108158957 - #BM_atomic_add_to_seq_cst<int16_t> 6.5491215252883315 ns 6.528080020397028 ns 107223462 - #BM_atomic_add_to_seq_cst<int32_t> 6.6043085052910895 ns 6.58277029519354 ns 106339895 - #BM_atomic_add_to_seq_cst<int64_t> 6.547130657683545 ns 6.527710835284538 ns 107241300 - #BM_atomic_add_to_seq_cst<float> 7.886748664549371 ns 7.8608841749954355 ns 89038641 - #BM_atomic_add_to_seq_cst<double> 7.935562180977917 ns 7.910317678915859 ns 88467659 - #BM_atomic_add_to_relaxed<int16_t> 5.144169800499881 ns 5.127361036326905 ns 137048029 - #BM_atomic_add_to_relaxed<int32_t> 5.168846899607784 ns 5.15352435703851 ns 135676634 - #BM_atomic_add_to_relaxed<int64_t> 5.156732436798179 ns 5.141098657660172 ns 136413594 - #BM_atomic_add_to_relaxed<float> 7.763552575883229 ns 7.740082913372091 ns 90622053 - #BM_atomic_add_to_relaxed<double> 7.760723400931919 ns 7.734912038002161 ns 90618849 - #BM_atomic_add_to_unordered<int16_t> 0.3533747960000255 ns 0.3520660080000013 ns 1000000000 - #BM_atomic_add_to_unordered<int32_t> 0.3534268799999154 ns 0.352105915000001 ns 1000000000 - #BM_atomic_add_to_unordered<int64_t> 0.35323697900003026 ns 0.35204362999999894 ns 1000000000 - #BM_atomic_add_to_unordered<float> 0.7043684281594664 ns 0.7021647364411598 ns 988700453 - #BM_atomic_add_to_unordered<double> 0.704450251294842 ns 0.7021509129361374 ns 996984194 - #BM_gettid 2.1128518266669247 ns 2.106438363375118 ns 332324191 - #BM_systemTime 43.50481860259643 ns 43.373881730104316 ns 16213103 - #BM_thread_8_variables 2.8174664322723015 ns 2.8088585587229447 ns 249169901 - #BM_thread_local_8_variables 2.8176008559183345 ns 2.8088503601840658 ns 249223823 - #BM_StdMutexLockUnlock 20.51083054083145 ns 20.372680732323307 ns 34365643 - #BM_RWMutexReadLockUnlock 17.182708241218037 ns 17.10085471231418 ns 40872349 - #BM_RWMutexWriteLockUnlock 20.01395996116509 ns 19.912307155808747 ns 35403322 - #BM_SharedMutexReadLockUnlock 39.34289759177089 ns 39.214633183208534 ns 17848256 - #BM_SharedMutexWriteLockUnlock 42.42260135644499 ns 42.25185684039555 ns 16568333 - #BM_AudioUtilsMutexLockUnlock 32.59607274485956 ns 32.48981289778165 ns 21549504 - #BM_AudioUtilsPIMutexLockUnlock 33.79847568429067 ns 33.6964229192697 ns 20775880 - #BM_StdMutexInitializationLockUnlock 30.36758342133683 ns 30.254101880283233 ns 23141887 - #BM_RWMutexInitializationReadLockUnlock 27.28375660870322 ns 27.196487591970985 ns 25738012 - #BM_RWMutexInitializationWriteLockUnlock 30.20724264266599 ns 30.096879362472333 ns 23256594 - #BM_SharedMutexInitializationReadLockUnlock 57.43815201585343 ns 57.27383821891096 ns 12222096 - #BM_SharedMutexInitializationWriteLockUnlock 59.42673061824289 ns 59.25235124842115 ns 11814362 - #BM_AudioUtilsMutexInitializationLockUnlock 46.10038716918369 ns 45.953988368973455 ns 15233909 - #BM_AudioUtilsPIMutexInitializationLockUnlock 50.73553222492994 ns 50.574418947890834 ns 13835334 - #BM_StdMutexBlockingConditionVariable/threads:2 11523.72384534072 ns 12714.605659025783 ns 58632 - #BM_AudioUtilsMutexBlockingConditionVariable/threads:2 9338.361496790618 ns 11206.032771535578 ns 74760 - #BM_AudioUtilsPIMutexBlockingConditionVariable/threads:2 12430.610334229705 ns 13459.017326162135 ns 52060 - #BM_StdMutexScopedLockUnlock/threads:1 33.534067204276546 ns 33.40309483152711 ns 20796027 - #BM_StdMutexScopedLockUnlock/threads:2 269.1759952499524 ns 533.950398499998 ns 2000000 - #BM_StdMutexScopedLockUnlock/threads:4 90.18870335515196 ns 271.3231852294451 ns 2269488 - #BM_StdMutexScopedLockUnlock/threads:8 121.03213508602038 ns 451.7371193384729 ns 2448632 - #BM_RWMutexScopedReadLockUnlock/threads:1 32.11047130691962 ns 31.96092065619549 ns 21757351 - #BM_RWMutexScopedReadLockUnlock/threads:2 117.73928731993787 ns 230.31362984633367 ns 2348992 - #BM_RWMutexScopedReadLockUnlock/threads:4 220.8538545474783 ns 858.6430804361402 ns 949424 - #BM_RWMutexScopedReadLockUnlock/threads:8 217.2344705376624 ns 1528.7949547499559 ns 460552 - #BM_RWMutexScopedWriteLockUnlock/threads:1 34.76444514474894 ns 34.665961723712094 ns 20194069 - #BM_RWMutexScopedWriteLockUnlock/threads:2 303.41208949994325 ns 603.2115715000019 ns 2000000 - #BM_RWMutexScopedWriteLockUnlock/threads:4 298.4931931843524 ns 916.926215593706 ns 1571660 - #BM_RWMutexScopedWriteLockUnlock/threads:8 432.74492906249407 ns 1240.5567937500045 ns 800000 - #BM_SharedMutexScopedReadLockUnlock/threads:1 70.04048550107358 ns 69.8046640694218 ns 9059342 - #BM_SharedMutexScopedReadLockUnlock/threads:2 357.07506909046754 ns 709.4210754541601 ns 1482834 - #BM_SharedMutexScopedReadLockUnlock/threads:4 336.03568074383156 ns 1087.821794679974 ns 989168 - #BM_SharedMutexScopedReadLockUnlock/threads:8 343.4415500594684 ns 1423.0045686060148 ns 870944 - #BM_SharedMutexScopedWriteLockUnlock/threads:1 77.31578352815413 ns 77.00259046212362 ns 8135228 - #BM_SharedMutexScopedWriteLockUnlock/threads:2 356.1377498778198 ns 627.7192368534169 ns 1218796 - #BM_SharedMutexScopedWriteLockUnlock/threads:4 2206.5972784481546 ns 5390.78073569482 ns 770700 - #BM_SharedMutexScopedWriteLockUnlock/threads:8 2643.145098618517 ns 7265.627503497389 ns 1012184 - #BM_AudioUtilsMutexScopedLockUnlock/threads:1 68.37942831761342 ns 68.16332511845363 ns 8684647 - #BM_AudioUtilsMutexScopedLockUnlock/threads:2 439.5642884199026 ns 868.7699421475584 ns 1605118 - #BM_AudioUtilsMutexScopedLockUnlock/threads:4 321.1245397453114 ns 1025.1737506853917 ns 2203128 - #BM_AudioUtilsMutexScopedLockUnlock/threads:8 302.42947515758783 ns 1176.8521985370544 ns 1262112 - #BM_AudioUtilsPIMutexScopedLockUnlock/threads:1 69.87225800700081 ns 69.64552224576019 ns 8994051 - #BM_AudioUtilsPIMutexScopedLockUnlock/threads:2 4420.777346513025 ns 5456.967229338184 ns 265756 - #BM_AudioUtilsPIMutexScopedLockUnlock/threads:4 1506.8638396645179 ns 1927.406805542472 ns 424360 - #BM_AudioUtilsPIMutexScopedLockUnlock/threads:8 25030.96209476646 ns 27871.63623561846 ns 33376 - #BM_StdMutexReverseScopedLockUnlock/threads:1 33.47593087477488 ns 33.37508010876382 ns 20550186 - #BM_StdMutexReverseScopedLockUnlock/threads:2 198.84388250011398 ns 385.92393400000117 ns 2000000 - #BM_StdMutexReverseScopedLockUnlock/threads:4 93.50488264641875 ns 276.2069913615782 ns 3951648 - #BM_StdMutexReverseScopedLockUnlock/threads:8 110.50842131360572 ns 378.4212902611287 ns 2141768 - #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:1 68.2132503060489 ns 68.01976601705918 ns 9013905 - #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:2 223.03285165273516 ns 424.2072166440236 ns 1879738 - #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:4 264.11614886066064 ns 743.2390829429721 ns 1815416 - #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:8 274.5291393750193 ns 1015.9050412499973 ns 800000 - #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:1 69.50784383771779 ns 69.31317329009033 ns 8408894 - #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:2 631.9418303245776 ns 790.5849174679049 ns 1417388 - #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:4 12829.762531245593 ns 15402.261100000063 ns 40000 - #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:8 24954.85928430851 ns 26994.147940851126 ns 28944 - #BM_empty_while 0.3522347409998474 ns 0.35108219199999985 ns 1000000000 + #BM_atomic_add_equals<int32_t> 6.49708926103908 ns 6.472003500961985 ns 100653478 + #BM_atomic_add_to_seq_cst<int16_t> 6.612271967786937 ns 6.581374958282836 ns 106359096 + #BM_atomic_add_to_seq_cst<int32_t> 6.557587423605134 ns 6.526606701594883 ns 107255215 + #BM_atomic_add_to_seq_cst<int64_t> 6.611507804022058 ns 6.581571911586092 ns 106345485 + #BM_atomic_add_to_seq_cst<float> 7.940855618974662 ns 7.903428099126927 ns 88699973 + #BM_atomic_add_to_seq_cst<double> 7.892445499019113 ns 7.8553659380799665 ns 89357153 + #BM_atomic_add_to_relaxed<int16_t> 5.184282118879067 ns 5.16066629475692 ns 135599206 + #BM_atomic_add_to_relaxed<int32_t> 5.1525768457041385 ns 5.13082227851535 ns 135158791 + #BM_atomic_add_to_relaxed<int64_t> 5.18196641836829 ns 5.159386369625577 ns 135816093 + #BM_atomic_add_to_relaxed<float> 7.770385032959841 ns 7.73380439539666 ns 90647928 + #BM_atomic_add_to_relaxed<double> 7.774301916271472 ns 7.738535149984901 ns 90634221 + #BM_atomic_add_to_unordered<int16_t> 0.3536779780000643 ns 0.35200833499999895 ns 1000000000 + #BM_atomic_add_to_unordered<int32_t> 0.35392805999993016 ns 0.35196308799999976 ns 1000000000 + #BM_atomic_add_to_unordered<int64_t> 0.35319002300002467 ns 0.35198640199999964 ns 1000000000 + #BM_atomic_add_to_unordered<float> 0.70537668831226 ns 0.7020379879090289 ns 997049929 + #BM_atomic_add_to_unordered<double> 0.7051115684923829 ns 0.7020150599803927 ns 997060395 + #BM_gettid 2.1148936981430118 ns 2.1060756400940295 ns 332362067 + #BM_systemTime 45.252249624452496 ns 45.0340629111274 ns 15544150 + #BM_thread_8_variables 2.8213460062780684 ns 2.808115782371781 ns 249265925 + #BM_thread_local_8_variables 2.821866488468177 ns 2.8083880672269608 ns 249247144 + #BM_StdMutexLockUnlock 20.443066322018925 ns 20.352346307194033 ns 34391639 + #BM_RWMutexReadLockUnlock 17.16239599718925 ns 17.081916542086816 ns 41022569 + #BM_RWMutexWriteLockUnlock 19.853425611091456 ns 19.76218186210376 ns 35401669 + #BM_SharedMutexReadLockUnlock 39.3766244173411 ns 39.22216162561333 ns 17844783 + #BM_SharedMutexWriteLockUnlock 42.54361761104441 ns 42.346080362033284 ns 16528427 + #BM_AudioUtilsMutexLockUnlock 32.316459678551176 ns 32.165598159419815 ns 21770091 + #BM_AudioUtilsPIMutexLockUnlock 32.91919379029218 ns 32.77652120372695 ns 21350822 + #BM_StdMutexInitializationLockUnlock 30.578741677235417 ns 30.43282384371753 ns 22998964 + #BM_RWMutexInitializationReadLockUnlock 27.291456623695325 ns 27.160228920838776 ns 25787779 + #BM_RWMutexInitializationWriteLockUnlock 30.177246005611483 ns 30.036540230009923 ns 23284993 + #BM_SharedMutexInitializationReadLockUnlock 57.531404407024816 ns 57.260222202532894 ns 12223173 + #BM_SharedMutexInitializationWriteLockUnlock 59.4755204867324 ns 59.19673708562941 ns 11821579 + #BM_AudioUtilsMutexInitializationLockUnlock 44.70003716328854 ns 44.516311007242074 ns 15719814 + #BM_AudioUtilsPIMutexInitializationLockUnlock 50.0569598188049 ns 49.76151232748948 ns 14066530 + #BM_StdMutexBlockingConditionVariable/threads:2 21567.811717894936 ns 24430.56515004263 ns 32924 + #BM_AudioUtilsMutexBlockingConditionVariable/threads:2 17895.140258052194 ns 21379.1065317615 ns 64638 + #BM_AudioUtilsPIMutexBlockingConditionVariable/threads:2 16234.8055847075 ns 19758.387649925 ns 53360 + #BM_StdMutexScopedLockUnlock/threads:1 33.67012270000208 ns 33.51255456709671 ns 20617278 + #BM_StdMutexScopedLockUnlock/threads:2 334.03488387281124 ns 663.680747963286 ns 4746864 + #BM_StdMutexScopedLockUnlock/threads:4 128.53316611793798 ns 401.5973070022828 ns 1473748 + #BM_StdMutexScopedLockUnlock/threads:8 121.5346446122214 ns 431.4386839654385 ns 1614912 + #BM_RWMutexScopedReadLockUnlock/threads:1 32.1396832808462 ns 31.999380498079336 ns 21767487 + #BM_RWMutexScopedReadLockUnlock/threads:2 170.84739199998467 ns 339.15160600000127 ns 2000000 + #BM_RWMutexScopedReadLockUnlock/threads:4 246.6480510579401 ns 975.0323799866918 ns 745584 + #BM_RWMutexScopedReadLockUnlock/threads:8 238.1892263738593 ns 1826.4869477861284 ns 797336 + #BM_RWMutexScopedWriteLockUnlock/threads:1 35.00912072790768 ns 34.85540479077848 ns 20082498 + #BM_RWMutexScopedWriteLockUnlock/threads:2 223.1827494999834 ns 438.1546670000002 ns 2000000 + #BM_RWMutexScopedWriteLockUnlock/threads:4 486.84123644366286 ns 1672.5719360034366 ns 931300 + #BM_RWMutexScopedWriteLockUnlock/threads:8 564.87138969739 ns 2151.4437615176666 ns 464504 + #BM_SharedMutexScopedReadLockUnlock/threads:1 70.36601526045072 ns 69.84103085964486 ns 8484414 + #BM_SharedMutexScopedReadLockUnlock/threads:2 379.4861527643546 ns 753.3688988828279 ns 1567172 + #BM_SharedMutexScopedReadLockUnlock/threads:4 347.9172981302136 ns 1101.924198607498 ns 621044 + #BM_SharedMutexScopedReadLockUnlock/threads:8 343.91707104488904 ns 1432.435581480825 ns 946824 + #BM_SharedMutexScopedWriteLockUnlock/threads:1 76.38441149160603 ns 76.09520014228487 ns 7489222 + #BM_SharedMutexScopedWriteLockUnlock/threads:2 549.22601694552 ns 992.5252101883976 ns 644184 + #BM_SharedMutexScopedWriteLockUnlock/threads:4 4286.404760312738 ns 10445.512869046388 ns 194420 + #BM_SharedMutexScopedWriteLockUnlock/threads:8 2777.3490250002196 ns 9345.93453749998 ns 80000 + #BM_AudioUtilsMutexScopedLockUnlock/threads:1 67.12805853866571 ns 66.83999986247574 ns 10470891 + #BM_AudioUtilsMutexScopedLockUnlock/threads:2 367.18428717404385 ns 720.3494810108673 ns 988460 + #BM_AudioUtilsMutexScopedLockUnlock/threads:4 247.06318310687618 ns 713.1825332376544 ns 847232 + #BM_AudioUtilsMutexScopedLockUnlock/threads:8 321.6128632660656 ns 1219.0056378491781 ns 1102016 + #BM_AudioUtilsPIMutexScopedLockUnlock/threads:1 68.06778275108272 ns 67.82319073618316 ns 8610878 + #BM_AudioUtilsPIMutexScopedLockUnlock/threads:2 1740.7659979908651 ns 2080.35904028861 ns 535536 + #BM_AudioUtilsPIMutexScopedLockUnlock/threads:4 44770.96510142291 ns 61714.283330752354 ns 25832 + #BM_AudioUtilsPIMutexScopedLockUnlock/threads:8 31532.62551468983 ns 39864.36350786635 ns 16272 + #BM_StdMutexReverseScopedLockUnlock/threads:1 33.53062718758725 ns 33.37078532917351 ns 20302060 + #BM_StdMutexReverseScopedLockUnlock/threads:2 56.8951118357229 ns 110.10677172633986 ns 6485584 + #BM_StdMutexReverseScopedLockUnlock/threads:4 136.7939034223377 ns 471.5097278019701 ns 3746684 + #BM_StdMutexReverseScopedLockUnlock/threads:8 159.71482571380815 ns 672.6030946954844 ns 1347984 + #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:1 67.08830703993506 ns 66.79194730109215 ns 9299168 + #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:2 349.1989208775619 ns 680.7213221240016 ns 1205424 + #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:4 242.54533324270986 ns 751.2404474633255 ns 1537288 + #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:8 264.199964885266 ns 1063.577426763085 ns 585224 + #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:1 68.08892559027429 ns 67.76412547568847 ns 10221231 + #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:2 2234.804406832892 ns 2710.598153659913 ns 2438554 + #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:4 30793.467662667594 ns 40396.41114605845 ns 20348 + #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:8 29475.46524613768 ns 38111.187252376985 ns 20192 + #BM_empty_while 0.35276680499998747 ns 0.35103430999999574 ns 1000000000 */ diff --git a/audio_utils/include/audio_utils/mutex.h b/audio_utils/include/audio_utils/mutex.h index aafdab45..ea846874 100644 --- a/audio_utils/include/audio_utils/mutex.h +++ b/audio_utils/include/audio_utils/mutex.h @@ -461,6 +461,8 @@ private: T t_; }; +inline constexpr pid_t kInvalidTid = -1; + // While std::atomic with the default std::memory_order_seq_cst // access could be used, it results in performance loss over less // restrictive memory access. @@ -884,6 +886,23 @@ public: return mutexes_held_.remove(mutex); } + // Variants used by condition_variable on wait() that handle + // hint metadata. This is used by deadlock detection algorithm to inform we + // are waiting on a worker thread identified by notifier_tid. + + void push_held_for_cv(MutexHandle mutex, Order order) { + push_held(mutex, order); + // condition wait has expired. always invalidate. + cv_info_.first = kInvalidTid; + } + + bool remove_held_for_cv(MutexHandle mutex, Order order, pid_t notifier_tid) { + // last condition on the mutex overwrites. + cv_info_.second = order; + cv_info_.first = notifier_tid; + return remove_held(mutex); + } + /* * Due to the fact that the thread_mutex_info contents are not globally locked, * there may be temporal shear. The string representation is @@ -894,6 +913,12 @@ public: s.append("tid: ").append(std::to_string(static_cast<int>(tid_))); s.append("\nwaiting: ").append(std::to_string( reinterpret_cast<uintptr_t>(mutex_wait_.load()))); + // inform if there is a condition variable wait associated with a known thread. + if (cv_info_.first != kInvalidTid) { + s.append("\ncv_tid: ").append(std::to_string(cv_info_.first.load())) + .append(" cv_order: ").append(std::to_string( + static_cast<size_t>(cv_info_.second.load()))); + } s.append("\nheld: ").append(mutexes_held_.to_string()); return s; } @@ -912,6 +937,8 @@ public: const pid_t tid_; // me thread_atomic<MutexHandle> mutex_wait_{}; // mutex waiting for + std::pair<thread_atomic<pid_t>, thread_atomic<Order>> cv_info_{ + kInvalidTid, (Order)-1 }; // condition variable wait with known notifier tid. atomic_stack_t mutexes_held_; // mutexes held }; @@ -951,6 +978,7 @@ public: const pid_t tid; // tid for which the deadlock was checked bool has_cycle = false; // true if there is a cycle detected + bool cv_detected = false; // true if the logic went through a condition variable. std::vector<std::pair<pid_t, std::string>> chain; // wait chain of tids and mutexes. }; @@ -1039,20 +1067,18 @@ public: } /** - * Returns the tid mutex pointer (a void*) if it is waiting. + * Returns the thread info for a pid_t. * * It should use a copy of the registry map which is not changing * as it does not take any lock. */ - static void* tid_to_mutex_wait( + static std::shared_ptr<ThreadInfo> tid_to_info( const std::unordered_map<pid_t, std::weak_ptr<ThreadInfo>>& registry_map, pid_t tid) { const auto it = registry_map.find(tid); if (it == registry_map.end()) return {}; const auto& weak_info = it->second; // unmapped returns empty weak_ptr. - const auto info = weak_info.lock(); - if (!info) return {}; - return info->mutex_wait_.load(); + return weak_info.lock(); } /** @@ -1078,8 +1104,14 @@ public: deadlock_info_t deadlock_info{tid}; // if tid not waiting, return. - void* m = tid_to_mutex_wait(registry_map, tid); - if (m == nullptr) return deadlock_info; + + const auto tinfo_original_tid = tid_to_info(registry_map, tid); + if (tinfo_original_tid == nullptr) return deadlock_info; + + void* m = tinfo_original_tid->mutex_wait_.load(); + pid_t cv_tid = tinfo_original_tid->cv_info_.first; + if (m == nullptr && cv_tid == kInvalidTid) return deadlock_info; + size_t cv_order = static_cast<size_t>(tinfo_original_tid->cv_info_.second.load()); bool subset = false; // do we have missing mutex data per thread? @@ -1129,14 +1161,29 @@ public: // until we get no more tids, or a tid cycle. std::unordered_set<pid_t> visited; visited.insert(tid); // mark the original tid, we start there for cycle detection. - while (true) { - // no tid associated with the mutex. - if (mutex_to_tid.count(m) == 0) return deadlock_info; - const auto [tid2, order] = mutex_to_tid[m]; + for (pid_t tid2 = tid; true;) { + size_t order; + bool cv_detected = false; // current wait relationship is through condition_variable. + + if (m != nullptr && mutex_to_tid.count(m)) { + // waiting on mutex held by another tid. + std::tie(tid2, order) = mutex_to_tid[m]; + } else if (cv_tid != kInvalidTid) { + // condition variable waiting on tid. + tid2 = cv_tid; + order = cv_order; + deadlock_info.cv_detected = true; + cv_detected = true; + } else { + // no mutex or cv info. + return deadlock_info; + } // add to chain. + // if waiting through a condition variable, we prefix with "cv-". const auto name = order < std::size(mutex_names) ? mutex_names[order] : "unknown"; - deadlock_info.chain.emplace_back(tid2, name); + deadlock_info.chain.emplace_back(tid2, cv_detected ? + std::string("cv-").append(name).c_str() : name); // cycle detected if (visited.count(tid2)) { @@ -1146,8 +1193,10 @@ public: visited.insert(tid2); // if tid not waiting return (could be blocked on binder). - m = tid_to_mutex_wait(registry_map, tid2); - if (m == nullptr) return deadlock_info; + const auto tinfo = tid_to_info(registry_map, tid2); + m = tinfo->mutex_wait_.load(); + cv_tid = tinfo->cv_info_.first; + cv_order = static_cast<size_t>(tinfo->cv_info_.second.load()); } } @@ -1423,9 +1472,12 @@ public: // helper class for registering statistics for a cv wait. class cv_wait_scoped_stat_enabled { public: - explicit cv_wait_scoped_stat_enabled(mutex& m) : mutex_(m) { + explicit cv_wait_scoped_stat_enabled(mutex& m, pid_t notifier_tid = kInvalidTid) + : mutex_(m) { ++mutex_.stat_.unlocks; - const bool success = mutex_.get_thread_mutex_info()->remove_held(&mutex_); + // metadata that we relinquish lock. + const bool success = mutex_.get_thread_mutex_info()->remove_held_for_cv( + &mutex_, mutex_.order_, notifier_tid); LOG_ALWAYS_FATAL_IF(Attributes::abort_on_invalid_unlock_ && mutex_get_enable_flag() && !success, @@ -1434,7 +1486,8 @@ public: ~cv_wait_scoped_stat_enabled() { ++mutex_.stat_.locks; - mutex_.get_thread_mutex_info()->push_held(&mutex_, mutex_.order_); + // metadata that we are reacquiring lock. + mutex_.get_thread_mutex_info()->push_held_for_cv(&mutex_, mutex_.order_); } private: mutex& mutex_; @@ -1592,6 +1645,9 @@ private: // It is possible to use std::condition_variable_any for a generic mutex type, // but it is less efficient. +// The audio_utils condition_variable permits speicifying a "notifier_tid" +// metadata in the wait() methods, which states the expected tid of the +// notification thread for deadlock / wait detection purposes. class condition_variable { public: void notify_one() noexcept { @@ -1602,44 +1658,46 @@ public: cv_.notify_all(); } - void wait(unique_lock& lock) { - mutex::cv_wait_scoped_stat_t ws(lock.native_mutex()); + void wait(unique_lock& lock, pid_t notifier_tid = kInvalidTid) { + mutex::cv_wait_scoped_stat_t ws(lock.native_mutex(), notifier_tid); cv_.wait(lock.std_unique_lock()); } template<typename Predicate> - void wait(unique_lock& lock, Predicate stop_waiting) { - mutex::cv_wait_scoped_stat_t ws(lock.native_mutex()); + void wait(unique_lock& lock, Predicate stop_waiting, pid_t notifier_tid = kInvalidTid) { + mutex::cv_wait_scoped_stat_t ws(lock.native_mutex(), notifier_tid); cv_.wait(lock.std_unique_lock(), std::move(stop_waiting)); } template<typename Rep, typename Period> std::cv_status wait_for(unique_lock& lock, - const std::chrono::duration<Rep, Period>& rel_time) { - mutex::cv_wait_scoped_stat_t ws(lock.native_mutex()); + const std::chrono::duration<Rep, Period>& rel_time, + pid_t notifier_tid = kInvalidTid) { + mutex::cv_wait_scoped_stat_t ws(lock.native_mutex(), notifier_tid); return cv_.wait_for(lock.std_unique_lock(), rel_time); } template<typename Rep, typename Period, typename Predicate> bool wait_for(unique_lock& lock, const std::chrono::duration<Rep, Period>& rel_time, - Predicate stop_waiting) { - mutex::cv_wait_scoped_stat_t ws(lock.native_mutex()); + Predicate stop_waiting, pid_t notifier_tid = kInvalidTid) { + mutex::cv_wait_scoped_stat_t ws(lock.native_mutex(), notifier_tid); return cv_.wait_for(lock.std_unique_lock(), rel_time, std::move(stop_waiting)); } template<typename Clock, typename Duration> std::cv_status wait_until(unique_lock& lock, - const std::chrono::time_point<Clock, Duration>& timeout_time) { - mutex::cv_wait_scoped_stat_t ws(lock.native_mutex()); + const std::chrono::time_point<Clock, Duration>& timeout_time, + pid_t notifier_tid = kInvalidTid) { + mutex::cv_wait_scoped_stat_t ws(lock.native_mutex(), notifier_tid); return cv_.wait_until(lock.std_unique_lock(), timeout_time); } template<typename Clock, typename Duration, typename Predicate> bool wait_until(unique_lock& lock, const std::chrono::time_point<Clock, Duration>& timeout_time, - Predicate stop_waiting) { - mutex::cv_wait_scoped_stat_t ws(lock.native_mutex()); + Predicate stop_waiting, pid_t notifier_tid = kInvalidTid) { + mutex::cv_wait_scoped_stat_t ws(lock.native_mutex(), notifier_tid); return cv_.wait_until(lock.std_unique_lock(), timeout_time, std::move(stop_waiting)); } diff --git a/audio_utils/tests/audio_mutex_tests.cpp b/audio_utils/tests/audio_mutex_tests.cpp index 29c967e2..5d638dd8 100644 --- a/audio_utils/tests/audio_mutex_tests.cpp +++ b/audio_utils/tests/audio_mutex_tests.cpp @@ -570,6 +570,9 @@ TEST_P(MutexTestSuite, DeadlockDetection) { // no cycle. EXPECT_EQ(false, deadlockInfo.has_cycle); + // no condition_variable detected + EXPECT_EQ(false, deadlockInfo.cv_detected); + // thread1 is waiting on a chain of 3 other threads. const auto chain = deadlockInfo.chain; const size_t chain_size = chain.size(); @@ -604,7 +607,130 @@ TEST_P(MutexTestSuite, DeadlockDetection) { t1.join(); } +TEST_P(MutexTestSuite, DeadlockConditionVariableDetection) { + using Mutex = android::audio_utils::mutex; + using UniqueLock = android::audio_utils::unique_lock; + using ConditionVariable = android::audio_utils::condition_variable; + const bool priority_inheritance = GetParam(); + + // order checked below. + constexpr size_t kOrder1 = 1; + constexpr size_t kOrder2 = 2; + constexpr size_t kOrder3 = 3; + static_assert(Mutex::attributes_t::order_size_ > kOrder3); + + Mutex m1{priority_inheritance, static_cast<Mutex::attributes_t::order_t>(kOrder1)}; + Mutex m2{priority_inheritance, static_cast<Mutex::attributes_t::order_t>(kOrder2)}; + Mutex m3{priority_inheritance, static_cast<Mutex::attributes_t::order_t>(kOrder3)}; + Mutex m4{priority_inheritance}; + Mutex m{priority_inheritance}; + ConditionVariable cv, cv2, cv4; + bool quit = false; // GUARDED_BY(m) + std::atomic<pid_t> tid1{}, tid2{}, tid3{}, tid4{}; + + std::thread t4([&]() { + // UniqueLock ul4(m4); + UniqueLock ul(m); + tid4 = android::audio_utils::gettid_wrapper(); + while (!quit) { + cv.wait(ul, [&]{ return quit; }); + if (quit) break; + } + }); + + while (tid4 == 0) { usleep(1000); } + + std::thread t3([&]() { + UniqueLock ul3(m3); + tid3 = android::audio_utils::gettid_wrapper(); + UniqueLock ul4(m4); + while (!quit) { + // use the wait method with the notifier_tid metadata. + cv4.wait(ul4, [&]{ return quit; }, tid4); + if (quit) break; + } + }); + + while (tid3 == 0) { usleep(1000); } + + std::thread t2([&]() { + // UniqueLock ul2(m2); + tid2 = android::audio_utils::gettid_wrapper(); + UniqueLock ul3(m3); + }); + + while (tid2 == 0) { usleep(1000); } + + std::thread t1([&]() { + UniqueLock ul1(m1); + tid1 = android::audio_utils::gettid_wrapper(); + UniqueLock ul2(m2); + while (!quit) { + // use the wait method with the notifier_tid metadata. + cv2.wait(ul2, [&]{ return quit; }, tid2); + if (quit) break; + } + }); + + while (tid1 == 0) { usleep(1000); } + + // we know that the threads will now block in the proper order. + // however, we need to wait for the block to happen. + // this part is racy unless we check the thread state or use + // futexes directly in our mutex (which allows atomic accuracy of wait). + usleep(20000); + + const auto deadlockInfo = android::audio_utils::mutex::deadlock_detection(tid1); + + // no cycle. + EXPECT_EQ(false, deadlockInfo.has_cycle); + + // condition_variable detected + EXPECT_EQ(true, deadlockInfo.cv_detected); + + // thread1 is waiting on a chain of 3 other threads. + const auto chain = deadlockInfo.chain; + const size_t chain_size = chain.size(); + EXPECT_EQ(3u, chain_size); + + const auto default_idx = static_cast<size_t>(Mutex::attributes_t::order_default_); + if (chain_size > 0) { + EXPECT_EQ(tid2, chain[0].first); + EXPECT_EQ(std::string("cv-").append(Mutex::attributes_t::order_names_[kOrder2]).c_str(), + chain[0].second); + } + if (chain_size > 1) { + EXPECT_EQ(tid3, chain[1].first); + EXPECT_EQ(Mutex::attributes_t::order_names_[kOrder3], chain[1].second); + } + if (chain_size > 2) { + EXPECT_EQ(tid4, chain[2].first); + EXPECT_EQ(std::string("cv-").append( + Mutex::attributes_t::order_names_[default_idx]).c_str(), chain[2].second); + } + + ALOGD("%s", android::audio_utils::mutex::all_threads_to_string().c_str()); + + { + UniqueLock ul(m); + quit = true; + cv.notify_one(); + } + { + UniqueLock ul2(m); + cv2.notify_one(); + } + { + UniqueLock ul4(m); + cv4.notify_one(); + } + + t4.join(); + t3.join(); + t2.join(); + t1.join(); +} + INSTANTIATE_TEST_SUITE_P(Mutex_NonPI_and_PI, MutexTestSuite, testing::Values(false, true)); } // namespace android - |