Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 685024
Collapse All | Expand All

(-)a/ChangeLog (-16 lines)
Lines 6777-6798 Link Here
6777
6777
6778
	* sysdeps/sparc/fpu/libm-test-ulps: Update.
6778
	* sysdeps/sparc/fpu/libm-test-ulps: Update.
6779
6779
6780
	* nptl/Makefile (routines): Remove unregister-atfork.
6781
	* nptl/register-atfork.c (fork_handler_pool): Remove variable.
6782
	(fork_handler_alloc): Remove function.
6783
	(fork_handlers, fork_handler_init): New variables.
6784
	(__fork_lock): Rename to atfork_lock.
6785
	(__register_atfork, __unregister_atfork, libc_freeres_fn): Rewrite
6786
	to use a dynamic array to add/remove atfork handlers.
6787
	* sysdeps/nptl/fork.c (__libc_fork): Likewise.
6788
	* sysdeps/nptl/fork.h (__fork_lock, __fork_handlers, __linkin_atfork):
6789
	Remove declaration.
6790
	(fork_handler): Remove next, refcntr, and need_signal member.
6791
	(__run_fork_handler_type): New enum.
6792
	(__run_fork_handlers): New prototype.
6793
	* nptl/register-atfork.c: Remove file.
6794
	* sysdeps/nptl/libc-lockP.h (__libc_atfork): Remove declaration.
6795
6796
	* sysdeps/nptl/nptl-signals.h: Move to ...
6780
	* sysdeps/nptl/nptl-signals.h: Move to ...
6797
	* sysdeps/generic/internal-signals.h: ... here.  Adjust internal
6781
	* sysdeps/generic/internal-signals.h: ... here.  Adjust internal
6798
	comments.
6782
	comments.
(-)a/nptl/Makefile (-2 / +2 lines)
Lines 29-36 extra-libs-others := $(extra-libs) Link Here
29
29
30
routines = alloca_cutoff forward libc-lowlevellock libc-cancellation \
30
routines = alloca_cutoff forward libc-lowlevellock libc-cancellation \
31
	   libc-cleanup libc_pthread_init libc_multiple_threads \
31
	   libc-cleanup libc_pthread_init libc_multiple_threads \
32
	   register-atfork pthread_atfork pthread_self thrd_current \
32
	   register-atfork unregister-atfork pthread_atfork pthread_self \
33
	   thrd_equal thrd_sleep thrd_yield
33
	   thrd_current thrd_equal thrd_sleep thrd_yield
34
shared-only-routines = forward
34
shared-only-routines = forward
35
static-only-routines = pthread_atfork
35
static-only-routines = pthread_atfork
36
36
(-)a/nptl/register-atfork.c (-89 / +85 lines)
Lines 22-148 Link Here
22
#include <fork.h>
22
#include <fork.h>
23
#include <atomic.h>
23
#include <atomic.h>
24
24
25
#define DYNARRAY_ELEMENT           struct fork_handler
26
#define DYNARRAY_STRUCT            fork_handler_list
27
#define DYNARRAY_PREFIX            fork_handler_list_
28
#define DYNARRAY_INITIAL_SIZE      48
29
#include <malloc/dynarray-skeleton.c>
30
25
31
static struct fork_handler_list fork_handlers;
26
struct fork_handler *__fork_handlers;
32
static bool fork_handler_init = false;
27
28
/* Lock to protect allocation and deallocation of fork handlers.  */
29
int __fork_lock = LLL_LOCK_INITIALIZER;
30
31
32
/* Number of pre-allocated handler entries.  */
33
#define NHANDLER 48
34
35
/* Memory pool for fork handler structures.  */
36
static struct fork_handler_pool
37
{
38
  struct fork_handler_pool *next;
39
  struct fork_handler mem[NHANDLER];
40
} fork_handler_pool;
41
42
43
static struct fork_handler *
44
fork_handler_alloc (void)
45
{
46
  struct fork_handler_pool *runp = &fork_handler_pool;
47
  struct fork_handler *result = NULL;
48
  unsigned int i;
49
50
  do
51
    {
52
      /* Search for an empty entry.  */
53
      for (i = 0; i < NHANDLER; ++i)
54
	if (runp->mem[i].refcntr == 0)
55
	  goto found;
56
    }
57
  while ((runp = runp->next) != NULL);
58
59
  /* We have to allocate a new entry.  */
60
  runp = (struct fork_handler_pool *) calloc (1, sizeof (*runp));
61
  if (runp != NULL)
62
    {
63
      /* Enqueue the new memory pool into the list.  */
64
      runp->next = fork_handler_pool.next;
65
      fork_handler_pool.next = runp;
66
67
      /* We use the last entry on the page.  This means when we start
68
	 searching from the front the next time we will find the first
69
	 entry unused.  */
70
      i = NHANDLER - 1;
71
72
    found:
73
      result = &runp->mem[i];
74
      result->refcntr = 1;
75
      result->need_signal = 0;
76
    }
77
78
  return result;
79
}
33
80
34
static int atfork_lock = LLL_LOCK_INITIALIZER;
35
81
36
int
82
int
37
__register_atfork (void (*prepare) (void), void (*parent) (void),
83
__register_atfork (void (*prepare) (void), void (*parent) (void),
38
		   void (*child) (void), void *dso_handle)
84
		   void (*child) (void), void *dso_handle)
