drd/tests/swapcontext: Improve the portability of this test further
[valgrind.git] / drd / drd_rwlock.c
blobe5f8457218ddd286500ecac7092f93c1fbe5c75f
1 /*
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;
92 return False;
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;
104 return False;
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'. */
140 static
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);
147 if (q == 0)
149 q = VG_(OSetGen_AllocNode)(oset, sizeof(*q));
150 q->tid = tid;
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);
157 tl_assert(q);
158 return 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;
170 VectorClock old_vc;
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; ) {
175 if (q->tid != tid) {
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
191 * match.
193 static Bool drd_rwlock_check_type(struct rwlock_info* const p,
194 const RwLockT rwlock_type)
196 tl_assert(p);
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)
202 return True;
205 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
206 VG_(maybe_record_error)
207 (VG_(get_running_tid)(),
208 RwlockErr,
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",
213 &REI);
215 return False;
218 /** Initialize the rwlock_info data structure *p. */
219 static
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;
228 p->delete_thread
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;
234 p->acquired_at = 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;
242 tl_assert(p);
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)(),
252 RwlockErr,
253 VG_(get_IP)(VG_(get_running_tid)()),
254 "Destroying locked rwlock",
255 &REI);
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);
268 static
269 struct rwlock_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);
276 if (p)
278 drd_rwlock_check_type(p, rwlock_type);
279 return p;
282 if (DRD_(clientobj_present)(rwlock, rwlock + 1))
284 GenericErrInfo GEI = {
285 .tid = DRD_(thread_get_running_tid)(),
286 .addr = rwlock,
288 VG_(maybe_record_error)(VG_(get_running_tid)(),
289 GenericErr,
290 VG_(get_IP)(VG_(get_running_tid)()),
291 "Not a reader-writer lock",
292 &GEI);
293 return 0;
296 p = &(DRD_(clientobj_add)(rwlock, ClientRwlock)->rwlock);
297 DRD_(rwlock_initialize)(p, rwlock, rwlock_type);
298 return p;
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);
319 if (p)
320 drd_rwlock_check_type(p, rwlock_type);
322 if (p)
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,
327 RwlockErr,
328 VG_(get_IP)(vg_tid),
329 "Reader-writer lock reinitialization",
330 &REI);
331 return p;
334 p = DRD_(rwlock_get_or_allocate)(rwlock, rwlock_type);
336 return p;
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);
345 if (p == 0)
347 GenericErrInfo GEI = {
348 .tid = DRD_(thread_get_running_tid)(),
349 .addr = rwlock,
351 VG_(maybe_record_error)(VG_(get_running_tid)(),
352 GenericErr,
353 VG_(get_IP)(VG_(get_running_tid)()),
354 "Not a reader-writer lock",
355 &GEI);
356 return;
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);
379 tl_assert(p);
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)(),
384 RwlockErr,
385 VG_(get_IP)(VG_(get_running_tid)()),
386 "Already locked for writing by calling thread",
387 &REI);
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)
409 return;
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);
441 if (p == 0)
442 p = DRD_(rwlock_get_or_allocate)(rwlock, rwlock_type);
444 tl_assert(p);
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)(),
450 RwlockErr,
451 VG_(get_IP)(VG_(get_running_tid)()),
452 "Recursive writer locking not allowed",
453 &REI);
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)
475 return;
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);
510 if (p == 0)
512 GenericErrInfo GEI = {
513 .tid = DRD_(thread_get_running_tid)(),
514 .addr = rwlock,
516 VG_(maybe_record_error)(VG_(get_running_tid)(),
517 GenericErr,
518 VG_(get_IP)(VG_(get_running_tid)()),
519 "Not a reader-writer lock",
520 &GEI);
521 return;
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,
530 RwlockErr,
531 VG_(get_IP)(vg_tid),
532 "Reader-writer lock not locked by calling thread",
533 &REI);
534 return;
536 q = DRD_(lookup_or_insert_node)(p->thread_info, drd_tid);
537 tl_assert(q);
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))
546 HoldtimeErrInfo HEI
547 = { DRD_(thread_get_running_tid)(),
548 rwlock, p->acquired_at, held, DRD_(s_shared_threshold_ms) };
549 VG_(maybe_record_error)(vg_tid,
550 HoldtimeErr,
551 VG_(get_IP)(vg_tid),
552 "rwlock",
553 &HEI);
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))
576 HoldtimeErrInfo HEI
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,
581 HoldtimeErr,
582 VG_(get_IP)(vg_tid),
583 "rwlock",
584 &HEI);
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)++;
599 else
601 tl_assert(False);
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)(),
615 RwlockErr,
616 VG_(get_IP)(VG_(get_running_tid)()),
617 "Reader-writer lock still locked at thread exit",
618 &REI);
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);