glibc/nptl/register-atfork.c

145 lines
3.7 KiB
C

/* Copyright (C) 2002-2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
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
<http://www.gnu.org/licenses/>. */
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fork.h>
#include <atomic.h>
struct fork_handler *__fork_handlers;
/* Lock to protect allocation and deallocation of fork handlers. */
int __fork_lock = LLL_LOCK_INITIALIZER;
/* Number of pre-allocated handler entries. */
#define NHANDLER 48
/* Memory pool for fork handler structures. */
static struct fork_handler_pool
{
struct fork_handler_pool *next;
struct fork_handler mem[NHANDLER];
} fork_handler_pool;
static struct fork_handler *
fork_handler_alloc (void)
{
struct fork_handler_pool *runp = &fork_handler_pool;
struct fork_handler *result = NULL;
unsigned int i;
do
{
/* Search for an empty entry. */
for (i = 0; i < NHANDLER; ++i)
if (runp->mem[i].refcntr == 0)
goto found;
}
while ((runp = runp->next) != NULL);
/* We have to allocate a new entry. */
runp = (struct fork_handler_pool *) calloc (1, sizeof (*runp));
if (runp != NULL)
{
/* Enqueue the new memory pool into the list. */
runp->next = fork_handler_pool.next;
fork_handler_pool.next = runp;
/* We use the last entry on the page. This means when we start
searching from the front the next time we will find the first
entry unused. */
i = NHANDLER - 1;
found:
result = &runp->mem[i];
result->refcntr = 1;
result->need_signal = 0;
}
return result;
}
int
__register_atfork (void (*prepare) (void), void (*parent) (void),
void (*child) (void), void *dso_handle)
{
/* Get the lock to not conflict with other allocations. */
lll_lock (__fork_lock, LLL_PRIVATE);
struct fork_handler *newp = fork_handler_alloc ();
if (newp != NULL)
{
/* Initialize the new record. */
newp->prepare_handler = prepare;
newp->parent_handler = parent;
newp->child_handler = child;
newp->dso_handle = dso_handle;
__linkin_atfork (newp);
}
/* Release the lock. */
lll_unlock (__fork_lock, LLL_PRIVATE);
return newp == NULL ? ENOMEM : 0;
}
libc_hidden_def (__register_atfork)
void
attribute_hidden
__linkin_atfork (struct fork_handler *newp)
{
do
newp->next = __fork_handlers;
while (catomic_compare_and_exchange_bool_acq (&__fork_handlers,
newp, newp->next) != 0);
}
libc_freeres_fn (free_mem)
{
/* Get the lock to not conflict with running forks. */
lll_lock (__fork_lock, LLL_PRIVATE);
/* No more fork handlers. */
__fork_handlers = NULL;
/* Free eventually allocated memory blocks for the object pool. */
struct fork_handler_pool *runp = fork_handler_pool.next;
memset (&fork_handler_pool, '\0', sizeof (fork_handler_pool));
/* Release the lock. */
lll_unlock (__fork_lock, LLL_PRIVATE);
/* We can free the memory after releasing the lock. */
while (runp != NULL)
{
struct fork_handler_pool *oldp = runp;
runp = runp->next;
free (oldp);
}
}