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

Collapse All | Expand All

(-)team.c (-18 / +87 lines)
Lines 43-48 Link Here
43
pthread_key_t gomp_tls_key;
43
pthread_key_t gomp_tls_key;
44
#endif
44
#endif
45
45
46
/* This is to enable best-effort cleanup after fork.  */
47
static int gomp_we_are_forked = 0;
46
48
47
/* This structure is used to communicate across pthread_create.  */
49
/* This structure is used to communicate across pthread_create.  */
48
50
Lines 204-245 Link Here
204
  return pool;
206
  return pool;
205
}
207
}
206
208
209
/* Free a thread pool and release its threads. */
210
207
static void
211
static void
208
gomp_free_pool_helper (void *thread_pool)
212
gomp_free_pool_helper (void *thread_pool)
209
{
213
{
210
  struct gomp_thread *thr = gomp_thread ();
211
  struct gomp_thread_pool *pool
214
  struct gomp_thread_pool *pool
212
    = (struct gomp_thread_pool *) thread_pool;
215
    = (struct gomp_thread_pool *) thread_pool;
213
  gomp_barrier_wait_last (&pool->threads_dock);
216
  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);
217
  pthread_exit (NULL);
218
}
218
}
219
219
220
/* Free a thread pool and release its threads. */
220
static void
221
221
gomp_free_thread_pool (int threads_running)
222
void
223
gomp_free_thread (void *arg __attribute__((unused)))
224
{
222
{
225
  struct gomp_thread *thr = gomp_thread ();
223
  struct gomp_thread *thr = gomp_thread ();
226
  struct gomp_thread_pool *pool = thr->thread_pool;
224
  struct gomp_thread_pool *pool = thr->thread_pool;
227
  if (pool)
225
  if (pool)
228
    {
226
    {
227
      int i;
229
      if (pool->threads_used > 0)
228
      if (pool->threads_used > 0)
230
	{
229
	{
231
	  int i;
230
	  if (threads_running)
232
	  for (i = 1; i < pool->threads_used; i++)
233
	    {
231
	    {
234
	      struct gomp_thread *nthr = pool->threads[i];
232
	      for (i = 1; i < pool->threads_used; i++)
235
	      nthr->fn = gomp_free_pool_helper;
233
		{
236
	      nthr->data = pool;
234
		  struct gomp_thread *nthr = pool->threads[i];
235
		  nthr->fn = gomp_free_pool_helper;
236
		  nthr->data = pool;
237
		}
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
241
		 gomp_barrier_wait_last in gomp_free_pool_helper.  */
242
	      gomp_barrier_wait (&pool->threads_dock);
237
	    }
243
	    }
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.  */
244
	  /* Now it is safe to destroy the barrier and free the pool.  */
244
	  gomp_barrier_destroy (&pool->threads_dock);
245
	  gomp_barrier_destroy (&pool->threads_dock);
245
246
Lines 251-256 Link Here
251
	  gomp_managed_threads -= pool->threads_used - 1L;
252
	  gomp_managed_threads -= pool->threads_used - 1L;
252
	  gomp_mutex_unlock (&gomp_managed_threads_lock);
253
	  gomp_mutex_unlock (&gomp_managed_threads_lock);
253
#endif
254
#endif
255
	  /* Clean up thread objects */
256
	  for (i = 1; i < pool->threads_used; i++)
257
	    {
258
	      struct gomp_thread *nthr = pool->threads[i];
259
	      gomp_sem_destroy (&nthr->release);
260
	      nthr->thread_pool = NULL;
261
	      nthr->task = NULL;
262
	    }
254
	}
263
	}
255
      free (pool->threads);
264
      free (pool->threads);
256
      if (pool->last_team)
265
      if (pool->last_team)
Lines 266-271 Link Here
266
    }
275
    }
267
}
276
}
268
277
278
/* This is called whenever a thread exits which has a non-NULL value for
279
   gomp_thread_destructor. In practice, the only thread for which this occurs
280
   is the one which created the thread pool.
281
*/
282
void
283
gomp_free_thread (void *arg __attribute__((unused)))
284
{
285
  gomp_free_thread_pool (1);
286
}
287
288
/* This is called in the child process after a fork.
289
290
   According to POSIX, if a process which uses threads calls fork(), then
291
   there are very few things that the resulting child process can do safely --
292
   mostly just exec().
293
294
   However, in practice, (almost?) all POSIX implementations seem to allow
295
   arbitrary code to run inside the child, *if* the parent process's threads
296
   are in a well-defined state when the fork occurs. And this circumstance can
297
   easily arise in OMP-using programs, e.g. when a library function like DGEMM
298
   uses OMP internally, and some other unrelated part of the program calls
299
   fork() at some other time, when no OMP sections are running.
300
301
   Therefore, we make a best effort attempt to handle the case:
302
303
     OMP section (in parent) -> quiesce -> fork -> OMP section (in child)
304
305
   "Best-effort" here means that:
306
   - Your system may or may not be able to handle this kind of code at all;
307
     our goal is just to make sure that if it fails it's not gomp's fault.
308
   - All threadprivate variables will be reset in the child. Fortunately this
309
     is entirely compliant with the spec, according to the rule of nasal
310
     demons.
311
   - We must have minimal speed impact, and no correctness impact, on
312
     compliant programs.
313
314
   We use this callback to notice when a fork has a occurred, and if the child
315
   later attempts to enter an OMP section (via gomp_team_start), then we know
316
   that it is non-compliant, and are free to apply our best-effort strategy of
317
   cleaning up the old thread pool structures and spawning a new one. Because
318
   compliant programs never call gomp_team_start after forking, they are
319
   unaffected.
320
*/
321
static void
322
gomp_after_fork_callback ()
323
{
324
  /* Only "async-signal-safe operations" are allowed here, so let's keep it
325
     simple. No mutex is needed, because we are currently single-threaded.
326
  */
327
  gomp_we_are_forked = 1;
328
}
329
269
/* Launch a team.  */
330
/* Launch a team.  */
270
331
271
void
332
void
Lines 288-298 Link Here
288
349
289
  thr = gomp_thread ();
350
  thr = gomp_thread ();
290
  nested = thr->ts.team != NULL;
351
  nested = thr->ts.team != NULL;
352
  if (__builtin_expect (gomp_we_are_forked, 0))
353
    {
354
      gomp_free_thread_pool (0);
355
      gomp_we_are_forked = 0;
356
    }
291
  if (__builtin_expect (thr->thread_pool == NULL, 0))
357
  if (__builtin_expect (thr->thread_pool == NULL, 0))
292
    {
358
    {
293
      thr->thread_pool = gomp_new_thread_pool ();
359
      thr->thread_pool = gomp_new_thread_pool ();
294
      thr->thread_pool->threads_busy = nthreads;
360
      thr->thread_pool->threads_busy = nthreads;
361
      /* The pool should be cleaned up whenever this thread exits... */
295
      pthread_setspecific (gomp_thread_destructor, thr);
362
      pthread_setspecific (gomp_thread_destructor, thr);
363
      /* ...and also in any fork()ed children. */
364
      pthread_atfork (NULL, NULL, &gomp_after_fork_callback);
296
    }
365
    }
297
  pool = thr->thread_pool;
366
  pool = thr->thread_pool;
298
  task = thr->task;
367
  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