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;
}