Blob Blame History Raw
From 3a30870111f1ce95d5715e66387d60548927c703 Mon Sep 17 00:00:00 2001
From: Mark Wielaard <mjw@redhat.com>
Date: Tue, 19 Jan 2016 15:13:47 +0100
Subject: [PATCH] pthread_barrier vs newer glibc implementation

glibc 2.23 will have a new pthread_barrier implementation.
This implementation reacts differently on bad usage of barriers.
Because of this the bar_bad testcase will hang indefinitely.
In particular pthread_barrier_destroy will hang when there are
still other threads waiting on a barrier. To solve this we add
extra threads to will "unblock" the hanging destroy by sleeping
for a while and then also waiting on the barrier, which will unblock
the destroy operation. Or if this is the last test, just exit
the whole program since we are done anyway. Also newer glibc is
more picky about destroying uninitialized barriers, we would crash
when zero filling, so we now one fill. Which doesn't crash, but
depending on glibc version might return an error or hang. Since
depending on version we now get slightly different error reports
there are now alternative exp files.

Tested against glibc 2.17, glibc 2.22 and glibc 2.23-prerelease.
---
 helgrind/tests/Makefile.am                         |  1 +
 helgrind/tests/bar_bad.c                           | 51 ++++++++++++++--------
 helgrind/tests/bar_bad.stderr.exp                  | 16 +++----
 ....stderr.exp => bar_bad.stderr.exp-destroy-hang} | 30 ++++++++-----
 4 files changed, 62 insertions(+), 36 deletions(-)
 copy helgrind/tests/{bar_bad.stderr.exp => bar_bad.stderr.exp-destroy-hang} (72%)

diff --git a/helgrind/tests/Makefile.am b/helgrind/tests/Makefile.am
index 8a0d6e6..df82169 100644
--- a/helgrind/tests/Makefile.am
+++ b/helgrind/tests/Makefile.am
@@ -19,6 +19,7 @@ EXTRA_DIST = \
 	cond_timedwait_test.vgtest cond_timedwait_test.stdout.exp \
 		cond_timedwait_test.stderr.exp \
 	bar_bad.vgtest bar_bad.stdout.exp bar_bad.stderr.exp \
+		bar_bad.stderr.exp-destroy-hang \
 	bar_trivial.vgtest bar_trivial.stdout.exp bar_trivial.stderr.exp \
 	free_is_write.vgtest free_is_write.stdout.exp \
 		free_is_write.stderr.exp \
diff --git a/helgrind/tests/bar_bad.c b/helgrind/tests/bar_bad.c
index dd6079c..424ae2f 100644
--- a/helgrind/tests/bar_bad.c
+++ b/helgrind/tests/bar_bad.c
@@ -15,23 +15,27 @@ void* child1 ( void* arg )
    return NULL;
 }
 
