View | Details | Return to bug 60035 | Differences between
and this patch

Collapse All | Expand All

(-)team.c (-18 / +88 lines)
Lines 28-33 Link Here
28
#include "libgomp.h"
28
#include "libgomp.h"
29
#include <stdlib.h>
29
#include <stdlib.h>
30
#include <string.h>
30
#include <string.h>
31
#include <stdbool.h>
31
32
32
/* This attribute contains PTHREAD_CREATE_DETACHED.  */
33
/* This attribute contains PTHREAD_CREATE_DETACHED.  */
33
pthread_attr_t gomp_thread_attr;
34
pthread_attr_t gomp_thread_attr;
Lines 43-48 __thread struct gomp_thread gomp_tls_data; Link Here
43
pthread_key_t gomp_tls_key;
44
pthread_key_t gomp_tls_key;
44
#endif
45
#endif
45
46
47
/* This is to enable best-effort cleanup after fork.  */
48
static bool gomp_we_are_forked;
46
49
47
/* This structure is used to communicate across pthread_create.  */
50
/* This structure is used to communicate across pthread_create.  */
48
51
Lines 204-245 static struct gomp_thread_pool *gomp_new_thread_po Link Here
204
  return pool;
207
  return pool;
205
}
208
}
206
209
210
/* Free a thread pool and release its threads. */
211
207
static void
212
static void
208
gomp_free_pool_helper (void *thread_pool)
213
gomp_free_pool_helper (void *thread_pool)
209
{
214
{
210
  struct gomp_thread *thr = gomp_thread ();
211
  struct gomp_thread_pool *pool
215
  struct gomp_thread_pool *pool
212
    = (struct gomp_thread_pool *) thread_pool;
216
    = (struct gomp_thread_pool *) thread_pool;
213
  gomp_barrier_wait_last (&pool->threads_dock);
217
  gomp_barrier_wait_last (&pool->threads_dock);
214
  gomp_sem_destroy (&thr->release);
215
  thr->thread_pool = NULL;
216
  thr->task = NULL;
217
  pthread_exit (NULL);
218
  pthread_exit (NULL);
218
}
219
}
219
220
220
/* Free a thread pool and release its threads. */
221
static void
221
222
gomp_free_thread_pool (bool threads_are_running)
222
void
223
gomp_free_thread (void *arg __attribute__((unused)))
224
{
223
{
225
  struct gomp_thread *thr = gomp_thread ();
224
  struct gomp_thread *thr = gomp_thread ();
226
  struct gomp_thread_pool *pool = thr->thread_pool;
225
  struct gomp_thread_pool *pool = thr->thread_pool;
227
  if (pool)
226
  if (pool)
228
    {
227
    {
228
      int i;
229
      if (pool->threads_used > 0)
229
      if (pool->threads_used > 0)
230
	{
230
	{
231
	  int i;
231
	  if (threads_are_running)
232
	  for (i = 1; i < pool->threads_used; i++)
233
	    {
232
	    {
234
	      struct gomp_thread *nthr = pool->threads[i];
233
	      for (i = 1; i < pool->threads_used; i++)
235
	      nthr->fn = gomp_free_pool_helper;
234
		{
236
	      nthr->data = pool;
235
		  struct gomp_thread *nthr = pool->threads[i];
236
		  nthr->fn = gomp_free_pool_helper;
237
		  nthr->data = pool;
238
		}
239
	      /* This barrier undocks threads docked on pool->threads_dock.  */
240
	      gomp_barrier_wait (&pool->threads_dock);
241
	      /* And this waits till all threads have called
242
		 gomp_barrier_wait_last in gomp_free_pool_helper.  */
243
	      gomp_barrier_wait (&pool->threads_dock);
237
	    }
244
	    }
238
	  /* This barrier undocks threads docked on pool->threads_dock.  */
239
	  gomp_barrier_wait (&pool->threads_dock);
240
	  /* And this waits till all threads have called gomp_barrier_wait_last
241
	     in gomp_free_pool_helper.  */
242
	  gomp_barrier_wait (&pool->threads_dock);
243
	  /* Now it is safe to destroy the barrier and free the pool.  */
245
	  /* Now it is safe to destroy the barrier and free the pool.  */
244
	  gomp_barrier_destroy (&pool->threads_dock);
246
	  gomp_barrier_destroy (&pool->threads_dock);
245
247
Lines 251-256 gomp_free_pool_helper (void *thread_pool) Link Here
251
	  gomp_managed_threads -= pool->threads_used - 1L;
253
	  gomp_managed_threads -= pool->threads_used - 1L;
252
	  gomp_mutex_unlock (&gomp_managed_threads_lock);
254
	  gomp_mutex_unlock (&gomp_managed_threads_lock);
253
#endif
255
#endif
256
	  /* Clean up thread objects */
257
	  for (i = 1; i < pool->threads_used; i++)
258
	    {
259
	      struct gomp_thread *nthr = pool->threads[i];
260
	      gomp_sem_destroy (&nthr->release);
261
	      nthr->thread_pool = NULL;
262
	      nthr->task = NULL;
263
	    }
254
	}
264
	}
255
      free (pool->threads);
265
      free (pool->threads);
256
      if (pool->last_team)
266
      if (pool->last_team)
Lines 266-271 gomp_free_pool_helper (void *thread_pool) Link Here
266
    }
276
    }
267
}
277
}
268
278
279
/* This is called whenever a thread exits which has a non-NULL value for
280
   gomp_thread_destructor. In practice, the only thread for which this occurs
281
   is the one which created the thread pool.
282
*/
283
void
284
gomp_free_thread (void *arg __attribute__((unused)))
285
{
286
  gomp_free_thread_pool (true);
287
}
288
289
/* This is called in the child process after a fork.
290
291
   According to POSIX, if a process which uses threads calls fork(), then
292
   there are very few things that the resulting child process can do safely --
293
   mostly just exec().
294
295
   However, in practice, (almost?) all POSIX implementations seem to allow
296
   arbitrary code to run inside the child, *if* the parent process's threads
297
   are in a well-defined state when the fork occurs. And this circumstance can
298
   easily arise in OMP-using programs, e.g. when a library function like DGEMM
299
   uses OMP internally, and some other unrelated part of the program calls
300
   fork() at some other time, when no OMP sections are running.
301
302
   Therefore, we make a best effort attempt to handle the case:
303
304
     OMP section (in parent) -> quiesce -> fork -> OMP section (in child)
305
306
   "Best-effort" here means that:
307
   - Your system may or may not be able to handle this kind of code at all;
308
     our goal is just to make sure that if it fails it's not gomp's fault.
309
   - All threadprivate variables will be reset in the child. Fortunately this
310
     is entirely compliant with the spec, according to the rule of nasal
311
     demons.
312
   - We must have minimal speed impact, and no correctness impact, on
313
     compliant programs.
314
315
   We use this callback to notice when a fork has a occurred, and if the child
316
   later attempts to enter an OMP section (via gomp_team_start), then we know
317
   that it is non-compliant, and are free to apply our best-effort strategy of
318
   cleaning up the old thread pool structures and spawning a new one. Because
319
   compliant programs never call gomp_team_start after forking, they are
320
   unaffected.
321
*/
322
static void
323
gomp_after_fork_callback (void)
324
{
325
  /* Only "async-signal-safe operations" are allowed here, so let's keep it
326
     simple. No mutex is needed, because we are currently single-threaded.
327
  */
328
  gomp_we_are_forked = 1;
329
}
330
269
/* Launch a team.  */
331
/* Launch a team.  */
270
332
271
void
333
void
Lines 288-298 gomp_team_start (void (*fn) (void *), void *data, Link Here
288
350
289
  thr = gomp_thread ();
351
  thr = gomp_thread ();
290
  nested = thr->ts.team != NULL;
352
  nested = thr->ts.team != NULL;
353
  if (__builtin_expect (gomp_we_are_forked, 0))
354
    {
355
      gomp_free_thread_pool (0);
356
      gomp_we_are_forked = 0;
357
    }
291
  if (__builtin_expect (thr->thread_pool == NULL, 0))
358
  if (__builtin_expect (thr->thread_pool == NULL, 0))
292
    {
359
    {
293
      thr->thread_pool = gomp_new_thread_pool ();
360
      thr->thread_pool = gomp_new_thread_pool ();
294
      thr->thread_pool->threads_busy = nthreads;
361
      thr->thread_pool->threads_busy = nthreads;
362
      /* The pool should be cleaned up whenever this thread exits... */
295
      pthread_setspecific (gomp_thread_destructor, thr);
363
      pthread_setspecific (gomp_thread_destructor, thr);
364
      /* ...and also in any fork()ed children. */
365
      pthread_atfork (NULL, NULL, gomp_after_fork_callback);
296
    }
366
    }
297
  pool = thr->thread_pool;
367
  pool = thr->thread_pool;
298
  task = thr->task;
368
  task = thr->task;
(-)testsuite/libgomp.c/fork-1.c (+77 lines)
Line 0 Link Here
1
/* { dg-do run } */
2
/* { dg-timeout 10 } */
3
4
#include <omp.h>
5
#include <string.h>
6
#include <sys/wait.h>
7
#include <unistd.h>
8
#include <assert.h>
9
10
static int saw[4];
11
12
static void
13
check_parallel (int exit_on_failure)
14
{
15
  memset (saw, 0, sizeof (saw));
16
  #pragma omp parallel num_threads (2)
17
  {
18
    int iam = omp_get_thread_num ();
19
    saw[iam] = 1;
20
  }
21
22
  // Encode failure in status code to report to parent process
23
  if (exit_on_failure)
24
    {
25
      if (saw[0] != 1)
26
        _exit(1);
27
      else if (saw[1] != 1)
28
        _exit(2);
29
      else if (saw[2] != 0)
30
        _exit(3);
31
      else if (saw[3] != 0)
32
        _exit(4);
33
      else
34
        _exit(0);
35
  }
36
  // Use regular assertions
37
  else
38
    {
39
      assert (saw[0] == 1);
40
      assert (saw[1] == 1);
41
      assert (saw[2] == 0);
42
      assert (saw[3] == 0);
43
    }
44
}
45
46
int
47
main ()
48
{
49
  // Initialize the OMP thread pool in the parent process
50
  check_parallel (0);
51
  pid_t fork_pid = fork();
52
  if (fork_pid == -1)
53
    return 1;
54
  else if (fork_pid == 0)
55
    {
56
      // Call OMP again in the child process and encode failures in exit
57
      // code.
58
      check_parallel (1);
59
    }
60
  else
61
    {
62
      // Check that OMP runtime is still functional in parent process after
63
      // the fork.
64
      check_parallel (0);
65
66
      // Wait for the child to finish and check the exit code.
67
      int child_status = 0;
68
      pid_t wait_pid = wait(&child_status);
69
      assert (wait_pid == fork_pid);
70
      assert (WEXITSTATUS (child_status) == 0);
71
72
      // Check that the termination of the child process did not impact
73
      // OMP in parent process.
74
      check_parallel (0);
75
    }
76
  return 0;
77
}

Return to bug 60035