This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH][libgomp/gomp-3_0-branch] Support for OpenMP in user threads, and pooling for nested threads


This patch for gomp-3_0-branch enables the following functionality.
-OpenMP parallel regions in multiple concurrent user (p)threads.
-Thread pooling for nested parallel regions.

Details:
-The code is simplified since nested threads are not treated differently
from top-level threads.
-A pool thread waits at only exactly one barrier between invocations.
-One thread pool is held per user thread. This allows very efficient
allocation of threads. Since the pool cannot be deallocated at the end
of a user thread (determination of end not safely possible), the pools
are either recycled or garbage collected when allocating a new one.
Thus, the number of (inactive) pool threads stays bounded by the number
of OpenMP threads in concurrently working user threads.
-By the above approach, threadprivate semantics are guaranteed.
-The "processor" overload heuristic for blocking threads does not count the idle pool threads.


Performance according to the CLOMP benchmark:

3gomp-newthreadpool (our patch):
./clomp_xgcc_i10pc121 8 1 64 100 32 1 100, calc_deposit, OMP Barrier,
Serial Ref, Bestcase OMP, Static OMP, Dynamic OMP, Manual OMP
Runtime, 0.0117879, 0.172061, 4.00551, 0.544991, 1.58542, 10.7785, 1.11315
us/Loop, 0.0754425, 1.10119, 25.6353, 3.48794, 10.1467, 68.9825, 7.12416
Speedup, N/A, N/A, 1, 7.34968, 2.52646, 0.37162, 3.59836
Efficacy, N/A, N/A, N/A, 100.00%, 34.38%, 5.06%, 48.96%
Overhead, N/A, N/A, N/A, 0.00, 6.66, 65.49, 3.64

3gomp:
./clomp_gcc_i10pc121 8 1 64 100 32 1 100, calc_deposit, OMP Barrier,
Serial Ref, Bestcase OMP, Static OMP, Dynamic OMP, Manual OMP
Runtime, 0.0117931, 0.159399, 4.0048, 0.665463, 1.50918, 11.2974, 1.41252
us/Loop, 0.0754761, 1.02015, 25.6307, 4.25896, 9.65873, 72.3033, 9.04011
Speedup, N/A, N/A, 1, 6.01806, 2.65363, 0.354488, 2.83522
Efficacy, N/A, N/A, N/A, 100.00%, 44.09%, 5.89%, 47.11%
Overhead, N/A, N/A, N/A, 0.00, 5.40, 68.04, 4.78


Performance according to the EPCC microbenchmark: -The parallel construct gets 27% slower (3,2->4,4 microseconds). However, this is still *much* faster (factors) than the current trunk implementation. -All other constructs remain unchanged.

Code contributed by Jakob Blomer (jakob.blomer@ira.uka.de), supervised
by Johannes Singler (singler@ira.uka.de).

Tested x86_64-unknown-linux-gnu: No regressions
        i386-apple-darwin9.2.2: No regressions
        i686-pc-linux-gnu: No regressions

Please comment and/or approve.

2008-04-24 Jakob Blomer <jakob.blomer@ira.uka.de>

            * team.c: Thread-safe thread pool,
              thread pool for nested threads.
              (gomp_team_start) Create thread pool on demand
              for user-created pthreads,
              garbage collection of thread pool of terminated
              pthreads,
              barrier at end of function eliminated,
              gomp_managed_threads does not take idle pool
              threads into account
              (gomp_team_end) Put worker threads back into pool
              gomp.
              (gomp_new_team) Allocate memory for worker threads
              array, will be automatically freed in free_team.
              (initialize_team) Initialize global user pthread
              directory.
              New static functions: gomp_new_thread_pool,
              gomp_free_thread_pool
            * libgomp.h: Thread-pool data structure,
              new pointer in struct gomp_thread to thread pool.
            * $arch/mutex.h:
              New function gomp_init_mutex_locked
            * $arch/sem.h:
              New synchronization primitive signal, which is a
              binary semaphore resp. a wrapper for mutex.
              New inlined functions: gomp_signal_init,
              gomp_signal_wait, gomp_signal_post,
              gomp_signal_destroy.

