gnuastro-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnuastro-commits] master ad70428 2/2: pthread_barrier_t re-usable after


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master ad70428 2/2: pthread_barrier_t re-usable after pthread_barrier_destroy
Date: Sat, 10 Sep 2016 12:32:04 +0000 (UTC)

branch: master
commit ad704286459b8ea7619c2d8a0ccbf22e320bd01f
Author: Mohammad Akhlaghi <address@hidden>
Commit: Mohammad Akhlaghi <address@hidden>

    pthread_barrier_t re-usable after pthread_barrier_destroy
    
    Barriers can greatly simplify the process of spinning off threads and
    waiting for them to finish. They allow us to define detached threads (where
    no communication is needed between the threads). However, they are a
    high-level pthreads construct and are thus optional in POSIX. Therefore
    they are not present in some OSs. So in Gnuastro we had an internal
    implementation.
    
    It was reported by Alan Lefor that on one of his Mac OS X systems, `make
    check' hangs and doens't finish. Since it could only be reproduced on his
    system, it took us several days of successive tests to narrow down the
    problem and find the cause.
    
    In our implementation of pthread_barrier, (which was inspired from another
    webpage), there was one hidden assumption: we won't be using the barrier
    any more. So the `pthread_barrier_destroy' function would just destroy the
    mutex and condition variable when the `pthread_barrier_wait' of the main
    thread-spinner function was freed. However, all the separate thread
    functions are also waiting behind the `pthread_barrier_wait' functions and
    there is no particular order to distinguish the thread-spinner's wait
    function from the separate threads. So it was possible that the
    thread-spinner's wait would be freed before the threads. Therefore, those
    threads that would get freed after the thread-spinner would hit an
    un-initialized mutex and condition variable.
    
    To fix this problem, a new element was added to the pthread_barrier
    function to count how many condition variables have been freed (are no
    longer needed). The `pthread_barrier_destroy' function (which is only
    called once by the thread-spinner) now waits (using the `sleep' function)
    until no more threads need the condition variables and only after they are
    all done, will it destroy the condition variable and mutex. The wait is
    1000 nano-seconds or 1 micro-second, and is only necessary when the
    thread-spinner gets to destroy, so its effect on performance can be
    negligible.
    
    This fixes bug #49049.
---
 lib/gnuastro/threads.h |   14 ++++++---
 lib/threads.c          |   75 ++++++++++++++++++++++++++++++++++--------------
 2 files changed, 64 insertions(+), 25 deletions(-)

diff --git a/lib/gnuastro/threads.h b/lib/gnuastro/threads.h
index d0b276b..3a1db59 100644
--- a/lib/gnuastro/threads.h
+++ b/lib/gnuastro/threads.h
@@ -49,6 +49,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /*****************************************************************/
 #ifndef HAVE_PTHREAD_BARRIER
 
+/* Integer number of nano-seconds that `pthread_barrier_destroy' should
+   wait for a check to see if all barriers have been reached. */
+#define GAL_THREADS_BARRIER_DESTROY_NANOSECS 1000
+
 typedef int pthread_barrierattr_t;
 
 typedef struct
@@ -56,18 +60,20 @@ typedef struct
   pthread_mutex_t mutex;
   pthread_cond_t   cond;
   size_t          count;
-  size_t      tripCount;
+  size_t          limit;
+  size_t   condfinished;
 } pthread_barrier_t;
 
 int
 pthread_barrier_init(pthread_barrier_t *b, pthread_barrierattr_t *attr,
-                     unsigned int count);
-int
-pthread_barrier_destroy(pthread_barrier_t *b);
+                     unsigned int limit);
 
 int
 pthread_barrier_wait(pthread_barrier_t *b);
 
+int
+pthread_barrier_destroy(pthread_barrier_t *b);
+
 #endif
 
 
diff --git a/lib/threads.c b/lib/threads.c
index 3d8ca26..89ff98b 100644
--- a/lib/threads.c
+++ b/lib/threads.c
@@ -22,6 +22,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 **********************************************************************/
 #include <config.h>
 
