2 This file is part of drd, a thread error detector.
4 Copyright (C) 2006-2020 Bart Van Assche <bvanassche@acm.org>.
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
19 The GNU General Public License is contained in the file COPYING.
23 #include "drd_clientobj.h"
25 #include "drd_error.h"
26 #include "drd_mutex.h"
27 #include "pub_tool_errormgr.h" /* VG_(maybe_record_error)() */
28 #include "pub_tool_libcassert.h" /* tl_assert() */
29 #include "pub_tool_libcbase.h" /* VG_(memcmp)() */
30 #include "pub_tool_libcprint.h" /* VG_(printf)() */
31 #include "pub_tool_machine.h" /* VG_(get_IP)() */
32 #include "pub_tool_threadstate.h" /* VG_(get_running_tid)() */
35 /* Local functions. */
37 static void DRD_(cond_cleanup
)(struct cond_info
* p
);
40 /* Local variables. */
42 static Bool
DRD_(s_report_signal_unlocked
) = True
;
43 static Bool
DRD_(s_trace_cond
);
46 /* Function definitions. */
48 void DRD_(cond_set_report_signal_unlocked
)(const Bool r
)
50 DRD_(s_report_signal_unlocked
) = r
;
53 void DRD_(cond_set_trace
)(const Bool trace_cond
)
55 DRD_(s_trace_cond
) = trace_cond
;
59 void DRD_(cond_initialize
)(struct cond_info
* const p
, const Addr cond
)
62 tl_assert(p
->a1
== cond
);
63 tl_assert(p
->type
== ClientCondvar
);
65 p
->cleanup
= (void(*)(DrdClientobj
*))(DRD_(cond_cleanup
));
72 * Free the memory that was allocated by cond_initialize(). Called by
73 * DRD_(clientobj_remove)().
75 static void DRD_(cond_cleanup
)(struct cond_info
* p
)
81 q
= &(DRD_(clientobj_get
)(p
->mutex
, ClientMutex
)->mutex
);
83 CondDestrErrInfo cde
= {
84 DRD_(thread_get_running_tid
)(),
87 q
? q
->owner
: DRD_INVALID_THREADID
89 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
91 VG_(get_IP
)(VG_(get_running_tid
)()),
92 "Destroying condition variable that is being"
100 * Report that the synchronization object at address 'addr' is of the
103 static void wrong_type(const Addr addr
)
105 GenericErrInfo gei
= {
106 .tid
= DRD_(thread_get_running_tid
)(),
109 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
111 VG_(get_IP
)(VG_(get_running_tid
)()),
112 "wrong type of synchronization object",
116 static struct cond_info
* cond_get_or_allocate(const Addr cond
)
120 tl_assert(offsetof(DrdClientobj
, cond
) == 0);
121 p
= &(DRD_(clientobj_get
)(cond
, ClientCondvar
)->cond
);
125 if (DRD_(clientobj_present
)(cond
, cond
+ 1))
131 p
= &(DRD_(clientobj_add
)(cond
, ClientCondvar
)->cond
);
132 DRD_(cond_initialize
)(p
, cond
);
136 struct cond_info
* DRD_(cond_get
)(const Addr cond
)
138 tl_assert(offsetof(DrdClientobj
, cond
) == 0);
139 return &(DRD_(clientobj_get
)(cond
, ClientCondvar
)->cond
);
142 /** Called before pthread_cond_init(). */
143 void DRD_(cond_pre_init
)(const Addr cond
)
147 if (DRD_(s_trace_cond
))
148 DRD_(trace_msg
)("[%u] cond_init cond 0x%lx",
149 DRD_(thread_get_running_tid
)(), cond
);
151 p
= DRD_(cond_get
)(cond
);
154 CondErrInfo cei
= { .tid
= DRD_(thread_get_running_tid
)(), .cond
= cond
};
155 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
157 VG_(get_IP
)(VG_(get_running_tid
)()),
162 cond_get_or_allocate(cond
);
165 /** Called after pthread_cond_destroy(). */
166 void DRD_(cond_post_destroy
)(const Addr cond
, const Bool destroy_succeeded
)
170 if (DRD_(s_trace_cond
))
171 DRD_(trace_msg
)("[%u] cond_destroy cond 0x%lx",
172 DRD_(thread_get_running_tid
)(), cond
);
174 p
= DRD_(cond_get
)(cond
);
177 CondErrInfo cei
= { .tid
= DRD_(thread_get_running_tid
)(), .cond
= cond
};
178 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
180 VG_(get_IP
)(VG_(get_running_tid
)()),
181 "not a condition variable",
186 if (p
->waiter_count
!= 0)
188 CondErrInfo cei
= { .tid
= DRD_(thread_get_running_tid
)(), .cond
= cond
};
189 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
191 VG_(get_IP
)(VG_(get_running_tid
)()),
192 "destruction of condition variable being waited"
197 if (destroy_succeeded
)
198 DRD_(clientobj_remove
)(p
->a1
, ClientCondvar
);
202 * Called before pthread_cond_wait(). Note: before this function is called,
203 * mutex_unlock() has already been called from drd_clientreq.c.
205 void DRD_(cond_pre_wait
)(const Addr cond
, const Addr mutex
)
208 struct mutex_info
* q
;
210 if (DRD_(s_trace_cond
))
211 DRD_(trace_msg
)("[%u] cond_pre_wait cond 0x%lx",
212 DRD_(thread_get_running_tid
)(), cond
);
214 p
= cond_get_or_allocate(cond
);
217 CondErrInfo cei
= { .tid
= DRD_(thread_get_running_tid
)(), .cond
= cond
};
218 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
220 VG_(get_IP
)(VG_(get_running_tid
)()),
221 "not a condition variable",
226 if (p
->waiter_count
== 0)
230 else if (p
->mutex
!= mutex
)
233 = { .tid
= DRD_(thread_get_running_tid
)(),
234 .cond
= cond
, .mutex1
= p
->mutex
, .mutex2
= mutex
};
235 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
237 VG_(get_IP
)(VG_(get_running_tid
)()),
238 "Inconsistent association of condition variable"
243 q
= DRD_(mutex_get
)(p
->mutex
);
245 && q
->owner
== DRD_(thread_get_running_tid
)() && q
->recursion_count
> 0)
247 const ThreadId vg_tid
= VG_(get_running_tid
)();
248 MutexErrInfo MEI
= { DRD_(thread_get_running_tid
)(),
249 q
->a1
, q
->recursion_count
, q
->owner
};
250 VG_(maybe_record_error
)(vg_tid
,
253 "Mutex locked recursively",
258 DRD_(not_a_mutex
)(p
->mutex
);
265 * Called after pthread_cond_wait().
267 void DRD_(cond_post_wait
)(const Addr cond
)
271 if (DRD_(s_trace_cond
))
272 DRD_(trace_msg
)("[%u] cond_post_wait cond 0x%lx",
273 DRD_(thread_get_running_tid
)(), cond
);
275 p
= DRD_(cond_get
)(cond
);
278 CondDestrErrInfo cde
= {
279 DRD_(thread_get_running_tid
)(), cond
, 0, DRD_INVALID_THREADID
281 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
283 VG_(get_IP
)(VG_(get_running_tid
)()),
284 "condition variable has been destroyed while"
285 " being waited upon",
290 if (p
->waiter_count
> 0)
293 if (p
->waiter_count
== 0)
300 static void cond_signal(const DrdThreadId tid
, struct cond_info
* const cond_p
)
302 const ThreadId vg_tid
= VG_(get_running_tid
)();
303 const DrdThreadId drd_tid
= DRD_(VgThreadIdToDrdThreadId
)(vg_tid
);
307 if (cond_p
->waiter_count
> 0)
309 if (DRD_(s_report_signal_unlocked
)
310 && ! DRD_(mutex_is_locked_by
)(cond_p
->mutex
, drd_tid
))
313 * A signal is sent while the associated mutex has not been locked.
314 * This can indicate but is not necessarily a race condition.
316 CondRaceErrInfo cei
= { .tid
= DRD_(thread_get_running_tid
)(),
318 .mutex
= cond_p
->mutex
,
320 VG_(maybe_record_error
)(vg_tid
,
330 * No other thread is waiting for the signal, hence the signal will
331 * be lost. This is normal in a POSIX threads application.
336 static void not_initialized(Addr
const cond
)
338 CondErrInfo cei
= { .tid
= DRD_(thread_get_running_tid
)(), .cond
= cond
};
339 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
341 VG_(get_IP
)(VG_(get_running_tid
)()),
342 "condition variable has not been initialized",
346 /** Called before pthread_cond_signal(). */
347 void DRD_(cond_pre_signal
)(Addr
const cond
)
351 p
= DRD_(cond_get
)(cond
);
352 if (DRD_(s_trace_cond
))
353 DRD_(trace_msg
)("[%u] cond_signal cond 0x%lx",
354 DRD_(thread_get_running_tid
)(), cond
);
356 tl_assert(DRD_(pthread_cond_initializer
));
357 if (!p
&& VG_(memcmp
)((void*)cond
, (void*)DRD_(pthread_cond_initializer
),
358 DRD_(pthread_cond_initializer_size
)) != 0)
360 not_initialized(cond
);
365 p
= cond_get_or_allocate(cond
);
367 cond_signal(DRD_(thread_get_running_tid
)(), p
);
370 /** Called before pthread_cond_broadcast(). */
371 void DRD_(cond_pre_broadcast
)(Addr
const cond
)
375 if (DRD_(s_trace_cond
))
376 DRD_(trace_msg
)("[%u] cond_broadcast cond 0x%lx",
377 DRD_(thread_get_running_tid
)(), cond
);
379 p
= DRD_(cond_get
)(cond
);
380 tl_assert(DRD_(pthread_cond_initializer
));
381 if (!p
&& VG_(memcmp
)((void*)cond
, (void*)DRD_(pthread_cond_initializer
),
382 DRD_(pthread_cond_initializer_size
)) != 0)
384 not_initialized(cond
);
389 p
= cond_get_or_allocate(cond
);
391 cond_signal(DRD_(thread_get_running_tid
)(), p
);