39
{
85
{
40
  lll_lock (atfork_lock, LLL_PRIVATE);
86
  /* Get the lock to not conflict with other allocations.  */
87
  lll_lock (__fork_lock, LLL_PRIVATE);
41
88
42
  if (!fork_handler_init)
89
  struct fork_handler *newp = fork_handler_alloc ();
43
    {
44
      fork_handler_list_init (&fork_handlers);
45
      fork_handler_init = true;
46
    }
47
90
48
  struct fork_handler *newp = fork_handler_list_emplace (&fork_handlers);
49
  if (newp != NULL)
91
  if (newp != NULL)
50
    {
92
    {
93
      /* Initialize the new record.  */
51
      newp->prepare_handler = prepare;
94
      newp->prepare_handler = prepare;
52
      newp->parent_handler = parent;
95
      newp->parent_handler = parent;
53
      newp->child_handler = child;
96
      newp->child_handler = child;
54
      newp->dso_handle = dso_handle;
97
      newp->dso_handle = dso_handle;
98
99
      __linkin_atfork (newp);
55
    }
100
    }
56
101
57
  /* Release the lock.  */
102
  /* Release the lock.  */
58
  lll_unlock (atfork_lock, LLL_PRIVATE);
103
  lll_unlock (__fork_lock, LLL_PRIVATE);
59
104
60
  return newp == NULL ? ENOMEM : 0;
105
  return newp == NULL ? ENOMEM : 0;
61
}
106
}
62
libc_hidden_def (__register_atfork)
107
libc_hidden_def (__register_atfork)
63
108
64
static struct fork_handler *
65
fork_handler_list_find (struct fork_handler_list *fork_handlers,
66
			void *dso_handle)