Jakob & Johannes


Index: team.c
===================================================================
--- team.c	(Revision 122)
+++ team.c	(Arbeitskopie)
@@ -31,18 +31,21 @@
 #include "libgomp.h"
 #include <stdlib.h>
 #include <string.h>
+#include <signal.h>
+#include <errno.h>
 
-/* This array manages threads spawned from the top level, which will
-   return to the idle loop once the current PARALLEL construct ends.  */
-static struct gomp_thread **gomp_threads;
-static unsigned gomp_threads_size;
-static unsigned gomp_threads_used;
-
 /* This attribute contains PTHREAD_CREATE_DETACHED.  */
 pthread_attr_t gomp_thread_attr;
 
-/* This barrier holds and releases threads waiting in gomp_threads.  */
-static gomp_barrier_t gomp_threads_dock;
+struct user_pthread_info
+{
+  pthread_t thread;
+  struct gomp_thread_pool *pool;
+};
+/* Global directory of user pthreads with a OpenMP thread pool */
+unsigned num_user_pthreads = 0;
+struct user_pthread_info *user_pthreads = NULL;
+gomp_mutex_t user_pthreads_lock;
 
 /* This is the libgomp per-thread data structure.  */
 #ifdef HAVE_TLS
@@ -60,10 +63,11 @@
   void *fn_data;
   struct gomp_team_state ts;
   struct gomp_task *task;
+  struct gomp_thread_pool *thread_pool;
   bool nested;
+  gomp_signal_t thread_created;
 };
 
-
 /* This function is a pthread_create entry point.  This contains the idle
    loop in which a thread waits to be called up to become part of a team.  */
 
@@ -83,54 +87,39 @@
   pthread_setspecific (gomp_tls_key, thr);
 #endif
   gomp_sem_init (&thr->release, 0);
+  /* event is fired when threads are to be woken up */
+  gomp_signal_init (&thr->pool_release, 0);
 
   /* Extract what we need from data.  */
-  local_fn = data->fn;
-  local_data = data->fn_data;
+  thr->fn = data->fn;
+  thr->data = data->fn_data;
   thr->ts = data->ts;
   thr->task = data->task;
+  thr->thread_pool = data->thread_pool;
 
   thr->ts.team->ordered_release[thr->ts.team_id] = &thr->release;
+  thr->ts.team->workers[thr->ts.team_id - 1] = thr;
 
-  if (data->nested)
+  gomp_signal_post (&data->thread_created);
+  /* Loop as long as thread is in the thread pool.
+    Thread pool is deallocated by garbage collection (gomp_team_start). */
+  do
     {
-      gomp_barrier_wait (&thr->ts.team->barrier);
+      /* wait until woken up */
+      gomp_signal_wait (&thr->pool_release);
+
+      local_fn = thr->fn;
+      if (__builtin_expect(local_fn == NULL, 0))
+        break;
+      local_data = thr->data;
+      /* Execute OMP parallel region (user code). */
       local_fn (local_data);
-      gomp_barrier_wait_last (&thr->ts.team->barrier);
-    }
-  else
-    {
-      gomp_threads[thr->ts.team_id] = thr;
+      gomp_end_task (); 
 
-      gomp_barrier_wait (&gomp_threads_dock);
-      do
-	{
-	  struct gomp_team *team;
+      thr->fn = NULL;
 
-	  local_fn (local_data);
-	  gomp_end_task ();
-
-	  /* Clear out the team and function data.  This is a debugging
-	     signal that we're in fact back in the dock.  */
-	  team = thr->ts.team;
-	  thr->fn = NULL;
-	  thr->data = NULL;
-	  thr->ts.team = NULL;
-	  thr->ts.work_share = NULL;
-	  thr->ts.last_work_share = NULL;
-	  thr->ts.team_id = 0;
-	  thr->ts.level = 0;
-	  thr->ts.active_level = 0;
-
-	  gomp_barrier_wait_last (&team->barrier);
-	  gomp_barrier_wait (&gomp_threads_dock);
-
-	  local_fn = thr->fn;
-	  local_data = thr->data;
-	}
-      while (local_fn);
-    }
-
+      gomp_barrier_wait_last (&thr->ts.team->barrier);
+    } while (local_fn);
   return NULL;
 }
 
