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