67
{
68
  for (size_t i = 0; i < fork_handler_list_size (fork_handlers); i++)
69
    {
70
      struct fork_handler *elem = fork_handler_list_at (fork_handlers, i);
71
      if (elem->dso_handle == dso_handle)
72
	return elem;
73
    }
74
  return NULL;
75
}
76
109
77
void
110
void
78
__unregister_atfork (void *dso_handle)
111
attribute_hidden
112
__linkin_atfork (struct fork_handler *newp)
79
{
113
{
80
  lll_lock (atfork_lock, LLL_PRIVATE);
114
  do
81
115
    newp->next = __fork_handlers;
82
  struct fork_handler *first = fork_handler_list_find (&fork_handlers,
116
  while (catomic_compare_and_exchange_bool_acq (&__fork_handlers,
83
						       dso_handle);
117
						newp, newp->next) != 0);
84
  /* Removing is done by shifting the elements in the way the elements
85
     that are not to be removed appear in the beginning in dynarray.
86
     This avoid the quadradic run-time if a naive strategy to remove and
87
     shift one element at time.  */
88
  if (first != NULL)
89
    {
90
      struct fork_handler *new_end = first;
91
      first++;
92
      for (; first != fork_handler_list_end (&fork_handlers); ++first)
93
	{
94
	  if (first->dso_handle != dso_handle)
95
	    {
96
	      *new_end = *first;
97
	      ++new_end;
98
	    }
99
	}
100
101
      ptrdiff_t removed = first - new_end;
102
      for (size_t i = 0; i < removed; i++)
103
	fork_handler_list_remove_last (&fork_handlers);
104
    }
105
106
  lll_unlock (atfork_lock, LLL_PRIVATE);
107
}
118
}
108
119
109
void
120
110
__run_fork_handlers (enum __run_fork_handler_type who)
121
libc_freeres_fn (free_mem)
111
{
122
{
112
  struct fork_handler *runp;
123
  /* Get the lock to not conflict with running forks.  */
124
  lll_lock (__fork_lock, LLL_PRIVATE);
113
125
114
  if (who == atfork_run_prepare)
126
  /* No more fork handlers.  */
115
    {
127
  __fork_handlers = NULL;
116
      lll_lock (atfork_lock, LLL_PRIVATE);
117
      size_t sl = fork_handler_list_size (&fork_handlers);
118
      for (size_t i = sl; i > 0; i--)
119
	{
120
	  runp = fork_handler_list_at (&fork_handlers, i - 1);
121
	  if (runp->prepare_handler != NULL)
122
	    runp->prepare_handler ();
123
	}
124
    }
125
  else
126
    {
127
      size_t sl = fork_handler_list_size (&fork_handlers);
128
      for (size_t i = 0; i < sl; i++)
129
	{
130
	  runp = fork_handler_list_at (&fork_handlers, i);
131
	  if (who == atfork_run_child && runp->child_handler)
132
	    runp->child_handler ();
133
	  else if (who == atfork_run_parent && runp->parent_handler)
134
	    runp->parent_handler ();
135
	}
136
      lll_unlock (atfork_lock, LLL_PRIVATE);
137
    }
138
}
139
128
129
  /* Free eventually allocated memory blocks for the object pool.  */
130
  struct fork_handler_pool *runp = fork_handler_pool.next;
140
131
141
libc_freeres_fn (free_mem)
132
  memset (&fork_handler_pool, '\0', sizeof (fork_handler_pool));
142
{
143
  lll_lock (atfork_lock, LLL_PRIVATE);
144
133
145
  fork_handler_list_free (&fork_handlers);
134
  /* Release the lock.  */
135
  lll_unlock (__fork_lock, LLL_PRIVATE);
146
136
147
  lll_unlock (atfork_lock, LLL_PRIVATE);
137
  /* We can free the memory after releasing the lock.  */
138
  while (runp != NULL)
139
    {
140
      struct fork_handler_pool *oldp = runp;
141
      runp = runp->next;
142
      free (oldp);
143
    }
148
}
144
}
(-)a/nptl/unregister-atfork.c (+121 lines)
Line 0 Link Here
1
/* Copyright (C) 2002-2018 Free Software Foundation, Inc.
2
   This file is part of the GNU C Library.
3
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
4
5
   The GNU C Library is free software; you can redistribute it and/or
6
   modify it under the terms of the GNU Lesser General Public
7
   License as published by the Free Software Foundation; either
8
   version 2.1 of the License, or (at your option) any later version.
9
10
   The GNU C Library is distributed in the hope that it will be useful,
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
   Lesser General Public License for more details.
14
15
   You should have received a copy of the GNU Lesser General Public
16
   License along with the GNU C Library; if not, see
17
   <http://www.gnu.org/licenses/>.  */
