diff mbox series

[gomp5] Add omp_pause_resource{,_all} support

Message ID 20180727130538.GK17988@tucnak
State New
Headers show
Series [gomp5] Add omp_pause_resource{,_all} support | expand

Commit Message

Jakub Jelinek July 27, 2018, 1:05 p.m. UTC
Hi!

This patch adds omp_pause_resource{,_all} APIs.  So far they do something
only for the host device, where they free the thread pool (if any),
including pthread_join waiting for the threads in that team (this means
we don't create threads as detached anymore, but instead detach them before
pthread_exit).

With this people can e.g. omp_pause_resource_all (omp_pause_soft); and
then use fork () in a process that uses pthreads only inside of libgomp and
have a chance to use OpenMP in the fork child.

Tested on x86_64-linux, committed to gomp-5_0-branch.

2018-07-27  Jakub Jelinek  <jakub@redhat.com>

	* libgomp.map (OMP_5.0): Export omp_pause_resource{,_all}{,_}.
	* omp.h.in (omp_pause_resource_t): New typedef.
	(omp_pause_resource, omp_pause_resource_all): New prototypes.
	* omp_lib.f90.in (omp_pause_resource_kind, omp_pause_soft,
	omp_pause_hard): New parameters.
	(omp_pause_resource, omp_pause_resource_all): New interfaces.
	* omp_lib.h.in (omp_pause_resource_kind, omp_pause_soft,
	omp_pause_hard): New parameters.
	(omp_pause_resource, omp_pause_resource_all): New externals.
	* libgomp.h (gomp_pause_host): New prototype.
	* team.c (gomp_thread_attr): Remove comment.
	(gomp_thread_start): Call pthread_detach.
	(gomp_free_pool_helper): Likewise.
	(gomp_team_start): Don't call pthread_attr_setdetachstate.
	(gomp_pause_pool_helper, gomp_pause_host): New functions.
	* target.c (omp_pause_resource, omp_pause_resource_all): New functions.
	* env.c (initialize_env): Don't call pthread_attr_setdetachstate.
	* fortran.c (omp_pause_resource, omp_pause_resource_all): Add
	ialias_redirect.
	(omp_pause_resource_, omp_pause_resource_all_): New functions.
	* config/nvptx/team.c (gomp_pause_host): New function.
	* config/nvptx/target.c (omp_pause_resource, omp_pause_resource_all):
	New functions.
	* testsuite/libgomp.c-c++-common/pause-1.c: New test.
	* testsuite/libgomp.c-c++-common/pause-2.c: New test.


	Jakub
diff mbox series

Patch

