diff --git a/libc/bionic/pthread-timers.c b/libc/bionic/pthread-timers.c index a53d04b57..b8f748801 100644 --- a/libc/bionic/pthread-timers.c +++ b/libc/bionic/pthread-timers.c @@ -144,6 +144,9 @@ thr_timer_table_alloc( thr_timer_table_t* t ) { thr_timer_t* timer; + if (t == NULL) + return NULL; + pthread_mutex_lock(&t->lock); timer = t->free_timer; if (timer != NULL) { @@ -201,7 +204,7 @@ thr_timer_table_from_id( thr_timer_table_t* t, unsigned index; thr_timer_t* timer; - if (!TIMER_ID_IS_WRAPPED(id)) + if (t == NULL || !TIMER_ID_IS_WRAPPED(id)) return NULL; index = (unsigned) TIMER_ID_UNWRAP(id); @@ -226,15 +229,21 @@ thr_timer_table_from_id( thr_timer_table_t* t, return timer; } -/* the static timer table */ +/* the static timer table - we only create it if the process + * really wants to use SIGEV_THREAD timers, which should be + * pretty infrequent + */ static pthread_once_t __timer_table_once = PTHREAD_ONCE_INIT; -static thr_timer_table_t __timer_table[1]; +static thr_timer_table_t* __timer_table; static void __timer_table_init( void ) { - thr_timer_table_init( __timer_table ); + __timer_table = calloc(1,sizeof(*__timer_table)); + + if (__timer_table != NULL) + thr_timer_table_init( __timer_table ); } static thr_timer_table_t* @@ -254,8 +263,10 @@ __timer_table_get(void) void __timer_table_start_stop( int stop ) { - thr_timer_table_t* table = __timer_table_get(); - thr_timer_table_start_stop(table, stop); + if (__timer_table != NULL) { + thr_timer_table_t* table = __timer_table_get(); + thr_timer_table_start_stop(table, stop); + } } static thr_timer_t* diff --git a/libc/bionic/semaphore.c b/libc/bionic/semaphore.c index fa9de0e41..0c94600b7 100644 --- a/libc/bionic/semaphore.c +++ b/libc/bionic/semaphore.c @@ -29,6 +29,7 @@ #include #include #include +#include int sem_init(sem_t *sem, int pshared, unsigned int value) { @@ -118,6 +119,62 @@ int sem_wait(sem_t *sem) return 0; } +int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout) +{ + int ret; + + if (sem == NULL) { + errno = EINVAL; + return -1; + } + + /* POSIX says we need to try to decrement the semaphore + * before checking the timeout value */ + if (__atomic_dec_if_positive(&sem->count)) + return 0; + + /* check it as per Posix */ + if (abs_timeout == NULL || + abs_timeout->tv_sec < 0 || + abs_timeout->tv_nsec < 0 || + abs_timeout->tv_nsec >= 1000000000) + { + errno = EINVAL; + return -1; + } + + for (;;) { + struct timespec ts; + int ret; + + /* Posix mandates CLOCK_REALTIME here */ + clock_gettime( CLOCK_REALTIME, &ts ); + ts.tv_sec = abs_timeout->tv_sec - ts.tv_sec; + ts.tv_nsec = abs_timeout->tv_nsec - ts.tv_nsec; + if (ts.tv_nsec < 0) { + ts.tv_nsec += 1000000000; + ts.tv_sec -= 1; + } + + if (ts.tv_sec < 0 || ts.tv_nsec < 0) { + errno = ETIMEDOUT; + return -1; + } + + ret = __futex_wait(&sem->count, 0, &ts); + + /* return in case of timeout or interrupt */ + if (ret == -ETIMEDOUT || ret == -EINTR) { + errno = -ret; + return -1; + } + + if (__atomic_dec_if_positive(&sem->count)) + break; + } + return 0; +} + int sem_post(sem_t *sem) { if (sem == NULL) diff --git a/libc/include/semaphore.h b/libc/include/semaphore.h index fdc401a3d..30e31239a 100644 --- a/libc/include/semaphore.h +++ b/libc/include/semaphore.h @@ -50,6 +50,9 @@ extern int sem_trywait(sem_t *); extern int sem_unlink(const char *); extern int sem_wait(sem_t *); +struct timespec; +extern int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); + __END_DECLS #endif /* _SEMAPHORE_H */ diff --git a/libc/stdlib/atexit.c b/libc/stdlib/atexit.c index 97eac4aeb..5bf82ba3a 100644 --- a/libc/stdlib/atexit.c +++ b/libc/stdlib/atexit.c @@ -1,4 +1,4 @@ -/* $OpenBSD: atexit.c,v 1.12 2006/02/22 07:16:32 otto Exp $ */ +/* $OpenBSD: atexit.c,v 1.14 2007/09/05 20:47:47 chl Exp $ */ /* * Copyright (c) 2002 Daniel Hartmeier * All rights reserved. @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "atexit.h" #include "thread_private.h" @@ -45,17 +46,22 @@ struct atexit *__atexit; * function pointer in the first allocated page (the last one in * the linked list) is reserved for the cleanup function. * - * Outside the following two functions, all pages are mprotect()'ed + * Outside the following functions, all pages are mprotect()'ed * to prevent unintentional/malicious corruption. */ /* - * Register a function to be performed at exit. + * Register a function to be performed at exit or when a shared object + * with the given dso handle is unloaded dynamically. Also used as + * the backend for atexit(). For more info on this API, see: + * + * http://www.codesourcery.com/cxx-abi/abi.html#dso-dtor */ int -atexit(void (*fn)(void)) +__cxa_atexit(void (*func)(void *), void *arg, void *dso) { - struct atexit *p; + struct atexit *p = __atexit; + struct atexit_fn *fnp; int pgsize = getpagesize(); int ret = -1; @@ -71,11 +77,11 @@ atexit(void (*fn)(void)) } if (p == NULL) { p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + MAP_ANON | MAP_PRIVATE, -1, 0); if (p == MAP_FAILED) goto unlock; if (__atexit == NULL) { - p->fns[0] = NULL; + memset(&p->fns[0], 0, sizeof(p->fns[0])); p->ind = 1; } else p->ind = 0; @@ -86,7 +92,10 @@ atexit(void (*fn)(void)) if (__atexit_invalid) __atexit_invalid = 0; } - p->fns[p->ind++] = fn; + fnp = &p->fns[p->ind++]; + fnp->fn_ptr.cxa_func = func; + fnp->fn_arg = arg; + fnp->fn_dso = dso; if (mprotect(p, pgsize, PROT_READ)) goto unlock; ret = 0; @@ -95,11 +104,76 @@ unlock: return (ret); } +/* + * Register a function to be performed at exit. + */ +int +atexit(void (*func)(void)) +{ + return (__cxa_atexit((void (*)(void *))func, NULL, NULL)); +} + +/* + * Call all handlers registered with __cxa_atexit() for the shared + * object owning 'dso'. + * Note: if 'dso' is NULL, then all remaining handlers are called. + */ +void +__cxa_finalize(void *dso) +{ + struct atexit *p, *q; + struct atexit_fn fn; + int n, pgsize = getpagesize(); + static int call_depth; + + if (__atexit_invalid) + return; + + call_depth++; + + for (p = __atexit; p != NULL; p = p->next) { + for (n = p->ind; --n >= 0;) { + if (p->fns[n].fn_ptr.cxa_func == NULL) + continue; /* already called */ + if (dso != NULL && dso != p->fns[n].fn_dso) + continue; /* wrong DSO */ + + /* + * Mark handler as having been already called to avoid + * dupes and loops, then call the appropriate function. + */ + fn = p->fns[n]; + if (mprotect(p, pgsize, PROT_READ | PROT_WRITE) == 0) { + p->fns[n].fn_ptr.cxa_func = NULL; + mprotect(p, pgsize, PROT_READ); + } + if (dso != NULL) + (*fn.fn_ptr.cxa_func)(fn.fn_arg); + else + (*fn.fn_ptr.std_func)(); + } + } + + /* + * If called via exit(), unmap the pages since we have now run + * all the handlers. We defer this until calldepth == 0 so that + * we don't unmap things prematurely if called recursively. + */ + if (dso == NULL && --call_depth == 0) { + for (p = __atexit; p != NULL; ) { + q = p; + p = p->next; + munmap(q, pgsize); + } + __atexit = NULL; + } +} + /* * Register the cleanup function */ void -__atexit_register_cleanup(void (*fn)(void)) +__atexit_register_cleanup(void (*func)(void)) { struct atexit *p; int pgsize = getpagesize(); @@ -112,7 +186,7 @@ __atexit_register_cleanup(void (*fn)(void)) p = p->next; if (p == NULL) { p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + MAP_ANON | MAP_PRIVATE, -1, 0); if (p == MAP_FAILED) goto unlock; p->ind = 1; @@ -126,7 +200,9 @@ __atexit_register_cleanup(void (*fn)(void)) if (mprotect(p, pgsize, PROT_READ | PROT_WRITE)) goto unlock; } - p->fns[0] = fn; + p->fns[0].fn_ptr.std_func = func; + p->fns[0].fn_arg = NULL; + p->fns[0].fn_dso = NULL; mprotect(p, pgsize, PROT_READ); unlock: _ATEXIT_UNLOCK(); diff --git a/libc/stdlib/atexit.h b/libc/stdlib/atexit.h index 21b0c2e53..1b23565dd 100644 --- a/libc/stdlib/atexit.h +++ b/libc/stdlib/atexit.h @@ -1,4 +1,4 @@ -/* $OpenBSD: atexit.h,v 1.6 2003/07/31 07:08:42 deraadt Exp $ */ +/* $OpenBSD: atexit.h,v 1.7 2007/09/03 14:40:16 millert Exp $ */ /* * Copyright (c) 2002 Daniel Hartmeier @@ -34,8 +34,18 @@ struct atexit { struct atexit *next; /* next in list */ int ind; /* next index in this table */ int max; /* max entries >= ATEXIT_SIZE */ - void (*fns[1])(void); /* the table itself */ + struct atexit_fn { + union { + void (*std_func)(void); + void (*cxa_func)(void *); + } fn_ptr; + void *fn_arg; /* argument for CXA callback */ + void *fn_dso; /* shared module handle */ + } fns[1]; /* the table itself */ }; extern int __atexit_invalid; extern struct atexit *__atexit; /* points to head of LIFO stack */ + +int __cxa_atexit(void (*)(void *), void *, void *); +void __cxa_finalize(void *); diff --git a/libc/stdlib/exit.c b/libc/stdlib/exit.c index 8fafcf592..83fe3d2de 100644 --- a/libc/stdlib/exit.c +++ b/libc/stdlib/exit.c @@ -1,4 +1,4 @@ -/* $OpenBSD: exit.c,v 1.11 2005/08/08 08:05:36 espie Exp $ */ +/* $OpenBSD: exit.c,v 1.12 2007/09/03 14:40:16 millert Exp $ */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. @@ -50,20 +50,10 @@ int __isthreaded = 0; void exit(int status) { - struct atexit *p, *q; - int n, pgsize = getpagesize(); - - if (!__atexit_invalid) { - p = __atexit; - while (p != NULL) { - for (n = p->ind; --n >= 0;) - if (p->fns[n] != NULL) - (*p->fns[n])(); - q = p; - p = p->next; - munmap(q, pgsize); - } - } - /* cleanup, if registered, was called through fns[0] in the last page */ + /* + * Call functions registered by atexit() or _cxa_atexit() + * (including the stdio cleanup routine) and then _exit(). + */ + __cxa_finalize(NULL); _exit(status); } diff --git a/libc/unistd/abort.c b/libc/unistd/abort.c index 32912870c..a02bebac3 100644 --- a/libc/unistd/abort.c +++ b/libc/unistd/abort.c @@ -56,9 +56,11 @@ abort(void) if (cleanup_called == 0) { while (p != NULL && p->next != NULL) p = p->next; - if (p != NULL && p->fns[0] != NULL) { + /* the check for fn_dso == NULL is mostly paranoia */ + if (p != NULL && p->fns[0].fn_dso == NULL && + p->fns[0].fn_ptr.std_func != NULL) { cleanup_called = 1; - (*p->fns[0])(); + (*p->fns[0].fn_ptr.std_func)(); } }