summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Hung <hunga@google.com>2024-01-22 17:25:25 -0800
committerAndy Hung <hunga@google.com>2024-03-11 15:06:15 -0700
commit08902efe629b02511f0923625b3850420da76a3e (patch)
tree709e4740a86f0101746090146ef9089dc070fed8
parent21d7c296eb7381bbbd65489d96844486341f07b6 (diff)
downloadmedia-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.cpp156
-rw-r--r--audio_utils/include/audio_utils/mutex.h116
-rw-r--r--audio_utils/tests/audio_mutex_tests.cpp128
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
-