posix: Consolidate register-atfork

Both htl and nptl uses a different data structure to implement atfork
handlers.  The nptl one was refactored by 27761a1042 to use a dynarray
which simplifies the code.

This patch moves the nptl one to be the generic implementation and
replace Hurd linked one.  Different than previous NPTL, Hurd also uses
a global lock, so performance should be similar.

Checked on x86_64-linux-gnu, i686-linux-gnu, and with a build for
i686-gnu.
This commit is contained in:
Adhemerval Zanella 2021-01-18 15:10:02 -03:00
parent cdba937662
commit 2b47727c68
10 changed files with 66 additions and 219 deletions

View File

@ -165,7 +165,7 @@ headers := \
distribute :=
routines := forward libc_pthread_init alloca_cutoff register-atfork pt-atfork
routines := forward libc_pthread_init alloca_cutoff pt-atfork
shared-only-routines = forward
static-only-routines = pt-atfork

View File

@ -1,157 +0,0 @@
/* Atfork handling. Hurd pthread version.
Copyright (C) 2002-2021 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <errno.h>
#include <stdlib.h>
#include <libc-lock.h>
#include <fork.h>
struct atfork
{
void (*prepare) (void);
void (*parent) (void);
void (*child) (void);
void *dso_handle;
struct atfork *prev;
struct atfork *next;
};
/* TODO: better locking */
__libc_lock_define_initialized (static, atfork_lock);
static struct atfork *fork_handlers, *fork_last_handler;
static void
atfork_pthread_prepare (void)
{
struct atfork *handlers, *last_handler;
__libc_lock_lock (atfork_lock);
handlers = fork_handlers;
last_handler = fork_last_handler;
__libc_lock_unlock (atfork_lock);
if (last_handler == NULL)
return;
while (1)
{
if (last_handler->prepare != NULL)
last_handler->prepare ();
if (last_handler == handlers)
break;
last_handler = last_handler->prev;
}
}
text_set_element (_hurd_atfork_prepare_hook, atfork_pthread_prepare);
static void
atfork_pthread_parent (void)
{
struct atfork *handlers;
__libc_lock_lock (atfork_lock);
handlers = fork_handlers;
__libc_lock_unlock (atfork_lock);
while (handlers != NULL)
{
if (handlers->parent != NULL)
handlers->parent ();
handlers = handlers->next;
}
}
text_set_element (_hurd_atfork_parent_hook, atfork_pthread_parent);
static void
atfork_pthread_child (void)
{
struct atfork *handlers;
__libc_lock_lock (atfork_lock);
handlers = fork_handlers;
__libc_lock_unlock (atfork_lock);
while (handlers != NULL)
{
if (handlers->child != NULL)
handlers->child ();
handlers = handlers->next;
}
}
text_set_element (_hurd_atfork_child_hook, atfork_pthread_child);
int
__register_atfork (void (*prepare) (void),
void (*parent) (void),
void (*child) (void),
void *dso_handle)
{
struct atfork *new = malloc (sizeof (*new));
if (new == NULL)
return errno;
new->prepare = prepare;
new->parent = parent;
new->child = child;
new->dso_handle = dso_handle;
new->next = NULL;
__libc_lock_lock (atfork_lock);
new->prev = fork_last_handler;
if (fork_last_handler != NULL)
fork_last_handler->next = new;
if (fork_handlers == NULL)
fork_handlers = new;
fork_last_handler = new;
__libc_lock_unlock (atfork_lock);
return 0;
}
libc_hidden_def (__register_atfork)
void
__unregister_atfork (void *dso_handle)
{
struct atfork **handlers, *prev = NULL, *next;
__libc_lock_lock (atfork_lock);
handlers = &fork_handlers;
while (*handlers != NULL)
{
if ((*handlers)->dso_handle == dso_handle)
{
/* Drop this handler from the list. */
if (*handlers == fork_last_handler)
{
/* Was last, new last is prev, if any. */
fork_last_handler = prev;
}
next = (*handlers)->next;
if (next != NULL)
next->prev = prev;
*handlers = next;
}
else
{
/* Just proceed to next handler. */
prev = *handlers;
handlers = &prev->next;
}
}
__libc_lock_unlock (atfork_lock);
}

View File