--- libgomp.map.jj	2018-07-25 15:50:38.595714295 +0200
+++ libgomp.map	2018-07-27 12:30:13.836168988 +0200
@@ -174,6 +174,10 @@  OMP_5.0 {
 	omp_get_affinity_format_;
 	omp_set_affinity_format;
 	omp_set_affinity_format_;
+	omp_pause_resource;
+	omp_pause_resource_;
+	omp_pause_resource_all;
+	omp_pause_resource_all_;
 } OMP_4.5;
 
 GOMP_1.0 {
--- omp.h.in.jj	2018-06-26 16:31:52.071327689 +0200
+++ omp.h.in	2018-07-26 16:50:15.568291658 +0200
@@ -83,6 +83,12 @@  typedef struct __attribute__((__aligned_
   char __omp_depend_t__[2 * sizeof (void *)];
 } omp_depend_t;
 
+typedef enum omp_pause_resource_t
+{
+  omp_pause_soft = 1,
+  omp_pause_hard = 2
+} omp_pause_resource_t;
+
 #ifdef __cplusplus
 extern "C" {
 # define __GOMP_NOTHROW throw ()
@@ -178,6 +184,9 @@  extern void omp_display_affinity (const
 extern __SIZE_TYPE__ omp_capture_affinity (char *, __SIZE_TYPE__, const char *)
   __GOMP_NOTHROW;
 
+extern int omp_pause_resource (omp_pause_resource_t, int) __GOMP_NOTHROW;
+extern int omp_pause_resource_all (omp_pause_resource_t) __GOMP_NOTHROW;
+
 #ifdef __cplusplus
 }
 #endif
--- omp_lib.f90.in.jj	2018-05-23 13:14:15.873893636 +0200
+++ omp_lib.f90.in	2018-07-27 13:51:33.178026630 +0200
@@ -30,6 +30,7 @@ 
         integer, parameter :: omp_sched_kind = 4
         integer, parameter :: omp_proc_bind_kind = 4
         integer, parameter :: omp_lock_hint_kind = 4
+        integer, parameter :: omp_pause_resource_kind = 4
         integer (omp_sched_kind), parameter :: omp_sched_static = 1
         integer (omp_sched_kind), parameter :: omp_sched_dynamic = 2
         integer (omp_sched_kind), parameter :: omp_sched_guided = 3
@@ -54,6 +55,10 @@ 
                  parameter :: omp_lock_hint_nonspeculative = 4
         integer (omp_lock_hint_kind), &
                  parameter :: omp_lock_hint_speculative = 8
+        integer (kind=omp_pause_resource_kind), &
+                 parameter :: omp_pause_soft = 1
+        integer (kind=omp_pause_resource_kind), &
+                 parameter :: omp_pause_hard = 2
       end module
 
       module omp_lib
@@ -460,4 +465,23 @@ 
           end function omp_capture_affinity
         end interface
 
+        interface
+          function omp_pause_resource (kind, device_num)
+            use omp_lib_kinds
+            integer (4) :: omp_pause_resource
+            integer (kind=omp_pause_resource_kind), &
+              intent(in) :: kind
+            integer (4) :: device_num
+          end function
+        end interface
+
+        interface
+          function omp_pause_resource_all (kind)
+            use omp_lib_kinds
+            integer (4) :: omp_pause_resource_all
+            integer (kind=omp_pause_resource_kind), &
+              intent(in) :: kind
+          end function
+        end interface
+
       end module omp_lib
--- omp_lib.h.in.jj	2018-05-23 13:21:07.605241351 +0200
+++ omp_lib.h.in	2018-07-27 12:05:46.150289751 +0200
@@ -59,6 +59,12 @@ 
       parameter (omp_lock_hint_nonspeculative = 4)
       parameter (omp_lock_hint_speculative = 8)
       parameter (openmp_version = 201511)
+      integer omp_pause_resource_kind
+      parameter (omp_pause_resource_kind = 4)
+      integer (omp_pause_resource_kind) omp_pause_soft
+      integer (omp_pause_resource_kind) omp_pause_hard
+      parameter (omp_pause_soft = 1)
+      parameter (omp_pause_hard = 2)
 
       external omp_init_lock, omp_init_nest_lock
       external omp_init_lock_with_hint
@@ -131,3 +137,7 @@ 
       external omp_display_affinity, omp_capture_affinity
       integer(4) omp_get_affinity_format
       integer(4) omp_capture_affinity
+
+      external omp_pause_resource, omp_pause_resource_all
+      integer(4) omp_pause_resource
+      integer(4) omp_pause_resource_all
--- libgomp.h.jj	2018-07-10 11:31:24.637097300 +0200
+++ libgomp.h	2018-07-27 12:57:58.964240636 +0200
@@ -821,6 +821,7 @@  extern void gomp_team_start (void (*) (v
 			     unsigned, struct gomp_team *);
 extern void gomp_team_end (void);
 extern void gomp_free_thread (void *);
+extern int gomp_pause_host (void);
 
 /* target.c */
 
--- team.c.jj	2018-05-23 16:45:09.478108168 +0200
+++ team.c	2018-07-27 12:57:16.868162523 +0200
@@ -32,7 +32,6 @@ 
 #include <string.h>
 
 #ifdef LIBGOMP_USE_PTHREADS
-/* This attribute contains PTHREAD_CREATE_DETACHED.  */
 pthread_attr_t gomp_thread_attr;
 
 /* This key is for the thread destructor.  */
@@ -135,6 +134,7 @@  gomp_thread_start (void *xdata)
     }
 
   gomp_sem_destroy (&thr->release);
+  pthread_detach (pthread_self ());
   thr->thread_pool = NULL;
   thr->task = NULL;
   return NULL;
@@ -235,6 +235,7 @@  gomp_free_pool_helper (void *thread_pool
   thr->thread_pool = NULL;
   thr->task = NULL;
 #ifdef LIBGOMP_USE_PTHREADS
+  pthread_detach (pthread_self ());
   pthread_exit (NULL);
 #elif defined(__nvptx__)
   asm ("exit;");
@@ -726,7 +727,6 @@  gomp_team_start (void (*fn) (void *), vo
     {
       size_t stacksize;
       pthread_attr_init (&thread_attr);
-      pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED);
       if (! pthread_attr_getstacksize (&gomp_thread_attr, &stacksize))
 	pthread_attr_setstacksize (&thread_attr, stacksize);
       attr = &thread_attr;
@@ -1010,6 +1010,76 @@  team_destructor (void)
      crashes.  */
   pthread_key_delete (gomp_thread_destructor);
 }
+
+/* Similar to gomp_free_pool_helper, but don't detach itself,
+   gomp_pause_host will pthread_join those threads.  */
+
+static void
+gomp_pause_pool_helper (void *thread_pool)
+{
+  struct gomp_thread *thr = gomp_thread ();
+  struct gomp_thread_pool *pool
+    = (struct gomp_thread_pool *) thread_pool;
+  gomp_simple_barrier_wait_last (&pool->threads_dock);
+  gomp_sem_destroy (&thr->release);
+  thr->thread_pool = NULL;
+  thr->task = NULL;
+  pthread_exit (NULL);
+}
+
+/* Free a thread pool and release its threads.  Return non-zero on
+   failure.  */
+
+int
+gomp_pause_host (void)
+{
+  struct gomp_thread *thr = gomp_thread ();
+  struct gomp_thread_pool *pool = thr->thread_pool;
+  if (thr->ts.level)
+    return -1;
+  if (pool)
+    {
+      if (pool->threads_used > 0)
+	{
+	  int i;
+	  pthread_t *thrs
+	    = gomp_alloca (sizeof (pthread_t) * pool->threads_used);
+	  for (i = 1; i < pool->threads_used; i++)
+	    {
+	      struct gomp_thread *nthr = pool->threads[i];
+	      nthr->fn = gomp_pause_pool_helper;
+	      nthr->data = pool;
+	      thrs[i] = gomp_thread_to_pthread_t (nthr);
+	    }
+	  /* This barrier undocks threads docked on pool->threads_dock.  */
+	  gomp_simple_barrier_wait (&pool->threads_dock);
+	  /* And this waits till all threads have called gomp_barrier_wait_last
+	     in gomp_pause_pool_helper.  */
+	  gomp_simple_barrier_wait (&pool->threads_dock);
+	  /* Now it is safe to destroy the barrier and free the pool.  */
+	  gomp_simple_barrier_destroy (&pool->threads_dock);
+
+#ifdef HAVE_SYNC_BUILTINS
+	  __sync_fetch_and_add (&gomp_managed_threads,
+				1L - pool->threads_used);
+#else
+	  gomp_mutex_lock (&gomp_managed_threads_lock);
+	  gomp_managed_threads -= pool->threads_used - 1L;
+	  gomp_mutex_unlock (&gomp_managed_threads_lock);
+#endif
+	  for (i = 1; i < pool->threads_used; i++)
+	    pthread_join (thrs[i], NULL);
+	}
+      if (pool->last_team)
+	free_team (pool->last_team);
+#ifndef __nvptx__
+      free (pool->threads);
+      free (pool);
+#endif
+      thr->thread_pool = NULL;
+    }
+  return 0;
+}
 #endif
 
 struct gomp_task_icv *
--- target.c.jj	2018-07-10 11:31:24.126096774 +0200
+++ target.c	2018-07-27 13:37:38.051930757 +0200
@@ -2503,6 +2503,31 @@  omp_target_disassociate_ptr (const void
   return ret;
 }
 
+int
+omp_pause_resource (omp_pause_resource_t kind, int device_num)
+{
+  (void) kind;
+  if (device_num == GOMP_DEVICE_HOST_FALLBACK)
+    return gomp_pause_host ();
+  if (device_num < 0 || device_num >= gomp_get_num_devices ())
+    return -1;
+  /* Do nothing for target devices for now.  */
+  return 0;
+}
+
+int
+omp_pause_resource_all (omp_pause_resource_t kind)
+{
+  (void) kind;
+  if (gomp_pause_host ())
+    return -1;
+  /* Do nothing for target devices for now.  */
+  return 0;
+}
+
+ialias (omp_pause_resource)
+ialias (omp_pause_resource_all)
+
 #ifdef PLUGIN_SUPPORT
 
 /* This function tries to load a plugin for DEVICE.  Name of plugin is passed
--- env.c.jj	2018-06-04 18:15:32.529308844 +0200
+++ env.c	2018-07-26 16:09:18.510102373 +0200
@@ -1348,7 +1348,6 @@  initialize_env (void)
 
   /* Not strictly environment related, but ordering constructors is tricky.  */
   pthread_attr_init (&gomp_thread_attr);
-  pthread_attr_setdetachstate (&gomp_thread_attr, PTHREAD_CREATE_DETACHED);
 
   if (parse_stacksize ("OMP_STACKSIZE", &stacksize)
       || parse_stacksize ("GOMP_STACKSIZE", &stacksize)
--- fortran.c.jj	2018-05-23 16:41:05.562875275 +0200
+++ fortran.c	2018-07-27 13:34:07.108684440 +0200
@@ -84,6 +84,8 @@  ialias_redirect (omp_get_team_num)
 ialias_redirect (omp_is_initial_device)
 ialias_redirect (omp_get_initial_device)
 ialias_redirect (omp_get_max_task_priority)
+ialias_redirect (omp_pause_resource)
+ialias_redirect (omp_pause_resource_all)
 #endif
 
 #ifndef LIBGOMP_GNU_SYMBOL_VERSIONING
@@ -659,3 +661,15 @@  omp_capture_affinity_ (char *buffer, con
     memset (buffer + ret, ' ', buffer_len - ret);
   return ret;
 }
+
+int32_t
+omp_pause_resource_ (const int32_t *kind, const int32_t *device_num)
+{
+  return omp_pause_resource (*kind, *device_num);
+}
+
+int32_t
+omp_pause_resource_all_ (const int32_t *kind)
+{
+  return omp_pause_resource_all (*kind);
+}
--- config/nvptx/team.c.jj	2018-04-30 13:20:56.011862116 +0200
+++ config/nvptx/team.c	2018-07-27 12:58:39.425317985 +0200
@@ -174,5 +174,11 @@  gomp_team_start (void (*fn) (void *), vo
   gomp_simple_barrier_wait (&pool->threads_dock);
 }
 
+int
+gomp_pause_host (void)
+{
+  return -1;
+}
+
 #include "../../team.c"
 #endif
--- config/nvptx/target.c.jj	2018-04-30 13:20:55.854862058 +0200
+++ config/nvptx/target.c	2018-07-27 13:40:27.724227079 +0200
@@ -47,3 +47,21 @@  GOMP_teams (unsigned int num_teams, unsi
     }
   gomp_num_teams_var = num_teams - 1;
 }
+
+int
+omp_pause_resource (omp_pause_resource_t kind, int device_num)
+{
+  (void) kind;
+  (void) device_num;
+  return -1;
+}
+
+int
+omp_pause_resource_all (omp_pause_resource_t kind)
+{
+  (void) kind;
+  return -1;
+}
+
+ialias (omp_pause_resource)
+ialias (omp_pause_resource_all)
--- testsuite/libgomp.c-c++-common/pause-1.c.jj	2018-07-27 14:03:27.108088443 +0200
+++ testsuite/libgomp.c-c++-common/pause-1.c	2018-07-27 14:09:40.379693702 +0200
@@ -0,0 +1,23 @@ 
+#include <omp.h>
+#include <stdlib.h>
+
+int a[64];
+
+int
+main ()
+{
+  int i;
+  #pragma omp parallel for
+  for (i = 0; i < 64; i++)
+    a[i] = i;
+  omp_pause_resource (omp_pause_soft, omp_get_initial_device ());
+  #pragma omp parallel for
+  for (i = 0; i < 64; i++)
+    a[i] += i;
+  omp_pause_resource_all (omp_pause_hard);
+  #pragma omp parallel for
+  for (i = 0; i < 64; i++)
+    if (a[i] != 2 * i)
+      abort ();
+  return 0;
+}
--- testsuite/libgomp.c-c++-common/pause-2.c.jj	2018-07-27 14:03:43.118112347 +0200
+++ testsuite/libgomp.c-c++-common/pause-2.c	2018-07-27 14:08:57.515618168 +0200
@@ -0,0 +1,41 @@ 
+/* { dg-do run } */
+/* { dg-require-effective-target tls_runtime } */
+
+#include <omp.h>
+#include <stdlib.h>
+
+int t = 128;
+#pragma omp threadprivate (t)
+
+int
+main ()
+{
+  #pragma omp parallel
+  t = omp_get_thread_num () + 256;
+  #pragma omp parallel
+  if (t != omp_get_thread_num () + 256)
+    abort ();
+  omp_pause_resource (omp_pause_soft, omp_get_initial_device ());
+  /* This goes beyond what is required by the standard, we actually
+     check if the threads other than the initial one have been destroyed.  */
+  #pragma omp parallel
+  {
+    if (omp_get_thread_num () != 0 && t != 128)
+      abort ();
+    t = omp_get_thread_num () + 384;
+  }
+  #pragma omp parallel
+  if (t != omp_get_thread_num () + 384)
+    abort ();
+  omp_pause_resource_all (omp_pause_hard);
+  #pragma omp parallel
+  {
+    if (omp_get_thread_num () != 0 && t != 128)
+      abort ();
+    t = omp_get_thread_num () + 512;
+  }
+  #pragma omp parallel
+  if (t != omp_get_thread_num () + 512)
+    abort ();
+  return 0;
+}