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"
24 #include "drd_error.h"
25 #include "drd_rwlock.h"
26 #include "pub_tool_vki.h"
27 #include "pub_tool_errormgr.h" // VG_(maybe_record_error)()
28 #include "pub_tool_libcassert.h" // tl_assert()
29 #include "pub_tool_libcprint.h" // VG_(message)()
30 #include "pub_tool_libcproc.h" // VG_(read_millisecond_timer)()
31 #include "pub_tool_machine.h" // VG_(get_IP)()
32 #include "pub_tool_mallocfree.h" // VG_(malloc)(), VG_(free)()
33 #include "pub_tool_threadstate.h" // VG_(get_running_tid)()
36 /* Local type definitions. */
38 struct rwlock_thread_info
40 UWord tid
; // DrdThreadId.
41 UInt reader_nesting_count
;
42 UInt writer_nesting_count
;
43 // Segment of last unlock call by this thread that unlocked a writer lock.
44 Segment
* latest_wrlocked_segment
;
45 // Segment of last unlock call by this thread that unlocked a reader lock.
46 Segment
* latest_rdlocked_segment
;
50 /* Local functions. */
52 static void rwlock_cleanup(struct rwlock_info
* p
);
53 static void rwlock_delete_thread(struct rwlock_info
* const p
,
54 const DrdThreadId tid
);
57 /* Local variables. */
59 static Bool
DRD_(s_trace_rwlock
);
60 static UInt
DRD_(s_exclusive_threshold_ms
);
61 static UInt
DRD_(s_shared_threshold_ms
);
62 static ULong
DRD_(s_rwlock_segment_creation_count
);
65 /* Function definitions. */
67 void DRD_(rwlock_set_trace
)(const Bool trace_rwlock
)
69 tl_assert(trace_rwlock
== False
|| trace_rwlock
== True
);
70 DRD_(s_trace_rwlock
) = trace_rwlock
;
73 void DRD_(rwlock_set_exclusive_threshold
)(const UInt exclusive_threshold_ms
)
75 DRD_(s_exclusive_threshold_ms
) = exclusive_threshold_ms
;
78 void DRD_(rwlock_set_shared_threshold
)(const UInt shared_threshold_ms
)
80 DRD_(s_shared_threshold_ms
) = shared_threshold_ms
;
83 static Bool
DRD_(rwlock_is_rdlocked
)(struct rwlock_info
* p
)
85 struct rwlock_thread_info
* q
;
87 VG_(OSetGen_ResetIter
)(p
->thread_info
);
88 for ( ; (q
= VG_(OSetGen_Next
)(p
->thread_info
)) != 0; )
90 return q
->reader_nesting_count
> 0;
95 static Bool
DRD_(rwlock_is_wrlocked
)(struct rwlock_info
* p
)
97 struct rwlock_thread_info
* q
;
99 VG_(OSetGen_ResetIter
)(p
->thread_info
);
100 for ( ; (q
= VG_(OSetGen_Next
)(p
->thread_info
)) != 0; )
102 return q
->writer_nesting_count
> 0;
107 static Bool
DRD_(rwlock_is_locked
)(struct rwlock_info
* p
)
109 return DRD_(rwlock_is_rdlocked
)(p
) || DRD_(rwlock_is_wrlocked
)(p
);
112 static Bool
DRD_(rwlock_is_rdlocked_by
)(struct rwlock_info
* p
,
113 const DrdThreadId tid
)
115 const UWord uword_tid
= tid
;
116 struct rwlock_thread_info
* q
;
118 q
= VG_(OSetGen_Lookup
)(p
->thread_info
, &uword_tid
);
119 return q
&& q
->reader_nesting_count
> 0;
122 static Bool
DRD_(rwlock_is_wrlocked_by
)(struct rwlock_info
* p
,
123 const DrdThreadId tid
)
125 const UWord uword_tid
= tid
;
126 struct rwlock_thread_info
* q
;
128 q
= VG_(OSetGen_Lookup
)(p
->thread_info
, &uword_tid
);
129 return q
&& q
->writer_nesting_count
> 0;
132 static Bool
DRD_(rwlock_is_locked_by
)(struct rwlock_info
* p
,
133 const DrdThreadId tid
)
135 return (DRD_(rwlock_is_rdlocked_by
)(p
, tid
)
136 || DRD_(rwlock_is_wrlocked_by
)(p
, tid
));
139 /** Either look up or insert a node corresponding to DRD thread id 'tid'. */
141 struct rwlock_thread_info
*
142 DRD_(lookup_or_insert_node
)(OSet
* oset
, const UWord tid
)
144 struct rwlock_thread_info
* q
;
146 q
= VG_(OSetGen_Lookup
)(oset
, &tid
);
149 q
= VG_(OSetGen_AllocNode
)(oset
, sizeof(*q
));
151 q
->reader_nesting_count
= 0;
152 q
->writer_nesting_count
= 0;
153 q
->latest_wrlocked_segment
= 0;
154 q
->latest_rdlocked_segment
= 0;
155 VG_(OSetGen_Insert
)(oset
, q
);
162 * Combine the vector clock corresponding to the last unlock operation of
163 * reader-writer lock p into the vector clock of thread 'tid'.
165 static void DRD_(rwlock_combine_other_vc
)(struct rwlock_info
* const p
,
166 const DrdThreadId tid
,
167 const Bool readers_too
)
169 struct rwlock_thread_info
* q
;
172 DRD_(vc_copy
)(&old_vc
, DRD_(thread_get_vc
)(tid
));
173 VG_(OSetGen_ResetIter
)(p
->thread_info
);
174 for ( ; (q
= VG_(OSetGen_Next
)(p
->thread_info
)) != 0; ) {
176 if (q
->latest_wrlocked_segment
)
177 DRD_(vc_combine
)(DRD_(thread_get_vc
)(tid
),
178 &q
->latest_wrlocked_segment
->vc
);
179 if (readers_too
&& q
->latest_rdlocked_segment
)
180 DRD_(vc_combine
)(DRD_(thread_get_vc
)(tid
),
181 &q
->latest_rdlocked_segment
->vc
);
184 DRD_(thread_update_conflict_set
)(tid
, &old_vc
);
185 DRD_(vc_cleanup
)(&old_vc
);
189 * Compare the type of the rwlock specified at initialization time with
190 * the type passed as an argument, and complain if these two types do not
193 static Bool
drd_rwlock_check_type(struct rwlock_info
* const p
,
194 const RwLockT rwlock_type
)
197 /* The code below has to be updated if additional rwlock types are added. */
198 tl_assert(rwlock_type
== pthread_rwlock
|| rwlock_type
== user_rwlock
);
199 tl_assert(p
->rwlock_type
== pthread_rwlock
|| p
->rwlock_type
== user_rwlock
);
201 if (p
->rwlock_type
== rwlock_type
)
205 RwlockErrInfo REI
= { DRD_(thread_get_running_tid
)(), p
->a1
};
206 VG_(maybe_record_error
)
207 (VG_(get_running_tid
)(),
209 VG_(get_IP
)(VG_(get_running_tid
)()),
210 rwlock_type
== pthread_rwlock
211 ? "Attempt to use a user-defined rwlock as a POSIX rwlock"
212 : "Attempt to use a POSIX rwlock as a user-defined rwlock",
218 /** Initialize the rwlock_info data structure *p. */
220 void DRD_(rwlock_initialize
)(struct rwlock_info
* const p
, const Addr rwlock
,
221 const RwLockT rwlock_type
)
223 tl_assert(rwlock
!= 0);
224 tl_assert(p
->a1
== rwlock
);
225 tl_assert(p
->type
== ClientRwlock
);
227 p
->cleanup
= (void(*)(DrdClientobj
*))rwlock_cleanup
;
229 = (void(*)(DrdClientobj
*, DrdThreadId
))rwlock_delete_thread
;
230 p
->rwlock_type
= rwlock_type
;
231 p
->thread_info
= VG_(OSetGen_Create
)(
232 0, 0, VG_(malloc
), "drd.rwlock.ri.1", VG_(free
));
233 p
->acquiry_time_ms
= 0;
237 /** Deallocate the memory that was allocated by rwlock_initialize(). */
238 static void rwlock_cleanup(struct rwlock_info
* p
)
240 struct rwlock_thread_info
* q
;
244 if (DRD_(s_trace_rwlock
))
245 DRD_(trace_msg
)("[%u] rwlock_destroy 0x%lx",
246 DRD_(thread_get_running_tid
)(), p
->a1
);
248 if (DRD_(rwlock_is_locked
)(p
))
250 RwlockErrInfo REI
= { DRD_(thread_get_running_tid
)(), p
->a1
};
251 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
253 VG_(get_IP
)(VG_(get_running_tid
)()),
254 "Destroying locked rwlock",
258 VG_(OSetGen_ResetIter
)(p
->thread_info
);
259 for ( ; (q
= VG_(OSetGen_Next
)(p
->thread_info
)) != 0; )
261 DRD_(sg_put
)(q
->latest_wrlocked_segment
);
262 DRD_(sg_put
)(q
->latest_rdlocked_segment
);
265 VG_(OSetGen_Destroy
)(p
->thread_info
);
270 DRD_(rwlock_get_or_allocate
)(const Addr rwlock
, const RwLockT rwlock_type
)
272 struct rwlock_info
* p
;
274 tl_assert(offsetof(DrdClientobj
, rwlock
) == 0);
275 p
= &(DRD_(clientobj_get
)(rwlock
, ClientRwlock
)->rwlock
);
278 drd_rwlock_check_type(p
, rwlock_type
);
282 if (DRD_(clientobj_present
)(rwlock
, rwlock
+ 1))
284 GenericErrInfo GEI
= {
285 .tid
= DRD_(thread_get_running_tid
)(),
288 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
290 VG_(get_IP
)(VG_(get_running_tid
)()),
291 "Not a reader-writer lock",
296 p
= &(DRD_(clientobj_add
)(rwlock
, ClientRwlock
)->rwlock
);
297 DRD_(rwlock_initialize
)(p
, rwlock
, rwlock_type
);
301 static struct rwlock_info
* DRD_(rwlock_get
)(const Addr rwlock
)
303 tl_assert(offsetof(DrdClientobj
, rwlock
) == 0);
304 return &(DRD_(clientobj_get
)(rwlock
, ClientRwlock
)->rwlock
);
307 /** Called before pthread_rwlock_init(). */
308 struct rwlock_info
* DRD_(rwlock_pre_init
)(const Addr rwlock
,
309 const RwLockT rwlock_type
)
311 struct rwlock_info
* p
;
313 if (DRD_(s_trace_rwlock
))
314 DRD_(trace_msg
)("[%u] rwlock_init 0x%lx",
315 DRD_(thread_get_running_tid
)(), rwlock
);
317 p
= DRD_(rwlock_get
)(rwlock
);
320 drd_rwlock_check_type(p
, rwlock_type
);
324 const ThreadId vg_tid
= VG_(get_running_tid
)();
325 RwlockErrInfo REI
= { DRD_(thread_get_running_tid
)(), p
->a1
};
326 VG_(maybe_record_error
)(vg_tid
,
329 "Reader-writer lock reinitialization",
334 p
= DRD_(rwlock_get_or_allocate
)(rwlock
, rwlock_type
);
339 /** Called after pthread_rwlock_destroy(). */
340 void DRD_(rwlock_post_destroy
)(const Addr rwlock
, const RwLockT rwlock_type
)
342 struct rwlock_info
* p
;
344 p
= DRD_(rwlock_get
)(rwlock
);
347 GenericErrInfo GEI
= {
348 .tid
= DRD_(thread_get_running_tid
)(),
351 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
353 VG_(get_IP
)(VG_(get_running_tid
)()),
354 "Not a reader-writer lock",
359 drd_rwlock_check_type(p
, rwlock_type
);
361 DRD_(clientobj_remove
)(rwlock
, ClientRwlock
);
365 * Called before pthread_rwlock_rdlock() is invoked. If a data structure for
366 * the client-side object was not yet created, do this now. Also check whether
367 * an attempt is made to lock recursively a synchronization object that must
368 * not be locked recursively.
370 void DRD_(rwlock_pre_rdlock
)(const Addr rwlock
, const RwLockT rwlock_type
)
372 struct rwlock_info
* p
;
374 if (DRD_(s_trace_rwlock
))
375 DRD_(trace_msg
)("[%u] pre_rwlock_rdlock 0x%lx",
376 DRD_(thread_get_running_tid
)(), rwlock
);
378 p
= DRD_(rwlock_get_or_allocate
)(rwlock
, rwlock_type
);
381 if (DRD_(rwlock_is_wrlocked_by
)(p
, DRD_(thread_get_running_tid
)())) {
382 RwlockErrInfo REI
= { DRD_(thread_get_running_tid
)(), p
->a1
};
383 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
385 VG_(get_IP
)(VG_(get_running_tid
)()),
386 "Already locked for writing by calling thread",
392 * Update rwlock_info state when locking the pthread_rwlock_t mutex.
393 * Note: this function must be called after pthread_rwlock_rdlock() has been
394 * called, or a race condition is triggered !
396 void DRD_(rwlock_post_rdlock
)(const Addr rwlock
, const RwLockT rwlock_type
,
397 const Bool took_lock
)
399 const DrdThreadId drd_tid
= DRD_(thread_get_running_tid
)();
400 struct rwlock_info
* p
;
401 struct rwlock_thread_info
* q
;
403 if (DRD_(s_trace_rwlock
))
404 DRD_(trace_msg
)("[%u] post_rwlock_rdlock 0x%lx", drd_tid
, rwlock
);
406 p
= DRD_(rwlock_get
)(rwlock
);
408 if (! p
|| ! took_lock
)
411 tl_assert(! DRD_(rwlock_is_wrlocked
)(p
));
413 q
= DRD_(lookup_or_insert_node
)(p
->thread_info
, drd_tid
);
414 if (++q
->reader_nesting_count
== 1)
416 DRD_(thread_new_segment
)(drd_tid
);
417 DRD_(s_rwlock_segment_creation_count
)++;
418 DRD_(rwlock_combine_other_vc
)(p
, drd_tid
, False
);
420 p
->acquiry_time_ms
= VG_(read_millisecond_timer
)();
421 p
->acquired_at
= VG_(record_ExeContext
)(VG_(get_running_tid
)(), 0);
426 * Called before pthread_rwlock_wrlock() is invoked. If a data structure for
427 * the client-side object was not yet created, do this now. Also check whether
428 * an attempt is made to lock recursively a synchronization object that must
429 * not be locked recursively.
431 void DRD_(rwlock_pre_wrlock
)(const Addr rwlock
, const RwLockT rwlock_type
)
433 struct rwlock_info
* p
;
435 p
= DRD_(rwlock_get
)(rwlock
);
437 if (DRD_(s_trace_rwlock
))
438 DRD_(trace_msg
)("[%u] pre_rwlock_wrlock 0x%lx",
439 DRD_(thread_get_running_tid
)(), rwlock
);
442 p
= DRD_(rwlock_get_or_allocate
)(rwlock
, rwlock_type
);
446 if (DRD_(rwlock_is_wrlocked_by
)(p
, DRD_(thread_get_running_tid
)()))
448 RwlockErrInfo REI
= { DRD_(thread_get_running_tid
)(), p
->a1
};
449 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
451 VG_(get_IP
)(VG_(get_running_tid
)()),
452 "Recursive writer locking not allowed",
458 * Update rwlock_info state when locking the pthread_rwlock_t rwlock.
459 * Note: this function must be called after pthread_rwlock_wrlock() has
460 * finished, or a race condition is triggered !
462 void DRD_(rwlock_post_wrlock
)(const Addr rwlock
, const RwLockT rwlock_type
,
463 const Bool took_lock
)
465 const DrdThreadId drd_tid
= DRD_(thread_get_running_tid
)();
466 struct rwlock_info
* p
;
467 struct rwlock_thread_info
* q
;
469 p
= DRD_(rwlock_get
)(rwlock
);
471 if (DRD_(s_trace_rwlock
))
472 DRD_(trace_msg
)("[%u] post_rwlock_wrlock 0x%lx", drd_tid
, rwlock
);
474 if (! p
|| ! took_lock
)
477 q
= DRD_(lookup_or_insert_node
)(p
->thread_info
,
478 DRD_(thread_get_running_tid
)());
479 tl_assert(q
->writer_nesting_count
== 0);
480 q
->writer_nesting_count
++;
481 tl_assert(q
->writer_nesting_count
== 1);
482 DRD_(thread_new_segment
)(drd_tid
);
483 DRD_(s_rwlock_segment_creation_count
)++;
484 DRD_(rwlock_combine_other_vc
)(p
, drd_tid
, True
);
485 p
->acquiry_time_ms
= VG_(read_millisecond_timer
)();
486 p
->acquired_at
= VG_(record_ExeContext
)(VG_(get_running_tid
)(), 0);
490 * Update rwlock_info state when unlocking the pthread_rwlock_t rwlock.
492 * @param rwlock Pointer to pthread_rwlock_t data structure in the client space.
494 * @return New value of the rwlock recursion count.
496 * @note This function must be called before pthread_rwlock_unlock() is called,
497 * or a race condition is triggered !
499 void DRD_(rwlock_pre_unlock
)(const Addr rwlock
, const RwLockT rwlock_type
)
501 const DrdThreadId drd_tid
= DRD_(thread_get_running_tid
)();
502 const ThreadId vg_tid
= VG_(get_running_tid
)();
503 struct rwlock_info
* p
;
504 struct rwlock_thread_info
* q
;
506 if (DRD_(s_trace_rwlock
))
507 DRD_(trace_msg
)("[%u] rwlock_unlock 0x%lx", drd_tid
, rwlock
);
509 p
= DRD_(rwlock_get
)(rwlock
);
512 GenericErrInfo GEI
= {
513 .tid
= DRD_(thread_get_running_tid
)(),
516 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
518 VG_(get_IP
)(VG_(get_running_tid
)()),
519 "Not a reader-writer lock",
524 drd_rwlock_check_type(p
, rwlock_type
);
526 if (! DRD_(rwlock_is_locked_by
)(p
, drd_tid
))
528 RwlockErrInfo REI
= { DRD_(thread_get_running_tid
)(), p
->a1
};
529 VG_(maybe_record_error
)(vg_tid
,
532 "Reader-writer lock not locked by calling thread",
536 q
= DRD_(lookup_or_insert_node
)(p
->thread_info
, drd_tid
);
538 if (q
->reader_nesting_count
> 0)
540 q
->reader_nesting_count
--;
541 if (q
->reader_nesting_count
== 0 && DRD_(s_shared_threshold_ms
) > 0)
543 Long held
= VG_(read_millisecond_timer
)() - p
->acquiry_time_ms
;
544 if (held
> DRD_(s_shared_threshold_ms
))
547 = { DRD_(thread_get_running_tid
)(),
548 rwlock
, p
->acquired_at
, held
, DRD_(s_shared_threshold_ms
) };
549 VG_(maybe_record_error
)(vg_tid
,
556 if (q
->reader_nesting_count
== 0 && q
->writer_nesting_count
== 0)
559 * This pthread_rwlock_unlock() call really unlocks the rwlock. Save
560 * the current vector clock of the thread such that it is available
561 * when this rwlock is locked again.
563 DRD_(thread_get_latest_segment
)(&q
->latest_rdlocked_segment
, drd_tid
);
564 DRD_(thread_new_segment
)(drd_tid
);
565 DRD_(s_rwlock_segment_creation_count
)++;
568 else if (q
->writer_nesting_count
> 0)
570 q
->writer_nesting_count
--;
571 if (q
->writer_nesting_count
== 0 && DRD_(s_exclusive_threshold_ms
) > 0)
573 Long held
= VG_(read_millisecond_timer
)() - p
->acquiry_time_ms
;
574 if (held
> DRD_(s_exclusive_threshold_ms
))
577 = { DRD_(thread_get_running_tid
)(),
578 rwlock
, p
->acquired_at
, held
,
579 DRD_(s_exclusive_threshold_ms
) };
580 VG_(maybe_record_error
)(vg_tid
,
587 if (q
->reader_nesting_count
== 0 && q
->writer_nesting_count
== 0)
590 * This pthread_rwlock_unlock() call really unlocks the rwlock. Save
591 * the current vector clock of the thread such that it is available
592 * when this rwlock is locked again.
594 DRD_(thread_get_latest_segment
)(&q
->latest_wrlocked_segment
, drd_tid
);
595 DRD_(thread_new_segment
)(drd_tid
);
596 DRD_(s_rwlock_segment_creation_count
)++;
605 /** Called when thread tid stops to exist. */
606 static void rwlock_delete_thread(struct rwlock_info
* const p
,
607 const DrdThreadId tid
)
609 struct rwlock_thread_info
* q
;
611 if (DRD_(rwlock_is_locked_by
)(p
, tid
))
613 RwlockErrInfo REI
= { DRD_(thread_get_running_tid
)(), p
->a1
};
614 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
616 VG_(get_IP
)(VG_(get_running_tid
)()),
617 "Reader-writer lock still locked at thread exit",
619 q
= DRD_(lookup_or_insert_node
)(p
->thread_info
, tid
);
620 q
->reader_nesting_count
= 0;
621 q
->writer_nesting_count
= 0;
625 ULong
DRD_(get_rwlock_segment_creation_count
)(void)
627 return DRD_(s_rwlock_segment_creation_count
);