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 <ja...@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. --- 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; +} Jakub