18
19
#include <errno.h>
20
#include <stdlib.h>
21
#include <fork.h>
22
#include <atomic.h>
23
#include <futex-internal.h>
24
25
26
void
27
__unregister_atfork (void *dso_handle)
28
{
29
  /* Check whether there is any entry in the list which we have to
30
     remove.  It is likely that this is not the case so don't bother
31
     getting the lock.
32
33
     We do not worry about other threads adding entries for this DSO
34
     right this moment.  If this happens this is a race and we can do
35
     whatever we please.  The program will crash anyway seen.  */
36
  struct fork_handler *runp = __fork_handlers;
37
  struct fork_handler *lastp = NULL;
38
39
  while (runp != NULL)
40
    if (runp->dso_handle == dso_handle)
41
      break;
42
    else
43
      {
44
	lastp = runp;
45
	runp = runp->next;
46
      }
47
48
  if (runp == NULL)
49
    /* Nothing to do.  */
50
    return;
51
52
  /* Get the lock to not conflict with additions or deletions.  Note
53
     that there couldn't have been another thread deleting something.
54
     The __unregister_atfork function is only called from the
55
     dlclose() code which itself serializes the operations.  */
56
  lll_lock (__fork_lock, LLL_PRIVATE);
57
58
  /* We have to create a new list with all the entries we don't remove.  */
59
  struct deleted_handler
60
  {
61
    struct fork_handler *handler;
62
    struct deleted_handler *next;
63
  } *deleted = NULL;
64
65
  /* Remove the entries for the DSO which is unloaded from the list.
66
     It's a single linked list so readers are.  */
67
  do
68
    {
69
    again:
70
      if (runp->dso_handle == dso_handle)
71
	{
72
	  if (lastp == NULL)
73
	    {
74
	      /* We have to use an atomic operation here because
75
		 __linkin_atfork also uses one.  */
76
	      if (catomic_compare_and_exchange_bool_acq (&__fork_handlers,
77
							 runp->next, runp)
78
		  != 0)
79
		{
80
		  runp = __fork_handlers;
81
		  goto again;
82
		}
83
	    }
84
	  else
85
	    lastp->next = runp->next;
86
87
	  /* We cannot overwrite the ->next element now.  Put the deleted
88
	     entries in a separate list.  */
89
	  struct deleted_handler *newp = alloca (sizeof (*newp));
90
	  newp->handler = runp;
91
	  newp->next = deleted;
92
	  deleted = newp;
93
	}
94
      else
95
	lastp = runp;
96
97
      runp = runp->next;
98
    }
99
  while (runp != NULL);
100
101
  /* Release the lock.  */
102
  lll_unlock (__fork_lock, LLL_PRIVATE);
103
104
  /* Walk the list of all entries which have to be deleted.  */
105
  while (deleted != NULL)
106
    {
107
      /* We need to be informed by possible current users.  */
108
      deleted->handler->need_signal = 1;
109
      /* Make sure this gets written out first.  */
110
      atomic_write_barrier ();
111
112
      /* Decrement the reference counter.  If it does not reach zero
113
	 wait for the last user.  */
114
      atomic_decrement (&deleted->handler->refcntr);
115
      unsigned int val;
116
      while ((val = deleted->handler->refcntr) != 0)
117
	futex_wait_simple (&deleted->handler->refcntr, val, FUTEX_PRIVATE);
118
119
      deleted = deleted->next;
120
    }
121
}
(-)a/sysdeps/nptl/fork.c (-3 / +93 lines)
Lines 48-53 pid_t Link Here
48
__libc_fork (void)
48
__libc_fork (void)
49
{
49
{
50
  pid_t pid;
50
  pid_t pid;
51
  struct used_handler
52
  {
53
    struct fork_handler *handler;
54
    struct used_handler *next;
55
  } *allp = NULL;
51
56
52
  /* Determine if we are running multiple threads.  We skip some fork
57
  /* Determine if we are running multiple threads.  We skip some fork
53
     handlers in the single-thread case, to make fork safer to use in
58
     handlers in the single-thread case, to make fork safer to use in
Lines 55-61 __libc_fork (void) Link Here
55
     but our current fork implementation is not.  */
60
     but our current fork implementation is not.  */
56
  bool multiple_threads = THREAD_GETMEM (THREAD_SELF, header.multiple_threads);
61
  bool multiple_threads = THREAD_GETMEM (THREAD_SELF, header.multiple_threads);
57
62
58
  __run_fork_handlers (atfork_run_prepare);
63
  /* Run all the registered preparation handlers.  In reverse order.
64
     While doing this we build up a list of all the entries.  */
65
  struct fork_handler *runp;
66
  while ((runp = __fork_handlers) != NULL)
67
    {
68
      /* Make sure we read from the current RUNP pointer.  */
69
      atomic_full_barrier ();
70
71
      unsigned int oldval = runp->refcntr;
72
73
      if (oldval == 0)
74
	/* This means some other thread removed the list just after
75
	   the pointer has been loaded.  Try again.  Either the list
76
	   is empty or we can retry it.  */
77
	continue;
78
79
      /* Bump the reference counter.  */
80
      if (atomic_compare_and_exchange_bool_acq (&__fork_handlers->refcntr,
81
						oldval + 1, oldval))
82
	/* The value changed, try again.  */
83
	continue;
84
85
      /* We bumped the reference counter for the first entry in the
86
	 list.  That means that none of the following entries will
87
	 just go away.  The unloading code works in the order of the
88
	 list.
89
90
	 While executing the registered handlers we are building a
91
	 list of all the entries so that we can go backward later on.  */
92
      while (1)
93
	{
94
	  /* Execute the handler if there is one.  */
95
	  if (runp->prepare_handler != NULL)
96
	    runp->prepare_handler ();
97
98
	  /* Create a new element for the list.  */
99
	  struct used_handler *newp
100
	    = (struct used_handler *) alloca (sizeof (*newp));
101
	  newp->handler = runp;
102
	  newp->next = allp;
103
	  allp = newp;
104
105
	  /* Advance to the next handler.  */
106
	  runp = runp->next;
107
	  if (runp == NULL)
108
	    break;
109
110
	  /* Bump the reference counter for the next entry.  */
111
	  atomic_increment (&runp->refcntr);
112
	}
113
114
      /* We are done.  */
115
      break;
116
    }
59
117
60
  /* If we are not running multiple threads, we do not have to
118
  /* If we are not running multiple threads, we do not have to
61
     preserve lock state.  If fork runs from a signal handler, only
119
     preserve lock state.  If fork runs from a signal handler, only
Lines 134-140 __libc_fork (void) Link Here
134
      __rtld_lock_initialize (GL(dl_load_lock));
192
      __rtld_lock_initialize (GL(dl_load_lock));
135
193
136
      /* Run the handlers registered for the child.  */
194
      /* Run the handlers registered for the child.  */
137
      __run_fork_handlers (atfork_run_child);
195
      while (allp != NULL)
196
	{
197
	  if (allp->handler->child_handler != NULL)
198
	    allp->handler->child_handler ();
199
200
	  /* Note that we do not have to wake any possible waiter.
201
	     This is the only thread in the new process.  The count
202
	     may have been bumped up by other threads doing a fork.
203
	     We reset it to 1, to avoid waiting for non-existing
204
	     thread(s) to release the count.  */
205
	  allp->handler->refcntr = 1;
206
207
	  /* XXX We could at this point look through the object pool
208
	     and mark all objects not on the __fork_handlers list as
209
	     unused.  This is necessary in case the fork() happened
210
	     while another thread called dlclose() and that call had
211
	     to create a new list.  */
212
213
	  allp = allp->next;
214
	}
215
216
      /* Initialize the fork lock.  */
217
      __fork_lock = LLL_LOCK_INITIALIZER;
138
    }
218
    }