@@ -145,7 +134,8 @@
   int i;
 
   size = sizeof (*team) + nthreads * (sizeof (team->ordered_release[0])
-				      + sizeof (team->implicit_task[0]));
+				      + sizeof (team->implicit_task[0])
+                                      + sizeof (team->workers[0]));
   team = gomp_malloc (size);
 
   team->work_share_chunk = 8;
@@ -169,6 +159,8 @@
   team->ordered_release = (void *) &team->implicit_task[nthreads];
   team->ordered_release[0] = &team->master_release;
 
+  team->workers = (void *) &team->ordered_release[nthreads];
+
   return team;
 }
 
@@ -198,6 +190,37 @@
 }
 
 
+/* Allocate and initialize thread pool. */
+
+static struct gomp_thread_pool
+*gomp_new_thread_pool (void)
+{
+  struct gomp_thread_pool *pool =
+      gomp_malloc (sizeof(struct gomp_thread_pool));
+
+  gomp_mutex_init (&pool->lock);
+  pool->threads = gomp_malloc (sizeof(void *));
+  pool->size = 1;
+  pool->capacity = 1;
+
+  return pool;
+}
+
+/* Terminate pool threads and free thread pool. */
+static void
+gomp_free_thread_pool (struct gomp_thread_pool *pool)
+{
+  int i;
+
+  for (i = 1; i < pool->size; i++)
+    {
+      gomp_signal_post (&pool->threads[i]->pool_release);
+    }
+  gomp_mutex_destroy (&pool->lock);
+  free (pool->threads);
+  free (pool);
+}
+
 /* Launch a team.  */
 
 void
@@ -208,15 +231,77 @@
   struct gomp_thread *thr, *nthr;
   struct gomp_task *task;
   struct gomp_task_icv *icv;
+  struct gomp_thread_pool *thread_pool;
   bool nested;
-  unsigned i, n, old_threads_used = 0;
+  unsigned i, n;
   pthread_attr_t thread_attr, *attr;
 
   thr = gomp_thread ();
   nested = thr->ts.team != NULL;
   task = thr->task;
   icv = task ? &task->icv : &gomp_global_icv;
