Changes:
- pthread_barrier_wait() intercept now passes the information to the DRD
  tool whether or not this function returned
  PTHREAD_BARRIER_SERIAL_THREAD. This information is now displayed when
  the command-line option --trace-barrier=yes has been specified.
- Changed the cleanup functions for client objects that are called just
  before a thread stops into callback functions.
- Added DRD_(clientobj_delete_thread)().
- Removed DRD_(clientobj_resetiter)(void) and DRD_(clientobj_next)().
- Added test for race conditions between pthread_barrier_wait() and
  pthread_barrier_destroy() calls. An error message is now printed if
  this condition has been detected.
- Bug fix: pthread_barrier_delete() calls on barriers being waited upon
  are now reported.
- Removed DRD_() wrapper from around the name of some static variables and
  functions.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@9211 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/drd/drd_barrier.c b/drd/drd_barrier.c
index 1807f5d..e20447f 100644
--- a/drd/drd_barrier.c
+++ b/drd/drd_barrier.c
@@ -40,57 +40,75 @@
 /** Information associated with one thread participating in a barrier. */
 struct barrier_thread_info
 {
-  UWord       tid;           // A DrdThreadId
+  UWord       tid;           // A DrdThreadId declared as UWord because
+                             // this member variable is the key of an OSet.
   Word        iteration;     // iteration of last pthread_barrier_wait()
                              // call thread tid participated in.
   Segment*    sg[2];         // Segments of the last two
                              // pthread_barrier() calls by thread tid.
+  ExeContext* wait_call_ctxt;// call stack for *_barrier_wait() call.
+  Segment*    post_wait_sg;  // Segment created after *_barrier_wait() finished
 };
 
 
 /* Local functions. */
 
-static void DRD_(barrier_cleanup)(struct barrier_info* p);
-static const char* DRD_(barrier_get_typename)(struct barrier_info* const p);
-static const char* DRD_(barrier_type_name)(const BarrierT bt);
+static void barrier_cleanup(struct barrier_info* p);
+static void barrier_delete_thread(struct barrier_info* const p,
+                                  const DrdThreadId tid);
+static const char* barrier_get_typename(struct barrier_info* const p);
+static const char* barrier_type_name(const BarrierT bt);
+static
+void barrier_report_wait_delete_race(const struct barrier_info* const p,
+                                    const struct barrier_thread_info* const q);
 
 
 /* Local variables. */
 
-static Bool DRD_(s_trace_barrier) = False;
-static ULong DRD_(s_barrier_segment_creation_count);
+static Bool  s_trace_barrier = False;
+static ULong s_barrier_segment_creation_count;
 
 
 /* Function definitions. */
 
 void DRD_(barrier_set_trace)(const Bool trace_barrier)
 {
-  DRD_(s_trace_barrier) = trace_barrier;
+  s_trace_barrier = trace_barrier;
 }
 
-/** Initialize the structure *p with the specified thread ID and iteration
- *  information. */
+/**
+ * Initialize the structure *p with the specified thread ID and iteration
+ * information.
+ */
 static
 void DRD_(barrier_thread_initialize)(struct barrier_thread_info* const p,
                                      const DrdThreadId tid,
                                      const Word iteration)
 {
-  p->tid = tid;
-  p->iteration = iteration;
-  p->sg[0] = 0;
-  p->sg[1] = 0;
+  p->tid            = tid;
+  p->iteration      = iteration;
+  p->sg[0]          = 0;
+  p->sg[1]          = 0;
+  p->wait_call_ctxt = 0;
+  p->post_wait_sg   = 0;
 }
 
-/** Deallocate the memory that was allocated in barrier_thread_initialize(). */
+/**
+ * Deallocate the memory that is owned by members of
+ * struct barrier_thread_info.
+ */
 static void DRD_(barrier_thread_destroy)(struct barrier_thread_info* const p)
 {
   tl_assert(p);
   DRD_(sg_put)(p->sg[0]);
   DRD_(sg_put)(p->sg[1]);
+  DRD_(sg_put)(p->post_wait_sg);
 }
 
