tests/vg_regtest: Always evaluate prerequisite expressions with sh
[valgrind.git] / drd / drd_rwlock.c
blobdb589a0bed4d1269a8f5c28dd61ac87c891991b7
1 /*
2 This file is part of drd, a thread error detector.
4 Copyright (C) 2006-2013 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, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307, USA.
21 The GNU General Public License is contained in the file COPYING.
25 #include "drd_clientobj.h"
26 #include "drd_error.h"
27 #include "drd_rwlock.h"
28 #include "pub_tool_vki.h"
29 #include "pub_tool_errormgr.h" // VG_(maybe_record_error)()
30 #include "pub_tool_libcassert.h" // tl_assert()
31 #include "pub_tool_libcprint.h" // VG_(message)()
32 #include "pub_tool_libcproc.h" // VG_(read_millisecond_timer)()
33 #include "pub_tool_machine.h" // VG_(get_IP)()
34 #include "pub_tool_mallocfree.h" // VG_(malloc)(), VG_(free)()
35 #include "pub_tool_threadstate.h" // VG_(get_running_tid)()
38 /* Local type definitions. */
40 struct rwlock_thread_info
42 UWord tid; // DrdThreadId.
43 UInt reader_nesting_count;
44 UInt writer_nesting_count;
45 // Segment of last unlock call by this thread that unlocked a writer lock.
46 Segment* latest_wrlocked_segment;
47 // Segment of last unlock call by this thread that unlocked a reader lock.
48 Segment* latest_rdlocked_segment;
52 /* Local functions. */
54 static void rwlock_cleanup(struct rwlock_info* p);
55 static void rwlock_delete_thread(struct rwlock_info* const p,
56 const DrdThreadId tid);
59 /* Local variables. */
61 static Bool DRD_(s_trace_rwlock);
62 static UInt DRD_(s_exclusive_threshold_ms);
63 static UInt DRD_(s_shared_threshold_ms);
64 static ULong DRD_(s_rwlock_segment_creation_count);
67 /* Function definitions. */
69 void DRD_(rwlock_set_trace)(const Bool trace_rwlock)
71 tl_assert(trace_rwlock == False || trace_rwlock == True);
72 DRD_(s_trace_rwlock) = trace_rwlock;
75 void DRD_(rwlock_set_exclusive_threshold)(const UInt exclusive_threshold_ms)
77 DRD_(s_exclusive_threshold_ms) = exclusive_threshold_ms;
80 void DRD_(rwlock_set_shared_threshold)(const UInt shared_threshold_ms)
82 DRD_(s_shared_threshold_ms) = shared_threshold_ms;
85 static Bool DRD_(rwlock_is_rdlocked)(struct rwlock_info* p)
87 struct rwlock_thread_info* q;
89 VG_(OSetGen_ResetIter)(p->thread_info);
90 for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; )
92 return q->reader_nesting_count > 0;
94 return False;
97 static Bool DRD_(rwlock_is_wrlocked)(struct rwlock_info* p)
99 struct rwlock_thread_info* q;
101 VG_(OSetGen_ResetIter)(p->thread_info);
102 for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; )
104 return q->writer_nesting_count > 0;
106 return False;
109 static Bool DRD_(rwlock_is_locked)(struct rwlock_info* p)
111 return DRD_(rwlock_is_rdlocked)(p) || DRD_(rwlock_is_wrlocked)(p);
114 static Bool DRD_(rwlock_is_rdlocked_by)(struct rwlock_info* p,
115 const DrdThreadId tid)
117 const UWord uword_tid = tid;
118 struct rwlock_thread_info* q;
120 q = VG_(OSetGen_Lookup)(p->thread_info, &uword_tid);
121 return q && q->reader_nesting_count > 0;
124 static Bool DRD_(rwlock_is_wrlocked_by)(struct rwlock_info* p,
125 const DrdThreadId tid)
127 const UWord uword_tid = tid;
128 struct rwlock_thread_info* q;
130 q = VG_(OSetGen_Lookup)(p->thread_info, &uword_tid);
131 return q && q->writer_nesting_count > 0;
134 static Bool DRD_(rwlock_is_locked_by)(struct rwlock_info* p,
135 const DrdThreadId tid)
137 return (DRD_(rwlock_is_rdlocked_by)(p, tid)
138 || DRD_(rwlock_is_wrlocked_by)(p, tid));
141 /** Either look up or insert a node corresponding to DRD thread id 'tid'. */
142 static
143 struct rwlock_thread_info*
144 DRD_(lookup_or_insert_node)(OSet* oset, const UWord tid)
146 struct rwlock_thread_info* q;
148 q = VG_(OSetGen_Lookup)(oset, &tid);
149 if (q == 0)
151 q = VG_(OSetGen_AllocNode)(oset, sizeof(*q));
152 q->tid = tid;
153 q->reader_nesting_count = 0;
154 q->writer_nesting_count = 0;
155 q->latest_wrlocked_segment = 0;
156 q->latest_rdlocked_segment = 0;
157 VG_(OSetGen_Insert)(oset, q);
159 tl_assert(q);
160 return q;
164 * Combine the vector clock corresponding to the last unlock operation of
165 * reader-writer lock p into the vector clock of thread 'tid'.
167 static void DRD_(rwlock_combine_other_vc)(struct rwlock_info* const p,
168 const DrdThreadId tid,
169 const Bool readers_too)
171 struct rwlock_thread_info* q;
172 VectorClock old_vc;
174 DRD_(vc_copy)(&old_vc, DRD_(thread_get_vc)(tid));
175 VG_(OSetGen_ResetIter)(p->thread_info);
176 for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; ) {
177 if (q->tid != tid) {
178 if (q->latest_wrlocked_segment)
179 DRD_(vc_combine)(DRD_(thread_get_vc)(tid),
180 &q->latest_wrlocked_segment->vc);
181 if (readers_too && q->latest_rdlocked_segment)
182 DRD_(vc_combine)(DRD_(thread_get_vc)(tid),
183 &q->latest_rdlocked_segment->vc);
186 DRD_(thread_update_conflict_set)(tid, &old_vc);
187 DRD_(vc_cleanup)(&old_vc);
191 * Compare the type of the rwlock specified at initialization time with
192 * the type passed as an argument, and complain if these two types do not
193 * match.
195 static Bool drd_rwlock_check_type(struct rwlock_info* const p,
196 const RwLockT rwlock_type)
198 tl_assert(p);
199 /* The code below has to be updated if additional rwlock types are added. */
200 tl_assert(rwlock_type == pthread_rwlock || rwlock_type == user_rwlock);
201 tl_assert(p->rwlock_type == pthread_rwlock || p->rwlock_type == user_rwlock);
203 if (p->rwlock_type == rwlock_type)
204 return True;
207 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
208 VG_(maybe_record_error)
209 (VG_(get_running_tid)(),
210 RwlockErr,
211 VG_(get_IP)(VG_(get_running_tid)()),
212 rwlock_type == pthread_rwlock
213 ? "Attempt to use a user-defined rwlock as a POSIX rwlock"
214 : "Attempt to use a POSIX rwlock as a user-defined rwlock",
215 &REI);
217 return False;
220 /** Initialize the rwlock_info data structure *p. */
221 static
222 void DRD_(rwlock_initialize)(struct rwlock_info* const p, const Addr rwlock,
223 const RwLockT rwlock_type)
225 tl_assert(rwlock != 0);
226 tl_assert(p->a1 == rwlock);
227 tl_assert(p->type == ClientRwlock);
229 p->cleanup = (void(*)(DrdClientobj*))rwlock_cleanup;
230 p->delete_thread
231 = (void(*)(DrdClientobj*, DrdThreadId))rwlock_delete_thread;
232 p->rwlock_type = rwlock_type;
233 p->thread_info = VG_(OSetGen_Create)(
234 0, 0, VG_(malloc), "drd.rwlock.ri.1", VG_(free));
235 p->acquiry_time_ms = 0;
236 p->acquired_at = 0;
239 /** Deallocate the memory that was allocated by rwlock_initialize(). */
240 static void rwlock_cleanup(struct rwlock_info* p)
242 struct rwlock_thread_info* q;
244 tl_assert(p);
246 if (DRD_(s_trace_rwlock))
247 DRD_(trace_msg)("[%d] rwlock_destroy 0x%lx",
248 DRD_(thread_get_running_tid)(), p->a1);
250 if (DRD_(rwlock_is_locked)(p))
252 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
253 VG_(maybe_record_error)(VG_(get_running_tid)(),
254 RwlockErr,
255 VG_(get_IP)(VG_(get_running_tid)()),
256 "Destroying locked rwlock",
257 &REI);
260 VG_(OSetGen_ResetIter)(p->thread_info);
261 for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; )
263 DRD_(sg_put)(q->latest_wrlocked_segment);
264 DRD_(sg_put)(q->latest_rdlocked_segment);
267 VG_(OSetGen_Destroy)(p->thread_info);
270 static
271 struct rwlock_info*
272 DRD_(rwlock_get_or_allocate)(const Addr rwlock, const RwLockT rwlock_type)
274 struct rwlock_info* p;
276 tl_assert(offsetof(DrdClientobj, rwlock) == 0);
277 p = &(DRD_(clientobj_get)(rwlock, ClientRwlock)->rwlock);
278 if (p)
280 drd_rwlock_check_type(p, rwlock_type);
281 return p;
284 if (DRD_(clientobj_present)(rwlock, rwlock + 1))
286 GenericErrInfo GEI = {
287 .tid = DRD_(thread_get_running_tid)(),
288 .addr = rwlock,
290 VG_(maybe_record_error)(VG_(get_running_tid)(),
291 GenericErr,
292 VG_(get_IP)(VG_(get_running_tid)()),
293 "Not a reader-writer lock",
294 &GEI);
295 return 0;
298 p = &(DRD_(clientobj_add)(rwlock, ClientRwlock)->rwlock);
299 DRD_(rwlock_initialize)(p, rwlock, rwlock_type);
300 return p;
303 static struct rwlock_info* DRD_(rwlock_get)(const Addr rwlock)
305 tl_assert(offsetof(DrdClientobj, rwlock) == 0);
306 return &(DRD_(clientobj_get)(rwlock, ClientRwlock)->rwlock);
309 /** Called before pthread_rwlock_init(). */
310 struct rwlock_info* DRD_(rwlock_pre_init)(const Addr rwlock,
311 const RwLockT rwlock_type)
313 struct rwlock_info* p;
315 if (DRD_(s_trace_rwlock))
316 DRD_(trace_msg)("[%d] rwlock_init 0x%lx",
317 DRD_(thread_get_running_tid)(), rwlock);
319 p = DRD_(rwlock_get)(rwlock);
321 if (p)
322 drd_rwlock_check_type(p, rwlock_type);
324 if (p)
326 const ThreadId vg_tid = VG_(get_running_tid)();
327 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
328 VG_(maybe_record_error)(vg_tid,
329 RwlockErr,
330 VG_(get_IP)(vg_tid),
331 "Reader-writer lock reinitialization",
332 &REI);
333 return p;
336 p = DRD_(rwlock_get_or_allocate)(rwlock, rwlock_type);
338 return p;
341 /** Called after pthread_rwlock_destroy(). */
342 void DRD_(rwlock_post_destroy)(const Addr rwlock, const RwLockT rwlock_type)
344 struct rwlock_info* p;
346 p = DRD_(rwlock_get)(rwlock);
347 if (p == 0)
349 GenericErrInfo GEI = {
350 .tid = DRD_(thread_get_running_tid)(),
351 .addr = rwlock,
353 VG_(maybe_record_error)(VG_(get_running_tid)(),
354 GenericErr,
355 VG_(get_IP)(VG_(get_running_tid)()),
356 "Not a reader-writer lock",
357 &GEI);
358 return;
361 drd_rwlock_check_type(p, rwlock_type);
363 DRD_(clientobj_remove)(rwlock, ClientRwlock);
367 * Called before pthread_rwlock_rdlock() is invoked. If a data structure for
368 * the client-side object was not yet created, do this now. Also check whether
369 * an attempt is made to lock recursively a synchronization object that must
370 * not be locked recursively.
372 void DRD_(rwlock_pre_rdlock)(const Addr rwlock, const RwLockT rwlock_type)
374 struct rwlock_info* p;
376 if (DRD_(s_trace_rwlock))
377 DRD_(trace_msg)("[%d] pre_rwlock_rdlock 0x%lx",
378 DRD_(thread_get_running_tid)(), rwlock);
380 p = DRD_(rwlock_get_or_allocate)(rwlock, rwlock_type);
381 tl_assert(p);
383 if (DRD_(rwlock_is_wrlocked_by)(p, DRD_(thread_get_running_tid)())) {
384 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
385 VG_(maybe_record_error)(VG_(get_running_tid)(),
386 RwlockErr,
387 VG_(get_IP)(VG_(get_running_tid)()),
388 "Already locked for writing by calling thread",
389 &REI);
394 * Update rwlock_info state when locking the pthread_rwlock_t mutex.
395 * Note: this function must be called after pthread_rwlock_rdlock() has been
396 * called, or a race condition is triggered !
398 void DRD_(rwlock_post_rdlock)(const Addr rwlock, const RwLockT rwlock_type,
399 const Bool took_lock)
401 const DrdThreadId drd_tid = DRD_(thread_get_running_tid)();
402 struct rwlock_info* p;
403 struct rwlock_thread_info* q;
405 if (DRD_(s_trace_rwlock))
406 DRD_(trace_msg)("[%d] post_rwlock_rdlock 0x%lx", drd_tid, rwlock);
408 p = DRD_(rwlock_get)(rwlock);
410 if (! p || ! took_lock)
411 return;
413 tl_assert(! DRD_(rwlock_is_wrlocked)(p));
415 q = DRD_(lookup_or_insert_node)(p->thread_info, drd_tid);
416 if (++q->reader_nesting_count == 1)
418 DRD_(thread_new_segment)(drd_tid);
419 DRD_(s_rwlock_segment_creation_count)++;
420 DRD_(rwlock_combine_other_vc)(p, drd_tid, False);
422 p->acquiry_time_ms = VG_(read_millisecond_timer)();
423 p->acquired_at = VG_(record_ExeContext)(VG_(get_running_tid)(), 0);
428 * Called before pthread_rwlock_wrlock() is invoked. If a data structure for
429 * the client-side object was not yet created, do this now. Also check whether
430 * an attempt is made to lock recursively a synchronization object that must
431 * not be locked recursively.
433 void DRD_(rwlock_pre_wrlock)(const Addr rwlock, const RwLockT rwlock_type)
435 struct rwlock_info* p;
437 p = DRD_(rwlock_get)(rwlock);
439 if (DRD_(s_trace_rwlock))
440 DRD_(trace_msg)("[%d] pre_rwlock_wrlock 0x%lx",
441 DRD_(thread_get_running_tid)(), rwlock);
443 if (p == 0)
444 p = DRD_(rwlock_get_or_allocate)(rwlock, rwlock_type);
446 tl_assert(p);
448 if (DRD_(rwlock_is_wrlocked_by)(p, DRD_(thread_get_running_tid)()))
450 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
451 VG_(maybe_record_error)(VG_(get_running_tid)(),
452 RwlockErr,
453 VG_(get_IP)(VG_(get_running_tid)()),
454 "Recursive writer locking not allowed",
455 &REI);
460 * Update rwlock_info state when locking the pthread_rwlock_t rwlock.
461 * Note: this function must be called after pthread_rwlock_wrlock() has
462 * finished, or a race condition is triggered !
464 void DRD_(rwlock_post_wrlock)(const Addr rwlock, const RwLockT rwlock_type,
465 const Bool took_lock)
467 const DrdThreadId drd_tid = DRD_(thread_get_running_tid)();
468 struct rwlock_info* p;
469 struct rwlock_thread_info* q;
471 p = DRD_(rwlock_get)(rwlock);
473 if (DRD_(s_trace_rwlock))
474 DRD_(trace_msg)("[%d] post_rwlock_wrlock 0x%lx", drd_tid, rwlock);
476 if (! p || ! took_lock)
477 return;
479 q = DRD_(lookup_or_insert_node)(p->thread_info,
480 DRD_(thread_get_running_tid)());
481 tl_assert(q->writer_nesting_count == 0);
482 q->writer_nesting_count++;
483 tl_assert(q->writer_nesting_count == 1);
484 DRD_(thread_new_segment)(drd_tid);
485 DRD_(s_rwlock_segment_creation_count)++;
486 DRD_(rwlock_combine_other_vc)(p, drd_tid, True);
487 p->acquiry_time_ms = VG_(read_millisecond_timer)();
488 p->acquired_at = VG_(record_ExeContext)(VG_(get_running_tid)(), 0);
492 * Update rwlock_info state when unlocking the pthread_rwlock_t rwlock.
494 * @param rwlock Pointer to pthread_rwlock_t data structure in the client space.
496 * @return New value of the rwlock recursion count.
498 * @note This function must be called before pthread_rwlock_unlock() is called,
499 * or a race condition is triggered !
501 void DRD_(rwlock_pre_unlock)(const Addr rwlock, const RwLockT rwlock_type)
503 const DrdThreadId drd_tid = DRD_(thread_get_running_tid)();
504 const ThreadId vg_tid = VG_(get_running_tid)();
505 struct rwlock_info* p;
506 struct rwlock_thread_info* q;
508 if (DRD_(s_trace_rwlock))
509 DRD_(trace_msg)("[%d] rwlock_unlock 0x%lx", drd_tid, rwlock);
511 p = DRD_(rwlock_get)(rwlock);
512 if (p == 0)
514 GenericErrInfo GEI = {
515 .tid = DRD_(thread_get_running_tid)(),
516 .addr = rwlock,
518 VG_(maybe_record_error)(VG_(get_running_tid)(),
519 GenericErr,
520 VG_(get_IP)(VG_(get_running_tid)()),
521 "Not a reader-writer lock",
522 &GEI);
523 return;
526 drd_rwlock_check_type(p, rwlock_type);
528 if (! DRD_(rwlock_is_locked_by)(p, drd_tid))
530 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
531 VG_(maybe_record_error)(vg_tid,
532 RwlockErr,
533 VG_(get_IP)(vg_tid),
534 "Reader-writer lock not locked by calling thread",
535 &REI);
536 return;
538 q = DRD_(lookup_or_insert_node)(p->thread_info, drd_tid);
539 tl_assert(q);
540 if (q->reader_nesting_count > 0)
542 q->reader_nesting_count--;
543 if (q->reader_nesting_count == 0 && DRD_(s_shared_threshold_ms) > 0)
545 Long held = VG_(read_millisecond_timer)() - p->acquiry_time_ms;
546 if (held > DRD_(s_shared_threshold_ms))
548 HoldtimeErrInfo HEI
549 = { DRD_(thread_get_running_tid)(),
550 rwlock, p->acquired_at, held, DRD_(s_shared_threshold_ms) };
551 VG_(maybe_record_error)(vg_tid,
552 HoldtimeErr,
553 VG_(get_IP)(vg_tid),
554 "rwlock",
555 &HEI);
558 if (q->reader_nesting_count == 0 && q->writer_nesting_count == 0)
561 * This pthread_rwlock_unlock() call really unlocks the rwlock. Save
562 * the current vector clock of the thread such that it is available
563 * when this rwlock is locked again.
565 DRD_(thread_get_latest_segment)(&q->latest_rdlocked_segment, drd_tid);
566 DRD_(thread_new_segment)(drd_tid);
567 DRD_(s_rwlock_segment_creation_count)++;
570 else if (q->writer_nesting_count > 0)
572 q->writer_nesting_count--;
573 if (q->writer_nesting_count == 0 && DRD_(s_exclusive_threshold_ms) > 0)
575 Long held = VG_(read_millisecond_timer)() - p->acquiry_time_ms;
576 if (held > DRD_(s_exclusive_threshold_ms))
578 HoldtimeErrInfo HEI
579 = { DRD_(thread_get_running_tid)(),
580 rwlock, p->acquired_at, held,
581 DRD_(s_exclusive_threshold_ms) };
582 VG_(maybe_record_error)(vg_tid,
583 HoldtimeErr,
584 VG_(get_IP)(vg_tid),
585 "rwlock",
586 &HEI);
589 if (q->reader_nesting_count == 0 && q->writer_nesting_count == 0)
592 * This pthread_rwlock_unlock() call really unlocks the rwlock. Save
593 * the current vector clock of the thread such that it is available
594 * when this rwlock is locked again.
596 DRD_(thread_get_latest_segment)(&q->latest_wrlocked_segment, drd_tid);
597 DRD_(thread_new_segment)(drd_tid);
598 DRD_(s_rwlock_segment_creation_count)++;
601 else
603 tl_assert(False);
607 /** Called when thread tid stops to exist. */
608 static void rwlock_delete_thread(struct rwlock_info* const p,
609 const DrdThreadId tid)
611 struct rwlock_thread_info* q;
613 if (DRD_(rwlock_is_locked_by)(p, tid))
615 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
616 VG_(maybe_record_error)(VG_(get_running_tid)(),
617 RwlockErr,
618 VG_(get_IP)(VG_(get_running_tid)()),
619 "Reader-writer lock still locked at thread exit",
620 &REI);
621 q = DRD_(lookup_or_insert_node)(p->thread_info, tid);
622 q->reader_nesting_count = 0;
623 q->writer_nesting_count = 0;
627 ULong DRD_(get_rwlock_segment_creation_count)(void)
629 return DRD_(s_rwlock_segment_creation_count);