1/* -*- mode: C; c-basic-offset: 3; indent-tabs-mode: nil; -*- */
2/*
3  This file is part of drd, a thread error detector.
4
5  Copyright (C) 2006-2011 Bart Van Assche <bvanassche@acm.org>.
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_cond.h"
28#include "drd_error.h"
29#include "drd_mutex.h"
30#include "pub_tool_errormgr.h"    /* VG_(maybe_record_error)() */
31#include "pub_tool_libcassert.h"  /* tl_assert()               */
32#include "pub_tool_libcbase.h"    /* VG_(memcmp)()             */
33#include "pub_tool_libcprint.h"   /* VG_(printf)()             */
34#include "pub_tool_machine.h"     /* VG_(get_IP)()             */
35#include "pub_tool_threadstate.h" /* VG_(get_running_tid)()    */
36
37
38/* Local functions. */
39
40static void DRD_(cond_cleanup)(struct cond_info* p);
41
42
43/* Local variables. */
44
45static Bool DRD_(s_report_signal_unlocked) = True;
46static Bool DRD_(s_trace_cond);
47
48
49/* Function definitions. */
50
51void DRD_(cond_set_report_signal_unlocked)(const Bool r)
52{
53   DRD_(s_report_signal_unlocked) = r;
54}
55
56void DRD_(cond_set_trace)(const Bool trace_cond)
57{
58   DRD_(s_trace_cond) = trace_cond;
59}
60
61static
62void DRD_(cond_initialize)(struct cond_info* const p, const Addr cond)
63{
64   tl_assert(cond != 0);
65   tl_assert(p->a1   == cond);
66   tl_assert(p->type == ClientCondvar);
67
68   p->cleanup       = (void(*)(DrdClientobj*))(DRD_(cond_cleanup));
69   p->delete_thread = 0;
70   p->waiter_count  = 0;
71   p->mutex         = 0;
72}
73
74/**
75 * Free the memory that was allocated by cond_initialize(). Called by
76 * DRD_(clientobj_remove)().
77 */
78static void DRD_(cond_cleanup)(struct cond_info* p)
79{
80   tl_assert(p);
81   if (p->mutex)
82   {
83      struct mutex_info* q;
84      q = &(DRD_(clientobj_get)(p->mutex, ClientMutex)->mutex);
85      {
86         CondDestrErrInfo cde = {
87	    DRD_(thread_get_running_tid)(),
88	    p->a1,
89	    q ? q->a1 : 0,
90	    q ? q->owner : DRD_INVALID_THREADID
91	 };
92         VG_(maybe_record_error)(VG_(get_running_tid)(),
93                                 CondDestrErr,
94                                 VG_(get_IP)(VG_(get_running_tid)()),
95                                 "Destroying condition variable that is being"
96                                 " waited upon",
97                                 &cde);
98      }
99   }
100}
101
102/**
103 * Report that the synchronization object at address 'addr' is of the
104 * wrong type.
105 */
106static void wrong_type(const Addr addr)
107{
108   GenericErrInfo gei = {
109      .tid  = DRD_(thread_get_running_tid)(),
110      .addr = addr,
111   };
112   VG_(maybe_record_error)(VG_(get_running_tid)(),
113                           GenericErr,
114                           VG_(get_IP)(VG_(get_running_tid)()),
115                           "wrong type of synchronization object",
116                           &gei);
117}
118
119static struct cond_info* cond_get_or_allocate(const Addr cond)
120{
121   struct cond_info *p;
122
123   tl_assert(offsetof(DrdClientobj, cond) == 0);
124   p = &(DRD_(clientobj_get)(cond, ClientCondvar)->cond);
125   if (p)
126      return p;
127
128   if (DRD_(clientobj_present)(cond, cond + 1))
129   {
130      wrong_type(cond);
131      return 0;
132   }
133
134   p = &(DRD_(clientobj_add)(cond, ClientCondvar)->cond);
135   DRD_(cond_initialize)(p, cond);
136   return p;
137}
138
139struct cond_info* DRD_(cond_get)(const Addr cond)
140{
141   tl_assert(offsetof(DrdClientobj, cond) == 0);
142   return &(DRD_(clientobj_get)(cond, ClientCondvar)->cond);
143}
144
145/** Called before pthread_cond_init(). */
146void DRD_(cond_pre_init)(const Addr cond)
147{
148   struct cond_info* p;
149
150   if (DRD_(s_trace_cond))
151      DRD_(trace_msg)("[%d] cond_init       cond 0x%lx",
152                      DRD_(thread_get_running_tid)(), cond);
153
154   p = DRD_(cond_get)(cond);
155
156   if (p)
157   {
158      CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond };
159      VG_(maybe_record_error)(VG_(get_running_tid)(),
160                              CondErr,
161                              VG_(get_IP)(VG_(get_running_tid)()),
162                              "initialized twice",
163                              &cei);
164   }
165
166   p = cond_get_or_allocate(cond);
167}
168
169/** Called after pthread_cond_destroy(). */
170void DRD_(cond_post_destroy)(const Addr cond)
171{
172   struct cond_info* p;
173
174   if (DRD_(s_trace_cond))
175      DRD_(trace_msg)("[%d] cond_destroy    cond 0x%lx",
176                      DRD_(thread_get_running_tid)(), cond);
177
178   p = DRD_(cond_get)(cond);
179   if (p == 0)
180   {
181      CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond };
182      VG_(maybe_record_error)(VG_(get_running_tid)(),
183                              CondErr,
184                              VG_(get_IP)(VG_(get_running_tid)()),
185                              "not a condition variable",
186                              &cei);
187      return;
188   }
189
190   if (p->waiter_count != 0)
191   {
192      CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond };
193      VG_(maybe_record_error)(VG_(get_running_tid)(),
194                              CondErr,
195                              VG_(get_IP)(VG_(get_running_tid)()),
196                              "destruction of condition variable being waited"
197                              " upon",
198                              &cei);
199   }
200
201   DRD_(clientobj_remove)(p->a1, ClientCondvar);
202}
203
204/**
205 * Called before pthread_cond_wait(). Note: before this function is called,
206 * mutex_unlock() has already been called from drd_clientreq.c.
207 */
208void DRD_(cond_pre_wait)(const Addr cond, const Addr mutex)
209{
210   struct cond_info* p;
211   struct mutex_info* q;
212
213   if (DRD_(s_trace_cond))
214      DRD_(trace_msg)("[%d] cond_pre_wait   cond 0x%lx",
215                      DRD_(thread_get_running_tid)(), cond);
216
217   p = cond_get_or_allocate(cond);
218   if (!p)
219   {
220      CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond };
221      VG_(maybe_record_error)(VG_(get_running_tid)(),
222                              CondErr,
223                              VG_(get_IP)(VG_(get_running_tid)()),
224                              "not a condition variable",
225                              &cei);
226      return;
227   }
228
229   if (p->waiter_count == 0)
230   {
231      p->mutex = mutex;
232   }
233   else if (p->mutex != mutex)
234   {
235      CondWaitErrInfo cwei
236         = { .tid = DRD_(thread_get_running_tid)(),
237             .cond = cond, .mutex1 = p->mutex, .mutex2 = mutex };
238      VG_(maybe_record_error)(VG_(get_running_tid)(),
239                              CondWaitErr,
240                              VG_(get_IP)(VG_(get_running_tid)()),
241                              "Inconsistent association of condition variable"
242                              " and mutex",
243                              &cwei);
244   }
245   tl_assert(p->mutex);
246   q = DRD_(mutex_get)(p->mutex);
247   if (q
248       && q->owner == DRD_(thread_get_running_tid)() && q->recursion_count > 0)
249   {
250      const ThreadId vg_tid = VG_(get_running_tid)();
251      MutexErrInfo MEI = { DRD_(thread_get_running_tid)(),
252                           q->a1, q->recursion_count, q->owner };
253      VG_(maybe_record_error)(vg_tid,
254                              MutexErr,
255                              VG_(get_IP)(vg_tid),
256                              "Mutex locked recursively",
257                              &MEI);
258   }
259   else if (q == 0)
260   {
261      DRD_(not_a_mutex)(p->mutex);
262   }
263
264   ++p->waiter_count;
265}
266
267/**
268 * Called after pthread_cond_wait().
269 */
270void DRD_(cond_post_wait)(const Addr cond)
271{
272   struct cond_info* p;
273
274   if (DRD_(s_trace_cond))
275      DRD_(trace_msg)("[%d] cond_post_wait  cond 0x%lx",
276                      DRD_(thread_get_running_tid)(), cond);
277
278   p = DRD_(cond_get)(cond);
279   if (!p)
280   {
281      CondDestrErrInfo cde = {
282         DRD_(thread_get_running_tid)(), cond, 0, DRD_INVALID_THREADID
283      };
284      VG_(maybe_record_error)(VG_(get_running_tid)(),
285                              CondDestrErr,
286                              VG_(get_IP)(VG_(get_running_tid)()),
287                              "condition variable has been destroyed while"
288                              " being waited upon",
289                              &cde);
290      return;
291   }
292
293   if (p->waiter_count > 0)
294   {
295      --p->waiter_count;
296      if (p->waiter_count == 0)
297      {
298	 p->mutex = 0;
299      }
300   }
301}
302
303static void cond_signal(const DrdThreadId tid, struct cond_info* const cond_p)
304{
305   const ThreadId vg_tid = VG_(get_running_tid)();
306   const DrdThreadId drd_tid = DRD_(VgThreadIdToDrdThreadId)(vg_tid);
307
308   tl_assert(cond_p);
309
310   if (cond_p->waiter_count > 0)
311   {
312      if (DRD_(s_report_signal_unlocked)
313	  && ! DRD_(mutex_is_locked_by)(cond_p->mutex, drd_tid))
314      {
315	 /*
316	  * A signal is sent while the associated mutex has not been locked.
317	  * This can indicate but is not necessarily a race condition.
318	  */
319	 CondRaceErrInfo cei = { .tid = DRD_(thread_get_running_tid)(),
320				 .cond  = cond_p->a1,
321				 .mutex = cond_p->mutex,
322	 };
323	 VG_(maybe_record_error)(vg_tid,
324				 CondRaceErr,
325				 VG_(get_IP)(vg_tid),
326				 "CondErr",
327				 &cei);
328      }
329   }
330   else
331   {
332      /*
333       * No other thread is waiting for the signal, hence the signal will
334       * be lost. This is normal in a POSIX threads application.
335       */
336   }
337}
338
339static void not_initialized(Addr const cond)
340{
341   CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond };
342   VG_(maybe_record_error)(VG_(get_running_tid)(),
343                           CondErr,
344                           VG_(get_IP)(VG_(get_running_tid)()),
345                           "condition variable has not been initialized",
346                           &cei);
347}
348
349/** Called before pthread_cond_signal(). */
350void DRD_(cond_pre_signal)(Addr const cond)
351{
352   struct cond_info* p;
353
354   p = DRD_(cond_get)(cond);
355   if (DRD_(s_trace_cond))
356      DRD_(trace_msg)("[%d] cond_signal     cond 0x%lx",
357                      DRD_(thread_get_running_tid)(), cond);
358
359   tl_assert(DRD_(pthread_cond_initializer));
360   if (!p && VG_(memcmp)((void*)cond, (void*)DRD_(pthread_cond_initializer),
361                         DRD_(pthread_cond_initializer_size)) != 0)
362   {
363      not_initialized(cond);
364      return;
365   }
366
367   if (!p)
368      p = cond_get_or_allocate(cond);
369
370   cond_signal(DRD_(thread_get_running_tid)(), p);
371}
372
373/** Called before pthread_cond_broadcast(). */
374void DRD_(cond_pre_broadcast)(Addr const cond)
375{
376   struct cond_info* p;
377
378   if (DRD_(s_trace_cond))
379      DRD_(trace_msg)("[%d] cond_broadcast  cond 0x%lx",
380                      DRD_(thread_get_running_tid)(), cond);
381
382   p = DRD_(cond_get)(cond);
383   tl_assert(DRD_(pthread_cond_initializer));
384   if (!p && VG_(memcmp)((void*)cond, (void*)DRD_(pthread_cond_initializer),
385                         DRD_(pthread_cond_initializer_size)) != 0)
386   {
387      not_initialized(cond);
388      return;
389   }
390
391   if (!p)
392      p = cond_get_or_allocate(cond);
393
394   cond_signal(DRD_(thread_get_running_tid)(), p);
395}
396