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
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;
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;
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'. */
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
);
151 q
= VG_(OSetGen_AllocNode
)(oset
, sizeof(*q
));
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
);
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
;
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; ) {
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
195 static Bool
drd_rwlock_check_type(struct rwlock_info
* const p
,
196 const RwLockT rwlock_type
)
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
)
207 RwlockErrInfo REI
= { DRD_(thread_get_running_tid
)(), p
->a1
};
208 VG_(maybe_record_error
)
209 (VG_(get_running_tid
)(),
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",
220 /** Initialize the rwlock_info data structure *p. */
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
;
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;
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
;
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
)(),
255 VG_(get_IP
)(VG_(get_running_tid
)()),
256 "Destroying locked rwlock",
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
);
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
);
280 drd_rwlock_check_type(p
, rwlock_type
);
284 if (DRD_(clientobj_present
)(rwlock
, rwlock
+ 1))
286 GenericErrInfo GEI
= {
287 .tid
= DRD_(thread_get_running_tid
)(),
290 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
292 VG_(get_IP
)(VG_(get_running_tid
)()),
293 "Not a reader-writer lock",
298 p
= &(DRD_(clientobj_add
)(rwlock
, ClientRwlock
)->rwlock
);
299 DRD_(rwlock_initialize
)(p
, rwlock
, rwlock_type
);
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
);
322 drd_rwlock_check_type(p
, rwlock_type
);
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
,
331 "Reader-writer lock reinitialization",
336 p
= DRD_(rwlock_get_or_allocate
)(rwlock
, rwlock_type
);
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
);
349 GenericErrInfo GEI
= {
350 .tid
= DRD_(thread_get_running_tid
)(),
353 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
355 VG_(get_IP
)(VG_(get_running_tid
)()),
356 "Not a reader-writer lock",
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
);
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
)(),
387 VG_(get_IP
)(VG_(get_running_tid
)()),
388 "Already locked for writing by calling thread",
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
)
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
);
444 p
= DRD_(rwlock_get_or_allocate
)(rwlock
, rwlock_type
);
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
)(),
453 VG_(get_IP
)(VG_(get_running_tid
)()),
454 "Recursive writer locking not allowed",
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
)
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
);
514 GenericErrInfo GEI
= {
515 .tid
= DRD_(thread_get_running_tid
)(),
518 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
520 VG_(get_IP
)(VG_(get_running_tid
)()),
521 "Not a reader-writer lock",
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
,
534 "Reader-writer lock not locked by calling thread",
538 q
= DRD_(lookup_or_insert_node
)(p
->thread_info
, drd_tid
);
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
))
549 = { DRD_(thread_get_running_tid
)(),
550 rwlock
, p
->acquired_at
, held
, DRD_(s_shared_threshold_ms
) };
551 VG_(maybe_record_error
)(vg_tid
,
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
))
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
,
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
)++;
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
)(),
618 VG_(get_IP
)(VG_(get_running_tid
)()),
619 "Reader-writer lock still locked at thread exit",
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
);