blob: 46ac1999ca41dc02904d58bb198a11b9010c6acc [file] [log] [blame]
bart777f7fe2008-03-02 17:43:18 +00001/*
2 This file is part of drd, a data race detector.
3
4 Copyright (C) 2006-2008 Bart Van Assche
5 bart.vanassche@gmail.com
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307, USA.
21
22 The GNU General Public License is contained in the file COPYING.
23*/
24
25
26#include "drd_clientobj.h"
27#include "drd_error.h"
28#include "drd_rwlock.h"
29#include "priv_drd_clientreq.h"
30#include "pub_tool_errormgr.h" // VG_(maybe_record_error)()
31#include "pub_tool_libcassert.h" // tl_assert()
32#include "pub_tool_libcprint.h" // VG_(message)()
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)()
36
37
38// Type definitions.
39
40struct rwlock_thread_info
41{
barta2b6e1b2008-03-17 18:32:39 +000042 UWord tid; // DrdThreadId.
43 UInt reader_nesting_count;
44 UInt writer_nesting_count;
45 Segment* last_unlock_segment; // Segment of last unlock call by this thread.
46 Bool last_lock_was_writer_lock;
bart777f7fe2008-03-02 17:43:18 +000047};
48
49
50// Local functions.
51
52static void rwlock_cleanup(struct rwlock_info* p);
bart6bbefaf2008-04-19 15:16:45 +000053static ULong s_rwlock_segment_creation_count;
bart777f7fe2008-03-02 17:43:18 +000054
55
56// Local variables.
57
58static Bool s_trace_rwlock;
59
60
61// Function definitions.
62
63void rwlock_set_trace(const Bool trace_rwlock)
64{
65 tl_assert(!! trace_rwlock == trace_rwlock);
66 s_trace_rwlock = trace_rwlock;
67}
68
69static Bool rwlock_is_rdlocked(struct rwlock_info* p)
70{
71 struct rwlock_thread_info* q;
72
73 VG_(OSetGen_ResetIter)(p->thread_info);
74 for ( ; (q = VG_(OSetGen_Next)(p->thread_info)); q++)
75 {
76 return q->reader_nesting_count > 0;
77 }
78 return False;
79}
80
81static Bool rwlock_is_wrlocked(struct rwlock_info* p)
82{
83 struct rwlock_thread_info* q;
84
85 VG_(OSetGen_ResetIter)(p->thread_info);
86 for ( ; (q = VG_(OSetGen_Next)(p->thread_info)); q++)
87 {
88 return q->writer_nesting_count > 0;
89 }
90 return False;
91}
92
93static Bool rwlock_is_locked(struct rwlock_info* p)
94{
95 return rwlock_is_rdlocked(p) || rwlock_is_wrlocked(p);
96}
97
98static Bool rwlock_is_rdlocked_by(struct rwlock_info* p, const DrdThreadId tid)
99{
100 const UWord uword_tid = tid;
101 struct rwlock_thread_info* q;
102
103 q = VG_(OSetGen_Lookup)(p->thread_info, &uword_tid);
104 return q && q->reader_nesting_count > 0;
105}
106
107static Bool rwlock_is_wrlocked_by(struct rwlock_info* p, const DrdThreadId tid)
108{
109 const UWord uword_tid = tid;
110 struct rwlock_thread_info* q;
111
112 q = VG_(OSetGen_Lookup)(p->thread_info, &uword_tid);
113 return q && q->writer_nesting_count > 0;
114}
115
116static Bool rwlock_is_locked_by(struct rwlock_info* p, const DrdThreadId tid)
117{
118 return rwlock_is_rdlocked_by(p, tid) || rwlock_is_wrlocked_by(p, tid);
119}
120
bart165b90f2008-05-10 12:54:27 +0000121/** Either look up or insert a node corresponding to DRD thread id 'tid'. */
bart777f7fe2008-03-02 17:43:18 +0000122static
123struct rwlock_thread_info* lookup_or_insert_node(OSet* oset, const UWord tid)
124{
125 struct rwlock_thread_info* q;
126
127 q = VG_(OSetGen_Lookup)(oset, &tid);
128 if (q == 0)
129 {
130 q = VG_(OSetGen_AllocNode)(oset, sizeof(*q));
bart165b90f2008-05-10 12:54:27 +0000131 q->tid = tid;
132 q->reader_nesting_count = 0;
133 q->writer_nesting_count = 0;
134 q->last_unlock_segment = 0;
bart5bd9f2d2008-03-03 20:31:58 +0000135 q->last_lock_was_writer_lock = False;
bart777f7fe2008-03-02 17:43:18 +0000136 VG_(OSetGen_Insert)(oset, q);
137 }
138 tl_assert(q);
139 return q;
140}
141
bart165b90f2008-05-10 12:54:27 +0000142/** Combine the vector clock corresponding to the last unlock operation of
143 * reader-writer lock p into the vector clock of thread 'tid'.
144 */
bart777f7fe2008-03-02 17:43:18 +0000145static void rwlock_combine_other_vc(struct rwlock_info* const p,
bart5bd9f2d2008-03-03 20:31:58 +0000146 const DrdThreadId tid,
147 const Bool readers_too)
bart777f7fe2008-03-02 17:43:18 +0000148{
149 struct rwlock_thread_info* q;
150
151 VG_(OSetGen_ResetIter)(p->thread_info);
152 for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; )
153 {
bart5bd9f2d2008-03-03 20:31:58 +0000154 if (q->tid != tid && (readers_too || q->last_lock_was_writer_lock))
bart777f7fe2008-03-02 17:43:18 +0000155 {
barta2b6e1b2008-03-17 18:32:39 +0000156 thread_combine_vc2(tid, &q->last_unlock_segment->vc);
bart777f7fe2008-03-02 17:43:18 +0000157 }
158 }
159}
160
bart165b90f2008-05-10 12:54:27 +0000161/** Initialize the rwlock_info data structure *p. */
bart777f7fe2008-03-02 17:43:18 +0000162static
bart0268dfa2008-03-11 20:10:21 +0000163void rwlock_initialize(struct rwlock_info* const p, const Addr rwlock)
bart777f7fe2008-03-02 17:43:18 +0000164{
165 tl_assert(rwlock != 0);
bart777f7fe2008-03-02 17:43:18 +0000166 tl_assert(p->a1 == rwlock);
bart777f7fe2008-03-02 17:43:18 +0000167 tl_assert(p->type == ClientRwlock);
168
169 p->cleanup = (void(*)(DrdClientobj*))&rwlock_cleanup;
170 p->thread_info = VG_(OSetGen_Create)(0, 0, VG_(malloc), VG_(free));
171}
172
173/** Deallocate the memory that was allocated by rwlock_initialize(). */
174static void rwlock_cleanup(struct rwlock_info* p)
175{
176 struct rwlock_thread_info* q;
177
178 tl_assert(p);
179
180 if (s_trace_rwlock)
181 {
182 VG_(message)(Vg_UserMsg,
bart5bd9f2d2008-03-03 20:31:58 +0000183 "[%d/%d] rwlock_destroy 0x%lx",
bart777f7fe2008-03-02 17:43:18 +0000184 VG_(get_running_tid)(),
185 thread_get_running_tid(),
186 p->a1);
187 }
188
189 if (rwlock_is_locked(p))
190 {
191 RwlockErrInfo REI = { p->a1 };
192 VG_(maybe_record_error)(VG_(get_running_tid)(),
193 RwlockErr,
194 VG_(get_IP)(VG_(get_running_tid)()),
195 "Destroying locked rwlock",
196 &REI);
197 }
198
199 VG_(OSetGen_ResetIter)(p->thread_info);
200 for ( ; (q = VG_(OSetGen_Next)(p->thread_info)); q++)
201 {
barta2b6e1b2008-03-17 18:32:39 +0000202 sg_put(q->last_unlock_segment);
bart777f7fe2008-03-02 17:43:18 +0000203 }
204 VG_(OSetGen_Destroy)(p->thread_info);
205}
206
207static
208struct rwlock_info*
bart0268dfa2008-03-11 20:10:21 +0000209rwlock_get_or_allocate(const Addr rwlock)
bart777f7fe2008-03-02 17:43:18 +0000210{
211 struct rwlock_info* p;
212
213 tl_assert(offsetof(DrdClientobj, rwlock) == 0);
214 p = &clientobj_get(rwlock, ClientRwlock)->rwlock;
215 if (p)
216 {
bart777f7fe2008-03-02 17:43:18 +0000217 return p;
218 }
219
bart0268dfa2008-03-11 20:10:21 +0000220 if (clientobj_present(rwlock, rwlock + 1))
bart777f7fe2008-03-02 17:43:18 +0000221 {
222 GenericErrInfo GEI;
223 VG_(maybe_record_error)(VG_(get_running_tid)(),
224 GenericErr,
225 VG_(get_IP)(VG_(get_running_tid)()),
226 "Not a reader-writer lock",
227 &GEI);
228 return 0;
229 }
230
bart0268dfa2008-03-11 20:10:21 +0000231 p = &clientobj_add(rwlock, ClientRwlock)->rwlock;
232 rwlock_initialize(p, rwlock);
bart777f7fe2008-03-02 17:43:18 +0000233 return p;
234}
235
236static struct rwlock_info* rwlock_get(const Addr rwlock)
237{
238 tl_assert(offsetof(DrdClientobj, rwlock) == 0);
239 return &clientobj_get(rwlock, ClientRwlock)->rwlock;
240}
241
242/** Called before pthread_rwlock_init(). */
bart0268dfa2008-03-11 20:10:21 +0000243struct rwlock_info* rwlock_pre_init(const Addr rwlock)
bart777f7fe2008-03-02 17:43:18 +0000244{
245 struct rwlock_info* p;
246
247 if (s_trace_rwlock)
248 {
249 VG_(message)(Vg_UserMsg,
bart5bd9f2d2008-03-03 20:31:58 +0000250 "[%d/%d] rwlock_init 0x%lx",
bart777f7fe2008-03-02 17:43:18 +0000251 VG_(get_running_tid)(),
252 thread_get_running_tid(),
253 rwlock);
254 }
255
256 p = rwlock_get(rwlock);
257
258 if (p)
259 {
260 const ThreadId vg_tid = VG_(get_running_tid)();
261 RwlockErrInfo REI
262 = { p->a1 };
263 VG_(maybe_record_error)(vg_tid,
264 RwlockErr,
265 VG_(get_IP)(vg_tid),
266 "Reader-writer lock reinitialization",
267 &REI);
268 return p;
269 }
270
bart0268dfa2008-03-11 20:10:21 +0000271 p = rwlock_get_or_allocate(rwlock);
bart777f7fe2008-03-02 17:43:18 +0000272
273 return p;
274}
275
276/** Called after pthread_rwlock_destroy(). */
277void rwlock_post_destroy(const Addr rwlock)
278{
279 struct rwlock_info* p;
280
281 p = rwlock_get(rwlock);
282 if (p == 0)
283 {
284 GenericErrInfo GEI;
285 VG_(maybe_record_error)(VG_(get_running_tid)(),
286 GenericErr,
287 VG_(get_IP)(VG_(get_running_tid)()),
288 "Not a reader-writer lock",
289 &GEI);
290 return;
291 }
292
293 clientobj_remove(rwlock, ClientRwlock);
294}
295
296/** Called before pthread_rwlock_rdlock() is invoked. If a data structure for
297 * the client-side object was not yet created, do this now. Also check whether
298 * an attempt is made to lock recursively a synchronization object that must
299 * not be locked recursively.
300 */
bart0268dfa2008-03-11 20:10:21 +0000301void rwlock_pre_rdlock(const Addr rwlock)
bart777f7fe2008-03-02 17:43:18 +0000302{
303 struct rwlock_info* p;
304
bart777f7fe2008-03-02 17:43:18 +0000305 if (s_trace_rwlock)
306 {
307 VG_(message)(Vg_UserMsg,
308 "[%d/%d] pre_rwlock_rdlock 0x%lx",
309 VG_(get_running_tid)(),
310 thread_get_running_tid(),
311 rwlock);
312 }
313
bart165b90f2008-05-10 12:54:27 +0000314 p = rwlock_get_or_allocate(rwlock);
315 tl_assert(p);
316
bart777f7fe2008-03-02 17:43:18 +0000317 if (rwlock_is_wrlocked_by(p, thread_get_running_tid()))
318 {
319 VG_(message)(Vg_UserMsg,
320 "reader-writer lock 0x%lx is already locked for"
321 " writing by calling thread",
322 p->a1);
323 }
324}
325
bart165b90f2008-05-10 12:54:27 +0000326/** Update rwlock_info state when locking the pthread_rwlock_t mutex.
327 * Note: this function must be called after pthread_rwlock_rdlock() has been
328 * called, or a race condition is triggered !
bart777f7fe2008-03-02 17:43:18 +0000329 */
330void rwlock_post_rdlock(const Addr rwlock, const Bool took_lock)
331{
332 const DrdThreadId drd_tid = thread_get_running_tid();
333 struct rwlock_info* p;
334 struct rwlock_thread_info* q;
335
bart777f7fe2008-03-02 17:43:18 +0000336 if (s_trace_rwlock)
337 {
338 VG_(message)(Vg_UserMsg,
339 "[%d/%d] post_rwlock_rdlock 0x%lx",
340 VG_(get_running_tid)(),
341 drd_tid,
342 rwlock);
343 }
344
bart165b90f2008-05-10 12:54:27 +0000345 p = rwlock_get(rwlock);
346
bart777f7fe2008-03-02 17:43:18 +0000347 if (! p || ! took_lock)
348 return;
349
350 tl_assert(! rwlock_is_wrlocked(p));
351
352 q = lookup_or_insert_node(p->thread_info, drd_tid);
353 if (++q->reader_nesting_count == 1)
354 {
bart5bd9f2d2008-03-03 20:31:58 +0000355 rwlock_combine_other_vc(p, drd_tid, False);
bart165b90f2008-05-10 12:54:27 +0000356 q->last_lock_was_writer_lock = False;
bart777f7fe2008-03-02 17:43:18 +0000357 thread_new_segment(drd_tid);
bart6bbefaf2008-04-19 15:16:45 +0000358 s_rwlock_segment_creation_count++;
bart777f7fe2008-03-02 17:43:18 +0000359 }
360}
361
362/** Called before pthread_rwlock_wrlock() is invoked. If a data structure for
363 * the client-side object was not yet created, do this now. Also check whether
364 * an attempt is made to lock recursively a synchronization object that must
365 * not be locked recursively.
366 */
bart0268dfa2008-03-11 20:10:21 +0000367void rwlock_pre_wrlock(const Addr rwlock)
bart777f7fe2008-03-02 17:43:18 +0000368{
369 struct rwlock_info* p;
370
371 p = rwlock_get(rwlock);
372
373 if (s_trace_rwlock)
374 {
375 VG_(message)(Vg_UserMsg,
376 "[%d/%d] pre_rwlock_wrlock 0x%lx",
377 VG_(get_running_tid)(),
378 thread_get_running_tid(),
379 rwlock);
380 }
381
382 if (p == 0)
383 {
bart0268dfa2008-03-11 20:10:21 +0000384 p = rwlock_get_or_allocate(rwlock);
bart777f7fe2008-03-02 17:43:18 +0000385 }
386
387 tl_assert(p);
388
389 if (rwlock_is_wrlocked_by(p, thread_get_running_tid()))
390 {
391 RwlockErrInfo REI = { p->a1 };
392 VG_(maybe_record_error)(VG_(get_running_tid)(),
393 RwlockErr,
394 VG_(get_IP)(VG_(get_running_tid)()),
395 "Recursive writer locking not allowed",
396 &REI);
397 }
398}
399
400/**
401 * Update rwlock_info state when locking the pthread_rwlock_t rwlock.
bart165b90f2008-05-10 12:54:27 +0000402 * Note: this function must be called after pthread_rwlock_wrlock() has
403 * finished, or a race condition is triggered !
bart777f7fe2008-03-02 17:43:18 +0000404 */
405void rwlock_post_wrlock(const Addr rwlock, const Bool took_lock)
406{
407 const DrdThreadId drd_tid = thread_get_running_tid();
408 struct rwlock_info* p;
409 struct rwlock_thread_info* q;
410
411 p = rwlock_get(rwlock);
412
413 if (s_trace_rwlock)
414 {
415 VG_(message)(Vg_UserMsg,
416 "[%d/%d] post_rwlock_wrlock 0x%lx",
417 VG_(get_running_tid)(),
418 drd_tid,
419 rwlock);
420 }
421
422 if (! p || ! took_lock)
423 return;
424
425 q = lookup_or_insert_node(p->thread_info, thread_get_running_tid());
426 tl_assert(q->writer_nesting_count == 0);
427 q->writer_nesting_count++;
bart5bd9f2d2008-03-03 20:31:58 +0000428 q->last_lock_was_writer_lock = True;
bart777f7fe2008-03-02 17:43:18 +0000429 tl_assert(q->writer_nesting_count == 1);
bart5bd9f2d2008-03-03 20:31:58 +0000430 rwlock_combine_other_vc(p, drd_tid, True);
bart777f7fe2008-03-02 17:43:18 +0000431 thread_new_segment(drd_tid);
bart6bbefaf2008-04-19 15:16:45 +0000432 s_rwlock_segment_creation_count++;
bart777f7fe2008-03-02 17:43:18 +0000433}
434
435/**
436 * Update rwlock_info state when unlocking the pthread_rwlock_t rwlock.
437 * Note: this function must be called before pthread_rwlock_unlock() is called,
438 * or a race condition is triggered !
439 * @return New value of the rwlock recursion count.
440 * @param rwlock Pointer to pthread_rwlock_t data structure in the client space.
441 * @param tid ThreadId of the thread calling pthread_rwlock_unlock().
442 * @param vc Pointer to the current vector clock of thread tid.
443 */
444void rwlock_pre_unlock(const Addr rwlock)
445{
446 const DrdThreadId drd_tid = thread_get_running_tid();
447 const ThreadId vg_tid = VG_(get_running_tid)();
bart777f7fe2008-03-02 17:43:18 +0000448 struct rwlock_info* const p = rwlock_get(rwlock);
449 struct rwlock_thread_info* q;
450
451 if (s_trace_rwlock && p != 0)
452 {
453 VG_(message)(Vg_UserMsg,
bart5bd9f2d2008-03-03 20:31:58 +0000454 "[%d/%d] rwlock_unlock 0x%lx",
bart777f7fe2008-03-02 17:43:18 +0000455 vg_tid,
456 drd_tid,
457 rwlock);
458 }
459
460 if (p == 0 || ! rwlock_is_locked_by(p, drd_tid))
461 {
462 RwlockErrInfo REI = { p->a1 };
463 VG_(maybe_record_error)(vg_tid,
464 RwlockErr,
465 VG_(get_IP)(vg_tid),
466 "Reader-writer lock not locked by calling thread",
467 &REI);
468 return;
469 }
470 tl_assert(p);
471 q = lookup_or_insert_node(p->thread_info, drd_tid);
472 tl_assert(q);
473 if (q->reader_nesting_count > 0)
474 q->reader_nesting_count--;
475 else if (q->writer_nesting_count > 0)
476 q->writer_nesting_count--;
477 else
478 tl_assert(False);
479
480 if (q->reader_nesting_count == 0 && q->writer_nesting_count == 0)
481 {
482 /* This pthread_rwlock_unlock() call really unlocks the rwlock. Save the */
483 /* current vector clock of the thread such that it is available when */
484 /* this rwlock is locked again. */
bart777f7fe2008-03-02 17:43:18 +0000485
barta2b6e1b2008-03-17 18:32:39 +0000486 thread_get_latest_segment(&q->last_unlock_segment, drd_tid);
bart777f7fe2008-03-02 17:43:18 +0000487 thread_new_segment(drd_tid);
bart6bbefaf2008-04-19 15:16:45 +0000488 s_rwlock_segment_creation_count++;
bart777f7fe2008-03-02 17:43:18 +0000489 }
490}
491
492/**
493 * Call this function when thread tid stops to exist, such that the
494 * "last owner" field can be cleared if it still refers to that thread.
495 */
496void rwlock_thread_delete(const DrdThreadId tid)
497{
498 struct rwlock_info* p;
499
500 clientobj_resetiter();
501 for ( ; (p = &clientobj_next(ClientRwlock)->rwlock) != 0; )
502 {
503 struct rwlock_thread_info* q;
504 if (rwlock_is_locked_by(p, tid))
505 {
506 RwlockErrInfo REI = { p->a1 };
507 VG_(maybe_record_error)(VG_(get_running_tid)(),
508 RwlockErr,
509 VG_(get_IP)(VG_(get_running_tid)()),
510 "Reader-writer lock still locked at thread exit",
511 &REI);
512 q = lookup_or_insert_node(p->thread_info, tid);
513 q->reader_nesting_count = 0;
514 q->writer_nesting_count = 0;
515 }
516 }
517}
bart6bbefaf2008-04-19 15:16:45 +0000518
519ULong get_rwlock_segment_creation_count(void)
520{
521 return s_rwlock_segment_creation_count;
522}