summaryrefslogtreecommitdiff
path: root/libpthread/nptl/sysdeps/unix/sysv/linux/register-atfork.c
blob: f6c3de4bc3aecfb73ff6c45b8261d9f045dd65b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/* Copyright (C) 2002, 2003 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, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "fork.h"


/* Lock to protect allocation and deallocation of fork handlers.  */
lll_lock_t __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);

  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;

      newp->next = __fork_handlers;
      __fork_handlers = newp;
    }

  /* Release the lock.  */
  lll_unlock (__fork_lock);

  return newp == NULL ? ENOMEM : 0;
}