-/** Initialize the structure *p with the specified client-side barrier address,
- *  barrier object size and number of participants in each barrier. */
+/**
+ * Initialize the structure *p with the specified client-side barrier address,
+ * barrier object size and number of participants in each barrier.
+ */
 static
 void DRD_(barrier_initialize)(struct barrier_info* const p,
                               const Addr barrier,
@@ -101,13 +119,16 @@
   tl_assert(barrier_type == pthread_barrier || barrier_type == gomp_barrier);
   tl_assert(p->a1 == barrier);
 
-  p->cleanup           = (void(*)(DrdClientobj*))DRD_(barrier_cleanup);
+  p->cleanup           = (void(*)(DrdClientobj*))barrier_cleanup;
+  p->delete_thread
+    = (void(*)(DrdClientobj*, DrdThreadId))barrier_delete_thread;
   p->barrier_type      = barrier_type;
   p->count             = count;
   p->pre_iteration     = 0;
   p->post_iteration    = 0;
   p->pre_waiters_left  = count;
   p->post_waiters_left = count;
+
   tl_assert(sizeof(((struct barrier_thread_info*)0)->tid) == sizeof(Word));
   tl_assert(sizeof(((struct barrier_thread_info*)0)->tid)
             >= sizeof(DrdThreadId));
@@ -116,18 +137,21 @@
 }
 
 /**
- * Deallocate the memory allocated by barrier_initialize() and in p->oset. 
+ * Deallocate the memory owned by the struct barrier_info object and also
+ * all the nodes in the OSet p->oset.
+ *
  * Called by clientobj_destroy().
  */
-void DRD_(barrier_cleanup)(struct barrier_info* p)
+static void barrier_cleanup(struct barrier_info* p)
 {
   struct barrier_thread_info* q;
+    Segment* latest_sg = 0;
 
   tl_assert(p);
 
   if (p->pre_waiters_left != p->count)
   {
-    BarrierErrInfo bei = { p->a1 };
+    BarrierErrInfo bei = { p->a1, 0, 0 };
     VG_(maybe_record_error)(VG_(get_running_tid)(),
                             BarrierErr,
                             VG_(get_IP)(VG_(get_running_tid)()),
@@ -136,16 +160,29 @@
                             &bei);
   }
 
+  DRD_(thread_get_latest_segment)(&latest_sg, DRD_(thread_get_running_tid)());
+  tl_assert(latest_sg);
+
   VG_(OSetGen_ResetIter)(p->oset);
   for ( ; (q = VG_(OSetGen_Next)(p->oset)) != 0; )
   {
+    if (q->post_wait_sg
+        && ! DRD_(vc_lte)(&q->post_wait_sg->vc, &latest_sg->vc))
+    {
+      barrier_report_wait_delete_race(p, q);
+    }
+
     DRD_(barrier_thread_destroy)(q);
   }
   VG_(OSetGen_Destroy)(p->oset);
+
+  DRD_(sg_put)(latest_sg);
 }
 
-/** Look up the client-side barrier address barrier in s_barrier[]. If not
- *  found, add it. */
+/**
+ * Look up the client-side barrier address barrier in s_barrier[]. If not
+ * found, add it.
+ */
 static
 struct barrier_info*
 DRD_(barrier_get_or_allocate)(const Addr barrier,
@@ -165,17 +202,21 @@
   return p;
 }
 
-/** Look up the address of the information associated with the client-side
- *  barrier object. */
+/**
+ * Look up the address of the information associated with the client-side
+ * barrier object.
+ */
 static struct barrier_info* DRD_(barrier_get)(const Addr barrier)
 {
   tl_assert(offsetof(DrdClientobj, barrier) == 0);
   return &(DRD_(clientobj_get)(barrier, ClientBarrier)->barrier);
 }
 