@ -1,5 +1,5 @@
/* Register fork handlers. Generic version.
Copyright (C) 2002-2021 Free Software Foundation, Inc.
/* Internal pthread_atfork definitions.
Copyright (C) 2021 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@ -16,10 +16,42 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef _REGISTER_ATFORK_H
#define _REGISTER_ATFORK_H
/* Elements of the fork handler lists. */
struct fork_handler
{
void (*prepare_handler) (void);
void (*parent_handler) (void);
void (*child_handler) (void);
void *dso_handle;
};
/* Function to call to unregister fork handlers. */
extern void __unregister_atfork (void *dso_handle) attribute_hidden;
#define UNREGISTER_ATFORK(dso_handle) __unregister_atfork (dso_handle)
enum __run_fork_handler_type
{
atfork_run_prepare,
atfork_run_child,
atfork_run_parent
};
/* Run the atfork handlers and lock/unlock the internal lock depending
of the WHO argument:
- atfork_run_prepare: run all the PREPARE_HANDLER in reverse order of
insertion and locks the internal lock.
- atfork_run_child: run all the CHILD_HANDLER and unlocks the internal
lock.
- atfork_run_parent: run all the PARENT_HANDLER and unlocks the internal
lock.
Perform locking only if DO_LOCKING. */
extern void __run_fork_handlers (enum __run_fork_handler_type who,
_Bool do_locking) attribute_hidden;
/* C library side function to register new fork handlers. */
extern int __register_atfork (void (*__prepare) (void),
@ -27,3 +59,5 @@ extern int __register_atfork (void (*__prepare) (void),
void (*__child) (void),
void *dso_handle);
libc_hidden_proto (__register_atfork)
#endif

View File

@ -74,7 +74,6 @@ routines = \
pthread_self \
pthread_setschedparam \
pthread_sigmask \
register-atfork \
shared-only-routines = forward
static-only-routines = pthread_atfork

View File

@ -39,7 +39,7 @@ routines := \
times \
wait waitpid wait3 wait4 waitid \
alarm sleep pause nanosleep \
fork vfork _exit \
fork vfork _exit register-atfork \
execve fexecve execv execle execl execvp execlp execvpe \
getpid getppid \
getuid geteuid getgid getegid getgroups setuid setgid group_member \

View File

@ -16,11 +16,9 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fork.h>
#include <atomic.h>
#include <libc-lock.h>
#include <stdbool.h>
#include <register-atfork.h>
#define DYNARRAY_ELEMENT struct fork_handler
#define DYNARRAY_STRUCT fork_handler_list

View File

@ -6,3 +6,22 @@
parameter which is the DSO handle for the DSO which gets unloaded.
The function so called has to remove the atfork handlers registered
by this module. */
/* System specific fork definition. Generic version.
Copyright (C) 2002-2021 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */

View File

@ -20,6 +20,7 @@
#include <pt-internal.h>
#include <fork.h>
#include <dso_handle.h>
#include <register-atfork.h>
/* Hide the symbol so that no definition but the one locally in the
executable or DSO is used. */

View File

@ -30,6 +30,7 @@
#include <malloc/malloc-internal.h>
#include <nss/nss_database.h>
#include <unwind-link.h>
#include <register-atfork.h>
#undef __fork
@ -37,12 +38,6 @@
/* Things that want to be locked while forking. */
symbol_set_declare (_hurd_fork_locks)
/* Application callbacks registered through pthread_atfork. */
DEFINE_HOOK (_hurd_atfork_prepare_hook, (void));
DEFINE_HOOK (_hurd_atfork_child_hook, (void));
DEFINE_HOOK (_hurd_atfork_parent_hook, (void));
/* Things that want to be called before we fork, to prepare the parent for
task_create, when the new child task will inherit our address space. */
DEFINE_HOOK (_hurd_fork_prepare_hook, (void));
@ -72,7 +67,7 @@ __fork (void)
struct hurd_sigstate *volatile ss;
struct nss_database_data nss_database_data;
RUN_HOOK (_hurd_atfork_prepare_hook, ());
__run_fork_handlers (atfork_run_prepare, true);
ss = _hurd_self_sigstate ();
__spin_lock (&ss->critical_section_lock);
@ -726,10 +721,8 @@ __fork (void)
if (!err)
{
if (pid != 0)
RUN_HOOK (_hurd_atfork_parent_hook, ());
else
RUN_HOOK (_hurd_atfork_child_hook, ());
__run_fork_handlers (pid == 0 ? atfork_run_child : atfork_run_parent,
true);
}
return err ? __hurd_fail (err) : pid;

View File

@ -17,50 +17,10 @@
<https://www.gnu.org/licenses/>. */
#include <lowlevellock.h>
#include <register-atfork.h>
/* The fork generation counter, defined in libpthread. */
extern unsigned long int __fork_generation attribute_hidden;
/* Pointer to the fork generation counter in the thread library. */
extern unsigned long int *__fork_generation_pointer attribute_hidden;
/* Elements of the fork handler lists. */
struct fork_handler
{
void (*prepare_handler) (void);
void (*parent_handler) (void);
void (*child_handler) (void);
void *dso_handle;
};
/* Function to call to unregister fork handlers. */
extern void __unregister_atfork (void *dso_handle) attribute_hidden;
#define UNREGISTER_ATFORK(dso_handle) __unregister_atfork (dso_handle)
enum __run_fork_handler_type
{
atfork_run_prepare,
atfork_run_child,
atfork_run_parent
};
/* Run the atfork handlers and lock/unlock the internal lock depending
of the WHO argument:
- atfork_run_prepare: run all the PREPARE_HANDLER in reverse order of
insertion and locks the internal lock.
- atfork_run_child: run all the CHILD_HANDLER and unlocks the internal
lock.
- atfork_run_parent: run all the PARENT_HANDLER and unlocks the internal
lock.
Perform locking only if DO_LOCKING. */
extern void __run_fork_handlers (enum __run_fork_handler_type who,
_Bool do_locking) attribute_hidden;
/* C library side function to register new fork handlers. */
extern int __register_atfork (void (*__prepare) (void),
void (*__parent) (void),
void (*__child) (void),
void *dso_handle);
libc_hidden_proto (__register_atfork)