diff --git a/libc/Android.mk b/libc/Android.mk index f54278ac0..a40998d33 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -353,6 +353,7 @@ libc_common_src_files += \ # up any thumb code. libc_common_src_files += \ bionic/pthread.c.arm \ + bionic/pthread-atfork.c.arm \ bionic/pthread-rwlocks.c.arm \ bionic/pthread-timers.c.arm \ bionic/ptrace.c.arm @@ -388,6 +389,7 @@ libc_common_src_files += \ arch-x86/string/strncmp_wrapper.S \ arch-x86/string/strlen.S \ bionic/pthread.c \ + bionic/pthread-atfork.c \ bionic/pthread-rwlocks.c \ bionic/pthread-timers.c \ bionic/ptrace.c @@ -425,6 +427,7 @@ libc_common_src_files += \ string/memcmp.c \ string/strlen.c \ bionic/pthread.c \ + bionic/pthread-atfork.c \ bionic/pthread-rwlocks.c \ bionic/pthread-timers.c \ bionic/ptrace.c \ diff --git a/libc/bionic/fork.c b/libc/bionic/fork.c index 79b8fff61..0eedb0119 100644 --- a/libc/bionic/fork.c +++ b/libc/bionic/fork.c @@ -41,9 +41,12 @@ int fork(void) * of error, or in the parent process */ __timer_table_start_stop(1); + __bionic_atfork_run_prepare(); + ret = __fork(); if (ret != 0) { /* not a child process */ __timer_table_start_stop(0); + __bionic_atfork_run_parent(); } else { /* * Newly created process must update cpu accounting. @@ -52,6 +55,7 @@ int fork(void) * as a parameter. */ cpuacct_add(getuid()); + __bionic_atfork_run_child(); } return ret; } diff --git a/libc/bionic/pthread-atfork.c b/libc/bionic/pthread-atfork.c new file mode 100644 index 000000000..3a5189d8c --- /dev/null +++ b/libc/bionic/pthread-atfork.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include + +static pthread_mutex_t handler_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; + +struct atfork_t +{ + CIRCLEQ_ENTRY(atfork_t) entries; + + void (*prepare)(void); + void (*child)(void); + void (*parent)(void); +}; +static CIRCLEQ_HEAD(atfork_head_t, atfork_t) atfork_head = \ + CIRCLEQ_HEAD_INITIALIZER(atfork_head); + +void __bionic_atfork_run_prepare() +{ + struct atfork_t *cursor; + + /* We will lock this here, and unlock it in the parent and child functions. + * This ensures that nobody can modify the handler array between the calls + * to the prepare and parent/child handlers. + * + * TODO: If a handler mucks with the list, it could cause problems. Right + * now it's ok because all they can do is add new items to the end + * of the list, but if/when we implement cleanup in dlclose() things + * will get more interesting... + */ + pthread_mutex_lock(&handler_mutex); + + /* Call pthread_atfork() prepare handlers. Posix states that the prepare + * handlers should be called in the reverse order of the parent/child + * handlers, so we iterate backwards. + */ + for (cursor = atfork_head.cqh_last; + cursor != (void*)&atfork_head; + cursor = cursor->entries.cqe_prev) { + if (cursor->prepare != NULL) { + cursor->prepare(); + } + } +} + +void __bionic_atfork_run_child() +{ + struct atfork_t *cursor; + + /* Call pthread_atfork() child handlers */ + for (cursor = atfork_head.cqh_first; + cursor != (void*)&atfork_head; + cursor = cursor->entries.cqe_next) { + if (cursor->child != NULL) { + cursor->child(); + } + } + + pthread_mutex_unlock(&handler_mutex); +} + +void __bionic_atfork_run_parent() +{ + struct atfork_t *cursor; + + /* Call pthread_atfork() parent handlers */ + for (cursor = atfork_head.cqh_first; + cursor != (void*)&atfork_head; + cursor = cursor->entries.cqe_next) { + if (cursor->parent != NULL) { + cursor->parent(); + } + } + + pthread_mutex_unlock(&handler_mutex); +} + +int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void)) +{ + struct atfork_t *entry = malloc(sizeof(struct atfork_t)); + + if (entry == NULL) { + return ENOMEM; + } + + entry->prepare = prepare; + entry->parent = parent; + entry->child = child; + + pthread_mutex_lock(&handler_mutex); + CIRCLEQ_INSERT_TAIL(&atfork_head, entry, entries); + pthread_mutex_unlock(&handler_mutex); + + return 0; +} diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index eb4e80ccc..655b8f341 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -109,6 +109,9 @@ extern int __pthread_cond_timedwait_relative(pthread_cond_t*, /* needed by fork.c */ extern void __timer_table_start_stop(int stop); +extern void __bionic_atfork_run_prepare(); +extern void __bionic_atfork_run_child(); +extern void __bionic_atfork_run_parent(); __END_DECLS diff --git a/libc/docs/CHANGES.TXT b/libc/docs/CHANGES.TXT index 96f25d34c..b13d8f728 100644 --- a/libc/docs/CHANGES.TXT +++ b/libc/docs/CHANGES.TXT @@ -70,6 +70,8 @@ Differences between current and Android 2.2: - : fixed implementation of fstatfs() (also fixes fpathconf() which uses it). +- Added an implementation of pthread_atfork() + ------------------------------------------------------------------------------- Differences between Android 2.2. and Android 2.1: diff --git a/libc/include/pthread.h b/libc/include/pthread.h index 9773dcba2..2e1b281fb 100644 --- a/libc/include/pthread.h +++ b/libc/include/pthread.h @@ -268,6 +268,8 @@ int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)); int pthread_setname_np(pthread_t thid, const char *thname); +int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void)); + typedef void (*__pthread_cleanup_func_t)(void*); typedef struct __pthread_cleanup_t {