+  /* Check whether a thread pool for current user pthread has to be created.
+     I. e., is this the first parallel region within a new user pthread? */
+  if (__builtin_expect(thr->thread_pool == NULL, 0))
+    {
+      /* Garbage collection */
+      int i;
+      int freed_threads = 0;
+      gomp_mutex_lock (&user_pthreads_lock);
+      for (i = 0; i < num_user_pthreads; i++)
+        {
+          /* Is this pthread re-used? */
+          if (pthread_equal (pthread_self (), user_pthreads[i].thread))
+            {
+              /* perfect, we take its thread pool */
+              thr->thread_pool = user_pthreads[i].pool;
+            }
 
+          /* Do we have a thread pool whose user pthread is non-existent? */
+          if (pthread_kill (user_pthreads[i].thread, 0) == ESRCH)
+            {
+              /* clean up and mark */
+              gomp_free_thread_pool (user_pthreads[i].pool);
+              user_pthreads[i].pool = NULL;
+              freed_threads++;
+            }
+        }
+
+      /* Compress thread-pool directory */
+      if (freed_threads > 0)
+        {
+          struct user_pthread_info *new_user_pthreads = gomp_malloc (
+            (num_user_pthreads - freed_threads) * sizeof(user_pthreads[0]));
+          int new_i = 0;
+          for (i = 0; i < num_user_pthreads; i++)
+            {
+              if (user_pthreads[i].pool != NULL)
+                new_user_pthreads[new_i++] = user_pthreads[i];
+            }
+          free (user_pthreads);
+          user_pthreads = new_user_pthreads;
+          num_user_pthreads -= freed_threads;
+        }
+
+      /* Do we have do create a new pool? If yes, this thread was not re-used 
+         by the system. */
+      if (thr->thread_pool == NULL)
+        {
+          thr->thread_pool = gomp_new_thread_pool ();
+      
+          /* append to global thread pool directory */
+          if (num_user_pthreads++ == 0)
+            user_pthreads = gomp_malloc (sizeof(struct user_pthread_info));
+          else
+            user_pthreads = gomp_realloc (user_pthreads, 
+              num_user_pthreads * sizeof(struct user_pthread_info));
+          user_pthreads[num_user_pthreads-1].thread = pthread_self ();
+          user_pthreads[num_user_pthreads-1].pool = thr->thread_pool;
+        }  
+      gomp_mutex_unlock (&user_pthreads_lock);
+    }
+  thread_pool = thr->thread_pool;
+
   /* Always save the previous state, even if this isn't a nested team.
      In particular, we should save any work share state from an outer
      orphaned work share construct.  */
@@ -240,89 +325,42 @@
     return;
 
   i = 1;
-
-  /* We only allow the reuse of idle threads for non-nested PARALLEL
-     regions.  This appears to be implied by the semantics of
-     threadprivate variables, but perhaps that's reading too much into
-     things.  Certainly it does prevent any locking problems, since
-     only the initial program thread will modify gomp_threads.  */
-  if (!nested)
-    {
-      old_threads_used = gomp_threads_used;
-
-      if (nthreads <= old_threads_used)
-	n = nthreads;
-      else if (old_threads_used == 0)
-	{
-	  n = 0;
-	  gomp_barrier_init (&gomp_threads_dock, nthreads);
-	}
-      else
-	{
-	  n = old_threads_used;
-
-	  /* Increase the barrier threshold to make sure all new
-	     threads arrive before the team is released.  */
-	  gomp_barrier_reinit (&gomp_threads_dock, nthreads);
-	}
-
-      /* Not true yet, but soon will be.  We're going to release all
-	 threads from the dock, and those that aren't part of the 
-	 team will exit.  */
-      gomp_threads_used = nthreads;
-
-      /* Release existing idle threads.  */
-      for (; i < n; ++i)
-	{
-	  nthr = gomp_threads[i];
-	  nthr->ts.team = team;
-	  nthr->ts.work_share = &team->work_shares[0];
-	  nthr->ts.last_work_share = NULL;
-	  nthr->ts.team_id = i;
-	  nthr->ts.level = team->prev_ts.level + 1;
-	  nthr->ts.active_level = thr->ts.active_level;
 #ifdef HAVE_SYNC_BUILTINS
-	  nthr->ts.single_count = 0;
+   __sync_fetch_and_add (&gomp_managed_threads, nthreads-1L);
+#else
+   gomp_mutex_lock (&gomp_remaining_threads_lock);
+   gomp_managed_threads += nthreads-1;
+   gomp_mutex_unlock (&gomp_remaining_threads_lock);
 #endif
-	  nthr->ts.static_trip = 0;
-	  nthr->task = &team->implicit_task[i];
-	  gomp_init_task (nthr->task, task, icv);
-	  nthr->fn = fn;
-	  nthr->data = data;
-	  team->ordered_release[i] = &nthr->release;
-	}
 
-      if (i == nthreads)
-	goto do_release;
-
-      /* If necessary, expand the size of the gomp_threads array.  It is
-	 expected that changes in the number of threads is rare, thus we
-	 make no effort to expand gomp_threads_size geometrically.  */
-      if (nthreads >= gomp_threads_size)
-	{
-	  gomp_threads_size = nthreads + 1;
-	  gomp_threads
-	    = gomp_realloc (gomp_threads,
-			    gomp_threads_size
-			    * sizeof (struct gomp_thread_data *));
-	}
-    }
-
-  if (__builtin_expect (nthreads > old_threads_used, 0))
+  struct gomp_thread **workers = team->workers;
+  gomp_mutex_lock (&thread_pool->lock);
+  int pool_size = thread_pool->size;
+  n = (nthreads < pool_size) ? nthreads : pool_size;
+  for (; i < n; ++i)
     {
-      long diff = (long) nthreads - (long) old_threads_used;
-
-      if (old_threads_used == 0)
-	--diff;
-
+      nthr = thread_pool->threads[pool_size-i];
+ 
+      nthr->thread_pool = thread_pool;
+      workers[i-1] = nthr;
+      nthr->ts.team = team;
+      nthr->ts.work_share = &team->work_shares[0];
+      nthr->ts.last_work_share = NULL;
+      nthr->ts.team_id = i;
+      nthr->ts.level = team->prev_ts.level + 1;
+      nthr->ts.active_level = thr->ts.active_level;
 #ifdef HAVE_SYNC_BUILTINS
-      __sync_fetch_and_add (&gomp_managed_threads, diff);
-#else
-      gomp_mutex_lock (&gomp_remaining_threads_lock);
-      gomp_managed_threads += diff;
-      gomp_mutex_unlock (&gomp_remaining_threads_lock);
+      nthr->ts.single_count = 0;
 #endif
+      nthr->ts.static_trip = 0;
+      nthr->task = &team->implicit_task[i];
+      gomp_init_task (nthr->task, task, icv);
+      nthr->fn = fn;
+      nthr->data = data;
+      team->ordered_release[i] = &nthr->release;
     }
