@@ -29,3 +29,168 @@ void gh_99628() {
29
29
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
30
30
m.unlock ();
31
31
}
32
+
33
+ void no_false_positive_gh_104241 () {
34
+ std::mutex m;
35
+ m.lock ();
36
+ // If inheritance not handled properly, this unlock might not match the lock
37
+ // above because technically they act on different memory regions:
38
+ // __mutex_base and mutex.
39
+ m.unlock ();
40
+ sleep (10 ); // no-warning
41
+ }
42
+
43
+ struct TwoMutexes {
44
+ std::mutex m1;
45
+ std::mutex m2;
46
+ };
47
+
48
+ void two_mutexes_no_false_negative (TwoMutexes &tm) {
49
+ tm.m1 .lock ();
50
+ // expected-note@-1 {{Entering critical section here}}
51
+ tm.m2 .unlock ();
52
+ sleep (10 );
53
+ // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
54
+ // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
55
+ tm.m1 .unlock ();
56
+ }
57
+
58
+ struct MyMutexBase1 : std::mutex {
59
+ void lock1 () { lock (); }
60
+ // expected-note@-1 {{Entering critical section here}}
61
+ void unlock1 () { unlock (); }
62
+ };
63
+ struct MyMutexBase2 : std::mutex {
64
+ void lock2 () { lock (); }
65
+ void unlock2 () { unlock (); }
66
+ };
67
+ struct MyMutex : MyMutexBase1, MyMutexBase2 {};
68
+ // MyMutex has two distinct std::mutex as base classes
69
+
70
+ void custom_mutex_tp (MyMutexBase1 &mb) {
71
+ mb.lock ();
72
+ // expected-note@-1 {{Entering critical section here}}
73
+ sleep (10 );
74
+ // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
75
+ // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
76
+ mb.unlock ();
77
+ }
78
+
79
+ void custom_mutex_tn (MyMutexBase1 &mb) {
80
+ mb.lock ();
81
+ mb.unlock ();
82
+ sleep (10 );
83
+ }
84
+
85
+ void custom_mutex_cast_tp (MyMutexBase1 &mb) {
86
+ static_cast <std::mutex&>(mb).lock ();
87
+ // expected-note@-1 {{Entering critical section here}}
88
+ sleep (10 );
89
+ // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
90
+ // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
91
+ static_cast <std::mutex&>(mb).unlock ();
92
+ }
93
+
94
+ void custom_mutex_cast_tn (MyMutexBase1 &mb) {
95
+ static_cast <std::mutex&>(mb).lock ();
96
+ static_cast <std::mutex&>(mb).unlock ();
97
+ sleep (10 );
98
+ }
99
+
100
+ void two_custom_mutex_bases_tp (MyMutex &m) {
101
+ m.lock1 ();
102
+ // expected-note@-1 {{Calling 'MyMutexBase1::lock1'}}
103
+ // expected-note@-2 {{Returning from 'MyMutexBase1::lock1'}}
104
+ m.unlock2 ();
105
+ sleep (10 );
106
+ // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
107
+ // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
108
+ m.unlock1 ();
109
+ }
110
+
111
+ void two_custom_mutex_bases_tn (MyMutex &m) {
112
+ m.lock1 ();
113
+ m.unlock1 ();
114
+ sleep (10 );
115
+ }
116
+
117
+ void two_custom_mutex_bases_casts_tp (MyMutex &m) {
118
+ static_cast <MyMutexBase1&>(m).lock ();
119
+ // expected-note@-1 {{Entering critical section here}}
120
+ static_cast <MyMutexBase2&>(m).unlock ();
121
+ sleep (10 );
122
+ // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
123
+ // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
124
+ static_cast <MyMutexBase1&>(m).unlock ();
125
+ }
126
+
127
+ void two_custom_mutex_bases_casts_tn (MyMutex &m) {
128
+ static_cast <MyMutexBase1&>(m).lock ();
129
+ static_cast <MyMutexBase1&>(m).unlock ();
130
+ sleep (10 );
131
+ }
132
+
133
+ struct MutexVirtBase1 : virtual std::mutex {
134
+ void lock1 () { lock (); }
135
+ // expected-note@-1 {{Entering critical section here}}
136
+ void unlock1 () { unlock (); }
137
+ };
138
+
139
+ struct MutexVirtBase2 : virtual std::mutex {
140
+ void lock2 () { lock (); }
141
+ void unlock2 () { unlock (); }
142
+ };
143
+
144
+ struct CombinedVirtMutex : MutexVirtBase1, MutexVirtBase2 {};
145
+
146
+ void virt_inherited_mutexes_same_base_tn1 (CombinedVirtMutex &cvt) {
147
+ cvt.lock1 ();
148
+ cvt.unlock1 ();
149
+ sleep (10 );
150
+ }
151
+
152
+ void virt_inherited_mutexes_different_bases_tn (CombinedVirtMutex &cvt) {
153
+ cvt.lock1 ();
154
+ cvt.unlock2 (); // Despite a different name, unlock2 acts on the same mutex as lock1
155
+ sleep (10 );
156
+ }
157
+
158
+ void virt_inherited_mutexes_different_bases_tp (CombinedVirtMutex &cvt) {
159
+ cvt.lock1 ();
160
+ // expected-note@-1 {{Calling 'MutexVirtBase1::lock1'}}
161
+ // expected-note@-2 {{Returning from 'MutexVirtBase1::lock1'}}
162
+ sleep (10 );
163
+ // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
164
+ // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
165
+ cvt.unlock1 ();
166
+ }
167
+
168
+ namespace std {
169
+ template <class ... MutexTypes> struct scoped_lock {
170
+ explicit scoped_lock (MutexTypes&... m);
171
+ ~scoped_lock ();
172
+ };
173
+ template <class MutexType > class scoped_lock <MutexType> {
174
+ public:
175
+ explicit scoped_lock (MutexType& m) : m(m) { m.lock (); }
176
+ ~scoped_lock () { m.unlock (); }
177
+ private:
178
+ MutexType& m;
179
+ };
180
+ } // namespace std
181
+
182
+ namespace gh_104241 {
183
+ int magic_number;
184
+ std::mutex m;
185
+
186
+ void fixed () {
187
+ int current;
188
+ for (int items_processed = 0 ; items_processed < 100 ; ++items_processed) {
189
+ {
190
+ std::scoped_lock<std::mutex> guard (m);
191
+ current = magic_number;
192
+ }
193
+ sleep (current); // expected no warning
194
+ }
195
+ }
196
+ } // namespace gh_104241
0 commit comments