+#include <time.h>
 #include <stdio.h>
 #include <errno.h>
 #include <error.h>
@@ -42,17 +43,20 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 http://blog.albertarmea.com/post/47089939939/using-pthread-barrier-on-mac-os-x
  */
 #ifndef HAVE_PTHREAD_BARRIER
+
+/* Initialize the barrier structure. A barrier is a high-level way to wait
+   until several threads have finished. */
 int
 pthread_barrier_init(pthread_barrier_t *b, pthread_barrierattr_t *attr,
-                     unsigned int count)
+                     unsigned int limit)
 {
   int err;
 
   /* Sanity check: */
-  if(count==0)
+  if(limit==0)
     {
       errno = EINVAL;
-      error(EXIT_FAILURE, errno, "in pthread_barrier_init, count is zero");
+      error(EXIT_FAILURE, errno, "in pthread_barrier_init, limit is zero");
     }
 
   /* Initialize the mutex: */
@@ -69,21 +73,9 @@ pthread_barrier_init(pthread_barrier_t *b, 
pthread_barrierattr_t *attr,
     }
 
   /* set the values: */
-  b->tripCount=count;
-  b->count=0;
-
-  return 0;
-}
-
-
-
+  b->limit=limit;
+  b->condfinished=b->count=0;
 
-
-int
-pthread_barrier_destroy(pthread_barrier_t *b)
-{
-  pthread_cond_destroy(&b->cond);
-  pthread_mutex_destroy(&b->mutex);
   return 0;
 }
 
@@ -91,25 +83,66 @@ pthread_barrier_destroy(pthread_barrier_t *b)
 
 
 
+/* Suspend the calling thread (tell it to wait), until the limiting number
+   of barriers is reached by the other threads. When the number isn't
+   reached yet (process goes into the `else'), then we use the
+   `pthread_cond_wait' function, which will wait until a broadcast is
+   announced by another thread that succeeds the `if'. After the thread no
+   longer needs the condition variable, we increment `b->condfinished' so
+   `pthread_barrier_destroy' can know if it should wait (sleep) or
+   continue.*/
 int
 pthread_barrier_wait(pthread_barrier_t *b)
 {
   pthread_mutex_lock(&b->mutex);
-  ++(b->count);
-  if(b->count >= b->tripCount)
+  ++b->count;
+  if(b->count >= b->limit)
     {
-      b->count = 0;
       pthread_cond_broadcast(&b->cond);
+      ++b->condfinished;
       pthread_mutex_unlock(&b->mutex);
       return 1;
     }
   else
     {
-      pthread_cond_wait(&b->cond, &(b->mutex));
+      /* Initially b->count is smaller than b->limit, otherwise control
+         would never have reached here. However, when it get to
+         `pthread_cond_wait', this thread goes into a suspended period and
+         is only awaken when a broad-cast is made. But we only want this
+         thread to finish when b->count >= b->limit, so we have this while
+         loop. */
+      while(b->count < b->limit)
+        pthread_cond_wait(&b->cond, &b->mutex);
+      ++b->condfinished;
       pthread_mutex_unlock(&b->mutex);
       return 0;
     }
 }
+
+
+
+
+
+/* Wait until all the threads that were blocked behind this barrier have
+   finished (don't need the mutex and condition variable anymore. Then
+   destroy the two. After this function, you can safely re-use the
+   pthread_barrier_t structure. */
+int
+pthread_barrier_destroy(pthread_barrier_t *b)
+{
+  struct timespec request, remaining;
+
+  /* Wait until no more threads need the barrier. */
+  request.tv_sec=0;
+  request.tv_nsec=GAL_THREADS_BARRIER_DESTROY_NANOSECS;
+  while( b->condfinished < b->limit )
+    nanosleep(&request, &remaining);
+
+  /* Destroy the condition variable and mutex */
+  pthread_cond_destroy(&b->cond);
+  pthread_mutex_destroy(&b->mutex);
+  return 0;
+}
 #endif
 
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]