+  thread_pool->size -= n-1;
+  gomp_mutex_unlock (&thread_pool->lock);
 
   attr = &gomp_thread_attr;
   if (__builtin_expect (gomp_cpu_affinity != NULL, 0))
@@ -336,14 +374,16 @@
     }
 
   start_data = gomp_alloca (sizeof (struct gomp_thread_start_data)
-			    * (nthreads-i));
-
+                            * (nthreads-i));
   /* Launch new threads.  */
-  for (; i < nthreads; ++i, ++start_data)
+  for (; i < nthreads; ++i,++start_data)
     {
       pthread_t pt;
       int err;
 
+      gomp_signal_init (&start_data->thread_created, 0);
+      start_data->thread_pool = thread_pool;
+      /* team->workers will be set in gomp_thread_start */
       start_data->fn = fn;
       start_data->fn_data = data;
       start_data->ts.team = team;
@@ -366,43 +406,28 @@
       err = pthread_create (&pt, attr, gomp_thread_start, start_data);
       if (err != 0)
 	gomp_fatal ("Thread creation failed: %s", strerror (err));
+      gomp_signal_wait (&start_data->thread_created);
+      gomp_signal_destroy (&start_data->thread_created);
     }
 
+  /* Wake up worker threads. */
+  for (i = 0; i < nthreads-1; ++i)
+    gomp_signal_post (&workers[i]->pool_release); 
+  
   if (__builtin_expect (gomp_cpu_affinity != NULL, 0))
     pthread_attr_destroy (&thread_attr);