-/** Initialize a barrier with client address barrier, client size size, and
- *  where count threads participate in each barrier.
- *  Called before pthread_barrier_init().
+/**
+ * Initialize a barrier with client address barrier, client size size, and
+ * where count threads participate in each barrier.
+ *
+ * Called before pthread_barrier_init().
  */
 void DRD_(barrier_init)(const Addr barrier,
                         const BarrierT barrier_type, const Word count,
@@ -187,7 +228,7 @@
 
   if (count == 0)
   {
-    BarrierErrInfo bei = { barrier };
+    BarrierErrInfo bei = { barrier, 0, 0 };
     VG_(maybe_record_error)(VG_(get_running_tid)(),
                             BarrierErr,
                             VG_(get_IP)(VG_(get_running_tid)()),
@@ -200,7 +241,7 @@
     p = DRD_(barrier_get)(barrier);
     if (p)
     {
-      BarrierErrInfo bei = { barrier };
+      BarrierErrInfo bei = { barrier, 0, 0 };
       VG_(maybe_record_error)(VG_(get_running_tid)(),
                               BarrierErr,
                               VG_(get_IP)(VG_(get_running_tid)()),
@@ -210,7 +251,7 @@
   }
   p = DRD_(barrier_get_or_allocate)(barrier, barrier_type, count);
 
-  if (DRD_(s_trace_barrier))
+  if (s_trace_barrier)
   {
     if (reinitialization)
     {
@@ -218,7 +259,7 @@
                    "[%d/%d] barrier_reinit    %s 0x%lx count %ld -> %ld",
                    VG_(get_running_tid)(),
                    DRD_(thread_get_running_tid)(),
-                   DRD_(barrier_get_typename)(p),
+                   barrier_get_typename(p),
                    barrier,
                    p->count,
                    count);
@@ -229,7 +270,7 @@
                    "[%d/%d] barrier_init      %s 0x%lx",
                    VG_(get_running_tid)(),
                    DRD_(thread_get_running_tid)(),
-                   DRD_(barrier_get_typename)(p),
+                   barrier_get_typename(p),
                    barrier);
     }
   }
@@ -238,7 +279,7 @@
   {
     if (p->pre_waiters_left != p->count || p->post_waiters_left != p->count)
     {
-      BarrierErrInfo bei = { p->a1 };
+      BarrierErrInfo bei = { p->a1, 0, 0 };
       VG_(maybe_record_error)(VG_(get_running_tid)(),
                               BarrierErr,
                               VG_(get_IP)(VG_(get_running_tid)()),
@@ -250,20 +291,20 @@
   }
 }
 
-/** Called after pthread_barrier_destroy(). */
+/** Called after pthread_barrier_destroy() / gomp_barrier_destroy(). */
 void DRD_(barrier_destroy)(const Addr barrier, const BarrierT barrier_type)
 {
   struct barrier_info* p;
 
   p = DRD_(barrier_get)(barrier);
 
-  if (DRD_(s_trace_barrier))
+  if (s_trace_barrier)
   {
     VG_(message)(Vg_UserMsg,
                  "[%d/%d] barrier_destroy   %s 0x%lx",
                  VG_(get_running_tid)(),
                  DRD_(thread_get_running_tid)(),
-                 DRD_(barrier_get_typename)(p),
+                 barrier_get_typename(p),
                  barrier);
   }
 
@@ -280,7 +321,7 @@
 
   if (p->pre_waiters_left != p->count || p->post_waiters_left != p->count)
   {
-    BarrierErrInfo bei = { p->a1 };
+    BarrierErrInfo bei = { p->a1, 0, 0 };
     VG_(maybe_record_error)(VG_(get_running_tid)(),
                             BarrierErr,
                             VG_(get_IP)(VG_(get_running_tid)()),
@@ -291,7 +332,7 @@
   DRD_(clientobj_remove)(p->a1, ClientBarrier);
 }
 
-/** Called before pthread_barrier_wait(). */
+/** Called before pthread_barrier_wait() / gomp_barrier_wait(). */
 void DRD_(barrier_pre_wait)(const DrdThreadId tid, const Addr barrier,
                             const BarrierT barrier_type)
 {
@@ -302,6 +343,11 @@
   p = DRD_(barrier_get)(barrier);
   if (p == 0 && barrier_type == gomp_barrier)
   {
+    /*
+     * gomp_barrier_wait() call has been intercepted but gomp_barrier_init()
+     * not. The only cause I know of that can trigger this is that libgomp.so
+     * has been compiled with --enable-linux-futex.
+     */
     VG_(message)(Vg_UserMsg, "");
     VG_(message)(Vg_UserMsg,
                  "Please verify whether gcc has been configured"
@@ -312,17 +358,18 @@
   }
   tl_assert(p);
 
-  if (DRD_(s_trace_barrier))
+  if (s_trace_barrier)
   {
     VG_(message)(Vg_UserMsg,
                  "[%d/%d] barrier_pre_wait  %s 0x%lx iteration %ld",
                  VG_(get_running_tid)(),
                  DRD_(thread_get_running_tid)(),
-                 DRD_(barrier_get_typename)(p),
+                 barrier_get_typename(p),
                  barrier,
                  p->pre_iteration);
   }
 
+  /* Allocate the per-thread data structure if necessary. */
   q = VG_(OSetGen_Lookup)(p->oset, &word_tid);
   if (q == 0)
   {
@@ -331,8 +378,21 @@
     VG_(OSetGen_Insert)(p->oset, q);
     tl_assert(VG_(OSetGen_Lookup)(p->oset, &word_tid) == q);
   }
+
+  /* Record *_barrier_wait() call context. */
+  q->wait_call_ctxt = VG_(record_ExeContext)(VG_(get_running_tid)(), 0);
+
+  /*
+   * Store a pointer to the latest segment of the current thread in the
+   * per-thread data structure.
+   */
   DRD_(thread_get_latest_segment)(&q->sg[p->pre_iteration], tid);
 
+  /*
+   * If the same number of threads as the barrier count indicates have
+   * called the pre *_barrier_wait() wrapper, toggle p->pre_iteration and
+   * reset the p->pre_waiters_left counter.
+   */
   if (--p->pre_waiters_left <= 0)
   {
     p->pre_iteration    = 1 - p->pre_iteration;
@@ -340,106 +400,143 @@
   }
 }
 
-/** Called after pthread_barrier_wait(). */
+/** Called after pthread_barrier_wait() / gomp_barrier_wait(). */
 void DRD_(barrier_post_wait)(const DrdThreadId tid, const Addr barrier,
-                             const BarrierT barrier_type, const Bool waited)
+                             const BarrierT barrier_type, const Bool waited,
+                             const Bool serializing)
 {
   struct barrier_info* p;
+  const UWord word_tid = tid;
+  struct barrier_thread_info* q;
+  struct barrier_thread_info* r;
 
   p = DRD_(barrier_get)(barrier);
 
-  if (DRD_(s_trace_barrier))
+  if (s_trace_barrier)
   {
     VG_(message)(Vg_UserMsg,
-                 "[%d/%d] barrier_post_wait %s 0x%lx iteration %ld",
+                 "[%d/%d] barrier_post_wait %s 0x%lx iteration %ld%s",
                  VG_(get_running_tid)(),
                  tid,
-                 p ? DRD_(barrier_get_typename)(p) : "(?)",
+                 p ? barrier_get_typename(p) : "(?)",
                  barrier,
-                 p ? p->post_iteration : -1);
+                 p ? p->post_iteration : -1,
+                 serializing ? " (serializing)" : "");
   }
 
-  /* If p == 0, this means that the barrier has been destroyed after     */
-  /* *_barrier_wait() returned and before this function was called. Just */
-  /* return in that case.                                                */
+  /*
+   * If p == 0, this means that the barrier has been destroyed after
+   * *_barrier_wait() returned and before this function was called. Just
+   * return in that case -- race conditions between *_barrier_wait()
+   * and *_barrier_destroy() are detected by the *_barrier_destroy() wrapper.
+   */
   if (p == 0)
     return;
 
-  if (waited)
+  /* If the *_barrier_wait() call returned an error code, exit. */
+  if (! waited)
+    return;
+
+  q = VG_(OSetGen_Lookup)(p->oset, &word_tid);
+  if (q == 0)
   {
-    const UWord word_tid = tid;
-    struct barrier_thread_info* q;
-    struct barrier_thread_info* r;
+    BarrierErrInfo bei = { p->a1, 0, 0 };
+    VG_(maybe_record_error)(VG_(get_running_tid)(),
+                            BarrierErr,
+                            VG_(get_IP)(VG_(get_running_tid)()),
+                            "Error in barrier implementation"
+                            " -- barrier_wait() started before"
+                            " barrier_destroy() and finished after"
+                            " barrier_destroy()",
+                            &bei);
 
-    q = VG_(OSetGen_Lookup)(p->oset, &word_tid);
-    if (q == 0)
+    q = VG_(OSetGen_AllocNode)(p->oset, sizeof(*q));
+    DRD_(barrier_thread_initialize)(q, tid, p->pre_iteration);
+    VG_(OSetGen_Insert)(p->oset, q);
+    tl_assert(VG_(OSetGen_Lookup)(p->oset, &word_tid) == q);
+  }
+  /*
+   * Combine all vector clocks that were stored in the pre_barrier_wait
+   * wrapper with the vector clock of the current thread.
+   */
+  VG_(OSetGen_ResetIter)(p->oset);
+  for ( ; (r = VG_(OSetGen_Next)(p->oset)) != 0; )
+  {
+    if (r != q)
     {
-      BarrierErrInfo bei = { p->a1 };
-      VG_(maybe_record_error)(VG_(get_running_tid)(),
-                              BarrierErr,
-                              VG_(get_IP)(VG_(get_running_tid)()),
-                              "Error in barrier implementation"
-                              " -- barrier_wait() started before"
-                              " barrier_destroy() and finished after"
-                              " barrier_destroy()",
-                              &bei);
-
-      q = VG_(OSetGen_AllocNode)(p->oset, sizeof(*q));
-      DRD_(barrier_thread_initialize)(q, tid, p->pre_iteration);
-      VG_(OSetGen_Insert)(p->oset, q);
-      tl_assert(VG_(OSetGen_Lookup)(p->oset, &word_tid) == q);
+      tl_assert(r->sg[p->post_iteration]);
+      DRD_(thread_combine_vc2)(tid, &r->sg[p->post_iteration]->vc);
     }
-    VG_(OSetGen_ResetIter)(p->oset);
-    for ( ; (r = VG_(OSetGen_Next)(p->oset)) != 0; )
-    {
-      if (r != q)
-      {
-        tl_assert(r->sg[p->post_iteration]);
-        DRD_(thread_combine_vc2)(tid, &r->sg[p->post_iteration]->vc);
-      }
-    }
+  }
 
-    DRD_(thread_new_segment)(tid);
-    DRD_(s_barrier_segment_creation_count)++;
+  /* Create a new segment and store a pointer to that segment. */
+  DRD_(thread_new_segment)(tid);
+  DRD_(thread_get_latest_segment)(&q->post_wait_sg, tid);
+  s_barrier_segment_creation_count++;
 
-    if (--p->post_waiters_left <= 0)
-    {
-      p->post_iteration    = 1 - p->post_iteration;
-      p->post_waiters_left = p->count;
-    }
+  /*
+   * If the same number of threads as the barrier count indicates have
+   * called the post *_barrier_wait() wrapper, toggle p->post_iteration and
+   * reset the p->post_waiters_left counter.
+   */
+  if (--p->post_waiters_left <= 0)
+  {
+    p->post_iteration    = 1 - p->post_iteration;
+    p->post_waiters_left = p->count;
   }
 }
 
-/** Call this function when thread tid stops to exist. */
-void DRD_(barrier_thread_delete)(const DrdThreadId tid)
+/** Called when thread tid stops to exist. */
+static void barrier_delete_thread(struct barrier_info* const p,
+                                  const DrdThreadId tid)
 {
-  struct barrier_info* p;
+  struct barrier_thread_info* q;
+  const UWord word_tid = tid;
 
-  DRD_(clientobj_resetiter)();
-  for ( ; (p = &(DRD_(clientobj_next)(ClientBarrier)->barrier)) != 0; )
+  q = VG_(OSetGen_Remove)(p->oset, &word_tid);
+
+  /*
+   * q is only non-zero if the barrier object has been used by thread tid
+   * after the barrier_init() call and before the thread finished.
+   */
+  if (q)
   {
-    struct barrier_thread_info* q;
-    const UWord word_tid = tid;
-    q = VG_(OSetGen_Remove)(p->oset, &word_tid);
-    /* q is only non-zero if the barrier object has been used by thread tid
-     * after the barrier_init() call and before the thread finished.
-     */
-    if (q)
-    {
-      DRD_(barrier_thread_destroy)(q);
-      VG_(OSetGen_FreeNode)(p->oset, q);
-    }
+    DRD_(barrier_thread_destroy)(q);
+    VG_(OSetGen_FreeNode)(p->oset, q);
   }
 }
 
-static const char* DRD_(barrier_get_typename)(struct barrier_info* const p)
+/**
+ * Report that *_barrier_destroy() has been called but that this call was
+ * not synchronized with the last *_barrier_wait() call on the same barrier.
+ */
+static
+void barrier_report_wait_delete_race(const struct barrier_info* const p,
+                                     const struct barrier_thread_info* const q)
+{
+  tl_assert(p);
+  tl_assert(q);
+
+  {
+    BarrierErrInfo bei
+      = { p->a1, q->tid, q->wait_call_ctxt };
+    VG_(maybe_record_error)(VG_(get_running_tid)(),
+                            BarrierErr,
+                            VG_(get_IP)(VG_(get_running_tid)()),
+                            "Destruction of barrier not synchronized with"
+                            " barrier wait call",
+                            &bei);
+  }
+}
+
+static const char* barrier_get_typename(struct barrier_info* const p)
 {
   tl_assert(p);
 
-  return DRD_(barrier_type_name)(p->barrier_type);
+  return barrier_type_name(p->barrier_type);
 }
 
-static const char* DRD_(barrier_type_name)(const BarrierT bt)
+static const char* barrier_type_name(const BarrierT bt)
 {
   switch (bt)
   {
@@ -453,5 +550,5 @@
 
 ULong DRD_(get_barrier_segment_creation_count)(void)
 {
-  return DRD_(s_barrier_segment_creation_count);
+  return s_barrier_segment_creation_count;
 }