+void *sleep1 ( void* arg )
+{
+   /* Long sleep, we hope to never trigger. */
+   sleep (6);
+   pthread_barrier_wait ( (pthread_barrier_t*)arg );
+   return NULL;
+}
+
+void *exit1 ( void* arg )
+{
+   /* Sleep a bit, then exit, we are done. */
+   sleep (1);
+   exit (0);
+   return NULL;
+}
+
 int main ( void )
 {
   pthread_barrier_t *bar1, *bar2, *bar3, *bar4, *bar5;
-  pthread_t thr1, thr2;
   int r;
-
-  /* possibly set up a watchdog timer thread here. */
-
-
-
-
-
-
-
-
-
-
+  pthread_t thr1, thr2, slp1, slp2, ext1;
 
   /* initialise a barrier with a zero count */
   fprintf(stderr, "\ninitialise a barrier with zero count\n");
@@ -49,6 +53,9 @@ int main ( void )
   fprintf(stderr, "\ninitialise a barrier which has threads waiting on it\n");
   bar3 = malloc(sizeof(pthread_barrier_t));
   pthread_barrier_init(bar3, NULL, 2);
+  /* create a thread, whose purpose is to "unblock" the barrier after
+     some sleeping in case it keeps being blocked.  */
+  pthread_create(&slp1, NULL, sleep1, (void*)bar3);
   /* create a thread, whose only purpose is to block on the barrier */
   pthread_create(&thr1, NULL, child1, (void*)bar3);
   /* guarantee that it gets there first */
@@ -61,6 +68,12 @@ int main ( void )
   /* once again, create a thread, whose only purpose is to block. */
   bar4 = malloc(sizeof(pthread_barrier_t));
   pthread_barrier_init(bar4, NULL, 2);
+  /* create a thread, whose purpose is to "unblock" the barrier after
+     some sleeping in case it keeps being blocked. We hope it isn't
+     needed, but if it is, because pthread_barier_destroy hangs
+     and we will get an extra warning about the barrier being already
+     destroyed. */
+  pthread_create(&slp2, NULL, sleep1, (void*)bar4);
   /* create a thread, whose only purpose is to block on the barrier */
   pthread_create(&thr2, NULL, child1, (void*)bar4);
   /* guarantee that it gets there first */
@@ -70,13 +83,16 @@ int main ( void )
 
   /* destroy a barrier that was never initialised.  This is a bit
      tricky, in that we have to fill the barrier with bytes which
-     ensure that the pthread_barrier_destroy call doesn't hang for
-     some reason.  Zero-fill seems to work ok on amd64-linux (glibc
+     ensure that the pthread_barrier_destroy call doesn't crash for
+     some reason.  One-fill seems to work ok on amd64-linux (glibc
      2.8). */
   fprintf(stderr, "\ndestroy a barrier that was never initialised\n");
+  /* Create a thread that just exits the process after some sleep.
+     We are really done at this point, even if we hang. */
+  pthread_create(&ext1, NULL, exit1, NULL);
   bar5 = malloc(sizeof(pthread_barrier_t));
   assert(bar5);
-  memset(bar5, 0, sizeof(*bar5));
+  memset(bar5, 1, sizeof(*bar5));
   pthread_barrier_destroy(bar5);
 
   /* now we need to clean up the mess .. */
@@ -85,5 +101,6 @@ int main ( void )
 
   free(bar1); free(bar2); free(bar3); free(bar4); free(bar5);
 
-  return 0;
+  /* Use exit, we want to kill any "sleeper threads". */
+  exit (0);
 }
diff --git a/helgrind/tests/bar_bad.stderr.exp b/helgrind/tests/bar_bad.stderr.exp
index 74af4fa..d0901b2 100644
--- a/helgrind/tests/bar_bad.stderr.exp
+++ b/helgrind/tests/bar_bad.stderr.exp
@@ -8,14 +8,14 @@ Thread #x is the program's root thread
 
 Thread #x: pthread_barrier_init: 'count' argument is zero
    at 0x........: pthread_barrier_init (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:39)
+   by 0x........: main (bar_bad.c:43)
 
 ----------------------------------------------------------------
 
 Thread #x's call to pthread_barrier_init failed
    with error code 22 (EINVAL: Invalid argument)
    at 0x........: pthread_barrier_init (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:39)
+   by 0x........: main (bar_bad.c:43)
 
 
 initialise a barrier twice
@@ -23,7 +23,7 @@ initialise a barrier twice
 
 Thread #x: pthread_barrier_init: barrier is already initialised
    at 0x........: pthread_barrier_init (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:45)
+   by 0x........: main (bar_bad.c:49)
 
 
 initialise a barrier which has threads waiting on it
@@ -31,13 +31,13 @@ initialise a barrier which has threads waiting on it
 
 Thread #x: pthread_barrier_init: barrier is already initialised
    at 0x........: pthread_barrier_init (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:57)
+   by 0x........: main (bar_bad.c:64)
 
 ----------------------------------------------------------------
 
 Thread #x: pthread_barrier_init: threads are waiting at barrier
    at 0x........: pthread_barrier_init (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:57)
+   by 0x........: main (bar_bad.c:64)
 
 
 destroy a barrier that has waiting threads