-
- do_release:
-  gomp_barrier_wait (nested ? &team->barrier : &gomp_threads_dock);
-
-  /* Decrease the barrier threshold to match the number of threads
-     that should arrive back at the end of this team.  The extra
-     threads should be exiting.  Note that we arrange for this test
-     to never be true for nested teams.  */
-  if (__builtin_expect (nthreads < old_threads_used, 0))
-    {
-      long diff = (long) nthreads - (long) old_threads_used;
-
-      gomp_barrier_reinit (&gomp_threads_dock, nthreads);
-
-#ifdef HAVE_SYNC_BUILTINS
-      __sync_fetch_and_add (&gomp_managed_threads, diff);
-#else
-      gomp_mutex_lock (&gomp_remaining_threads_lock);
-      gomp_managed_threads += diff;
-      gomp_mutex_unlock (&gomp_remaining_threads_lock);
-#endif
-    }
 }
 
 
 /* Terminate the current team.  This is only to be called by the master
    thread.  We assume that we must wait for the other threads.  */
-
 void
 gomp_team_end (void)
 {
   struct gomp_thread *thr = gomp_thread ();
   struct gomp_team *team = thr->ts.team;
+  struct gomp_thread_pool *thread_pool = thr->thread_pool;
+  int num_workers = team->nthreads - 1;
 
   gomp_barrier_wait (&team->barrier);
 
@@ -411,21 +436,43 @@
   gomp_end_task ();
   thr->ts = team->prev_ts;
 
-  if (__builtin_expect (thr->ts.team != NULL, 0))
+  if (__builtin_expect(num_workers > 0, 1))
     {
 #ifdef HAVE_SYNC_BUILTINS
-      __sync_fetch_and_add (&gomp_managed_threads, 1L - team->nthreads);
+      __sync_fetch_and_add (&gomp_managed_threads, -num_workers);
 #else
       gomp_mutex_lock (&gomp_remaining_threads_lock);
-      gomp_managed_threads -= team->nthreads - 1L;
+      gomp_managed_threads -= num_workers;
       gomp_mutex_unlock (&gomp_remaining_threads_lock);
 #endif
+
+      int i;
+      struct gomp_thread **workers = team->workers;
+
+      gomp_mutex_lock (&thread_pool->lock);
+      if (__builtin_expect((1 + thread_pool->size + num_workers) > 
+          thread_pool->capacity, 0)) 
+        {
+          thread_pool->capacity = 1 + thread_pool->size + num_workers;
+          thread_pool->threads = gomp_realloc (thread_pool->threads, 
+            thread_pool->capacity * sizeof(struct gomp_thread *));
+        }
+      int size = thread_pool->size;
+      struct gomp_thread **threads = thread_pool->threads;
+      /* Put the workers back int the thread pool. */
+      for (i = num_workers-1; i >= 0; i--)
+        threads[size++] = workers[i];
+      thread_pool->size += num_workers;
+      gomp_mutex_unlock (&thread_pool->lock);
     }
+      
+  thr->ts = team->prev_ts;
 
   free_team (team);
 }
 
 
+
 /* Constructors for this file.  */
 
 static void __attribute__((constructor))
@@ -446,4 +493,5 @@
   thr = &initial_thread_tls_data;
 #endif
   gomp_sem_init (&thr->release, 0);
+  gomp_mutex_init (&user_pthreads_lock);
 }
Index: libgomp.h
===================================================================
--- libgomp.h	(Revision 122)
+++ libgomp.h	(Arbeitskopie)
@@ -248,6 +248,10 @@
      parallels, as the master is a member of two teams.  */
   gomp_sem_t master_release;
 
+  /* This array contains all the worker threads to be able to put them back
+     into the pool. */
+  struct gomp_thread **workers;
+
   /* This points to an array with pointers to the release semaphore
      of the threads in the team.  */
   gomp_sem_t **ordered_release;
@@ -303,8 +307,22 @@
 
   /* This semaphore is used for ordered loops.  */
   gomp_sem_t release;
+  
+  /* This points to the respective thread pool */
+  struct gomp_thread_pool *thread_pool;
+
+  /* This signal is to wake up a thread in the thread pool */
+  gomp_signal_t pool_release;
 };
 
