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 |
} |