139
  else
219
  else
140
    {
220
    {
Lines 149-155 __libc_fork (void) Link Here
149
	}
229
	}
150
230
151
      /* Run the handlers registered for the parent.  */
231
      /* Run the handlers registered for the parent.  */
152
      __run_fork_handlers (atfork_run_parent);
232
      while (allp != NULL)
233
	{
234
	  if (allp->handler->parent_handler != NULL)
235
	    allp->handler->parent_handler ();
236
237
	  if (atomic_decrement_and_test (&allp->handler->refcntr)
238
	      && allp->handler->need_signal)
239
	    futex_wake (&allp->handler->refcntr, 1, FUTEX_PRIVATE);
240
241
	  allp = allp->next;
242
	}
153
    }
243
    }
154
244
155
  return pid;
245
  return pid;
(-)a/sysdeps/nptl/fork.h (-18 / +13 lines)
Lines 24-60 extern unsigned long int __fork_generation attribute_hidden; Link Here
24
/* Pointer to the fork generation counter in the thread library.  */
24
/* Pointer to the fork generation counter in the thread library.  */
25
extern unsigned long int *__fork_generation_pointer attribute_hidden;
25
extern unsigned long int *__fork_generation_pointer attribute_hidden;
26
26
27
/* Lock to protect allocation and deallocation of fork handlers.  */
28
extern int __fork_lock attribute_hidden;
29
27
/* Elements of the fork handler lists.  */
30
/* Elements of the fork handler lists.  */
28
struct fork_handler
31
struct fork_handler
29
{
32
{
33
  struct fork_handler *next;
30
  void (*prepare_handler) (void);
34
  void (*prepare_handler) (void);
31
  void (*parent_handler) (void);
35
  void (*parent_handler) (void);
32
  void (*child_handler) (void);
36
  void (*child_handler) (void);
33
  void *dso_handle;
37
  void *dso_handle;
38
  unsigned int refcntr;
39
  int need_signal;
34
};
40
};
35
41
42
/* The single linked list of all currently registered for handlers.  */
43
extern struct fork_handler *__fork_handlers attribute_hidden;
44
45
36
/* Function to call to unregister fork handlers.  */
46
/* Function to call to unregister fork handlers.  */
37
extern void __unregister_atfork (void *dso_handle) attribute_hidden;
47
extern void __unregister_atfork (void *dso_handle) attribute_hidden;
38
#define UNREGISTER_ATFORK(dso_handle) __unregister_atfork (dso_handle)
48
#define UNREGISTER_ATFORK(dso_handle) __unregister_atfork (dso_handle)
39
49
40
enum __run_fork_handler_type
41
{
42
  atfork_run_prepare,
43
  atfork_run_child,
44
  atfork_run_parent
45
};
46
47
/* Run the atfork handlers and lock/unlock the internal lock depending
48
   of the WHO argument:
49
50
   - atfork_run_prepare: run all the PREPARE_HANDLER in reverse order of
51
			 insertion and locks the internal lock.
52
   - atfork_run_child: run all the CHILD_HANDLER and unlocks the internal
53
		       lock.
54
   - atfork_run_parent: run all the PARENT_HANDLER and unlocks the internal
55
			lock.  */