+struct gomp_thread_pool
+{
+  struct gomp_thread **threads;
+  int size;
+  int capacity;
+  gomp_mutex_t lock;
+};
+
 /* ... and here is that TLS data.  */
 
 #ifdef HAVE_TLS
Index: config/linux/mutex.h
===================================================================
--- config/linux/mutex.h	(Revision 122)
+++ config/linux/mutex.h	(Arbeitskopie)
@@ -41,6 +41,11 @@
   *mutex = 0;
 }
 
+static inline void gomp_mutex_init_locked (gomp_mutex_t *mutex)
+{
+  *mutex = 1;
+}
+
 extern void gomp_mutex_lock_slow (gomp_mutex_t *mutex);
 static inline void gomp_mutex_lock (gomp_mutex_t *mutex)
 {
Index: config/linux/sem.h
===================================================================
--- config/linux/sem.h	(Revision 122)
+++ config/linux/sem.h	(Arbeitskopie)
@@ -32,8 +32,36 @@
 #ifndef GOMP_SEM_H
 #define GOMP_SEM_H 1
 
+#include "mutex.h"
+
 typedef int gomp_sem_t;
+/* This is a binary semaphore aka signal,
+   technically just a wrapper for a mutex */
+typedef gomp_mutex_t gomp_signal_t;
 
+static inline void gomp_signal_init (gomp_signal_t *signal, int fired)
+{
+  if (fired == 0)
+    gomp_mutex_init_locked (signal);
+  else
+    gomp_mutex_init (signal);
+}
+
+static inline void gomp_signal_wait (gomp_signal_t *signal)
+{
+  gomp_mutex_lock (signal);
+}
+
+static inline void gomp_signal_post (gomp_signal_t *signal)
+{
+  gomp_mutex_unlock (signal);
+}
+
+static inline void gomp_signal_destroy (gomp_signal_t *signal)
+{
+  gomp_mutex_destroy (signal);
+}
+
 static inline void gomp_sem_init (gomp_sem_t *sem, int value)
 {
   *sem = value;
Index: config/posix/mutex.h
===================================================================
--- config/posix/mutex.h	(Revision 122)
+++ config/posix/mutex.h	(Arbeitskopie)
@@ -47,6 +47,12 @@
   pthread_mutex_lock (mutex);
 }
 
+static inline void gomp_mutex_init_locked (gomp_mutex_t *mutex)
+{
+  gomp_mutex_init (mutex);
+  gomp_mutex_lock (mutex);
+}
+
 static inline void gomp_mutex_unlock (gomp_mutex_t *mutex)
 {
    pthread_mutex_unlock (mutex);
Index: config/posix/sem.h
===================================================================
--- config/posix/sem.h	(Revision 122)
+++ config/posix/sem.h	(Arbeitskopie)
@@ -49,6 +49,35 @@
 #ifdef HAVE_BROKEN_POSIX_SEMAPHORES
 #include <pthread.h>
 
+#include "mutex.h"
+
+/* This is a binary semaphore aka signal,
+   technically just a wrapper for a mutex */
+typedef gomp_mutex_t gomp_signal_t;
+
+static inline void gomp_signal_init (gomp_signal_t *signal, int fired)
+{
+  if (fired == 0)
+    gomp_mutex_init_locked (signal);
+  else
+    gomp_mutex_init (signal);
+}
+
+static inline void gomp_signal_wait (gomp_signal_t *signal)
+{
+  gomp_mutex_lock (signal);
+}
+
+static inline void gomp_signal_post (gomp_signal_t *signal)
+{
+  gomp_mutex_unlock (signal);
+}
+
+static inline void gomp_signal_destroy (gomp_signal_t *signal)
+{
+  gomp_mutex_destroy (signal);
+}
+
 struct gomp_sem
 {
   pthread_mutex_t	mutex;


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]