@@ -45,14 +45,14 @@ destroy a barrier that has waiting threads
 
 Thread #x: pthread_barrier_destroy: threads are waiting at barrier
    at 0x........: pthread_barrier_destroy (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:69)
+   by 0x........: main (bar_bad.c:82)
 
 ----------------------------------------------------------------
 
 Thread #x's call to pthread_barrier_destroy failed
    with error code 16 (EBUSY: Device or resource busy)
    at 0x........: pthread_barrier_destroy (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:69)
+   by 0x........: main (bar_bad.c:82)
 
 
 destroy a barrier that was never initialised
@@ -60,5 +60,5 @@ destroy a barrier that was never initialised
 
 Thread #x: pthread_barrier_destroy: barrier was never initialised
    at 0x........: pthread_barrier_destroy (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:80)
+   by 0x........: main (bar_bad.c:96)
 
diff --git a/helgrind/tests/bar_bad.stderr.exp b/helgrind/tests/bar_bad.stderr.exp-destroy-hang
similarity index 72%
copy from helgrind/tests/bar_bad.stderr.exp
copy to helgrind/tests/bar_bad.stderr.exp-destroy-hang
index 74af4fa..ddf5624 100644
--- a/helgrind/tests/bar_bad.stderr.exp
+++ b/helgrind/tests/bar_bad.stderr.exp-destroy-hang
@@ -8,14 +8,14 @@ Thread #x is the program's root thread
 
 Thread #x: pthread_barrier_init: 'count' argument is zero
    at 0x........: pthread_barrier_init (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:39)
+   by 0x........: main (bar_bad.c:43)
 
 ----------------------------------------------------------------
 
 Thread #x's call to pthread_barrier_init failed
    with error code 22 (EINVAL: Invalid argument)
    at 0x........: pthread_barrier_init (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:39)
+   by 0x........: main (bar_bad.c:43)
 
 
 initialise a barrier twice
@@ -23,7 +23,7 @@ initialise a barrier twice
 
 Thread #x: pthread_barrier_init: barrier is already initialised
    at 0x........: pthread_barrier_init (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:45)
+   by 0x........: main (bar_bad.c:49)
 
 
 initialise a barrier which has threads waiting on it
@@ -31,13 +31,13 @@ initialise a barrier which has threads waiting on it
 
 Thread #x: pthread_barrier_init: barrier is already initialised
    at 0x........: pthread_barrier_init (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:57)
+   by 0x........: main (bar_bad.c:64)
 
 ----------------------------------------------------------------
 
 Thread #x: pthread_barrier_init: threads are waiting at barrier
    at 0x........: pthread_barrier_init (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:57)
+   by 0x........: main (bar_bad.c:64)
 
 
 destroy a barrier that has waiting threads
@@ -45,14 +45,22 @@ destroy a barrier that has waiting threads
 
 Thread #x: pthread_barrier_destroy: threads are waiting at barrier
    at 0x........: pthread_barrier_destroy (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:69)
+   by 0x........: main (bar_bad.c:82)
+
+---Thread-Announcement------------------------------------------
+
+Thread #x was created
+   ...
+   by 0x........: pthread_create@* (hg_intercepts.c:...)
+   by 0x........: main (bar_bad.c:76)
 
 ----------------------------------------------------------------
 
-Thread #x's call to pthread_barrier_destroy failed
-   with error code 16 (EBUSY: Device or resource busy)
-   at 0x........: pthread_barrier_destroy (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:69)
+Thread #x: pthread_barrier_wait: barrier is uninitialised
+   at 0x........: pthread_barrier_wait (hg_intercepts.c:...)
+   by 0x........: sleep1 (bar_bad.c:22)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
 
 
 destroy a barrier that was never initialised
@@ -60,5 +68,5 @@ destroy a barrier that was never initialised
 
 Thread #x: pthread_barrier_destroy: barrier was never initialised
    at 0x........: pthread_barrier_destroy (hg_intercepts.c:...)
-   by 0x........: main (bar_bad.c:80)
+   by 0x........: main (bar_bad.c:96)
 
-- 
1.8.3.1