56
extern void __run_fork_handlers (enum __run_fork_handler_type who)
57
  attribute_hidden;
58
50
59
/* C library side function to register new fork handlers.  */
51
/* C library side function to register new fork handlers.  */
60
extern int __register_atfork (void (*__prepare) (void),
52
extern int __register_atfork (void (*__prepare) (void),
Lines 62-64 extern int __register_atfork (void (*__prepare) (void), Link Here
62
			      void (*__child) (void),
54
			      void (*__child) (void),
63
			      void *dso_handle);
55
			      void *dso_handle);
64
libc_hidden_proto (__register_atfork)
56
libc_hidden_proto (__register_atfork)
57
58
/* Add a new element to the fork list.  */
59
extern void __linkin_atfork (struct fork_handler *newp) attribute_hidden;
(-)a/sysdeps/nptl/libc-lockP.h (+2 lines)
Lines 319-324 __libc_cleanup_routine (struct __pthread_cleanup_frame *f) Link Here
319
/* Register handlers to execute before and after `fork'.  Note that the
319
/* Register handlers to execute before and after `fork'.  Note that the
320
   last parameter is NULL.  The handlers registered by the libc are
320
   last parameter is NULL.  The handlers registered by the libc are
321
   never removed so this is OK.  */
321
   never removed so this is OK.  */
322
#define __libc_atfork(PREPARE, PARENT, CHILD) \
323
  __register_atfork (PREPARE, PARENT, CHILD, NULL)
322
extern int __register_atfork (void (*__prepare) (void),
324
extern int __register_atfork (void (*__prepare) (void),
323
			      void (*__parent) (void),
325
			      void (*__parent) (void),
324
			      void (*__child) (void),
326
			      void (*__child) (void),

Return to bug 685024