diff --git a/libc/Android.mk b/libc/Android.mk index f55f9fe56..46641d54f 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -440,18 +440,19 @@ endif # !arm # Define some common cflags # ======================================================== libc_common_cflags := \ - -DWITH_ERRLIST \ - -DANDROID_CHANGES \ - -DUSE_LOCKS \ - -DREALLOC_ZERO_BYTES_FREES \ - -D_LIBC=1 \ - -DSOFTFLOAT \ - -DFLOATING_POINT \ - -DINET6 \ - -I$(LOCAL_PATH)/private \ - -DUSE_DL_PREFIX \ - -DPOSIX_MISTAKE \ - -DLOG_ON_HEAP_ERROR \ + -DWITH_ERRLIST \ + -DANDROID_CHANGES \ + -DUSE_LOCKS \ + -DREALLOC_ZERO_BYTES_FREES \ + -D_LIBC=1 \ + -DSOFTFLOAT \ + -DFLOATING_POINT \ + -DINET6 \ + -I$(LOCAL_PATH)/private \ + -DUSE_DL_PREFIX \ + -DPOSIX_MISTAKE \ + -DLOG_ON_HEAP_ERROR \ + -std=gnu99 # these macro definitions are required to implement the # 'timezone' and 'daylight' global variables, as well as diff --git a/libc/bionic/pthread-timers.c b/libc/bionic/pthread-timers.c index ae04029eb..23d31dff1 100644 --- a/libc/bionic/pthread-timers.c +++ b/libc/bionic/pthread-timers.c @@ -9,7 +9,7 @@ * 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 + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,55 +19,56 @@ * 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 + * 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 "pthread_internal.h" -#include -#include + #include +#include +#include +#include -/* This file implements the support required to implement SIGEV_THREAD posix - * timers. See the following pages for additionnal details: - * - * www.opengroup.org/onlinepubs/000095399/functions/timer_create.html - * www.opengroup.org/onlinepubs/000095399/functions/timer_settime.html - * www.opengroup.org/onlinepubs/000095399/functions/xsh_chap02_04.html#tag_02_04_01 - * - * The Linux kernel doesn't support these, so we need to implement them in the - * C library. We use a very basic scheme where each timer is associated to a - * thread that will loop, waiting for timeouts or messages from the program - * corresponding to calls to timer_settime() and timer_delete(). - * - * Note also an important thing: Posix mandates that in the case of fork(), - * the timers of the child process should be disarmed, but not deleted. - * this is implemented by providing a fork() wrapper (see bionic/fork.c) which - * stops all timers before the fork, and only re-start them in case of error - * or in the parent process. - * - * the stop/start is implemented by the __timer_table_start_stop() function - * below. - */ - -/* normal (i.e. non-SIGEV_THREAD) timer ids are created directly by the kernel - * and are passed as is to/from the caller. - * - * on the other hand, a SIGEV_THREAD timer ID will have its TIMER_ID_WRAP_BIT - * always set to 1. In this implementation, this is always bit 31, which is - * guaranteed to never be used by kernel-provided timer ids - * - * (see code in /lib/idr.c, used to manage IDs, to see why) - */ +// Normal (i.e. non-SIGEV_THREAD) timers are created directly by the kernel +// and are passed as is to/from the caller. +// +// This file also implements the support required for SIGEV_THREAD ("POSIX interval") +// timers. See the following pages for additional details: +// +// www.opengroup.org/onlinepubs/000095399/functions/timer_create.html +// www.opengroup.org/onlinepubs/000095399/functions/timer_settime.html +// www.opengroup.org/onlinepubs/000095399/functions/xsh_chap02_04.html#tag_02_04_01 +// +// The Linux kernel doesn't support these, so we need to implement them in the +// C library. We use a very basic scheme where each timer is associated to a +// thread that will loop, waiting for timeouts or messages from the program +// corresponding to calls to timer_settime() and timer_delete(). +// +// Note also an important thing: Posix mandates that in the case of fork(), +// the timers of the child process should be disarmed, but not deleted. +// this is implemented by providing a fork() wrapper (see bionic/fork.c) which +// stops all timers before the fork, and only re-start them in case of error +// or in the parent process. +// +// This stop/start is implemented by the __timer_table_start_stop() function +// below. +// +// A SIGEV_THREAD timer ID will always have its TIMER_ID_WRAP_BIT +// set to 1. In this implementation, this is always bit 31, which is +// guaranteed to never be used by kernel-provided timer ids +// +// (See code in /lib/idr.c, used to manage IDs, to see why.) #define TIMER_ID_WRAP_BIT 0x80000000 #define TIMER_ID_WRAP(id) ((timer_t)((id) | TIMER_ID_WRAP_BIT)) #define TIMER_ID_UNWRAP(id) ((timer_t)((id) & ~TIMER_ID_WRAP_BIT)) #define TIMER_ID_IS_WRAPPED(id) (((id) & TIMER_ID_WRAP_BIT) != 0) -/* this value is used internally to indicate a 'free' or 'zombie' +/* this value is used internally to indicate a 'free' or 'zombie' * thr_timer structure. Here, 'zombie' means that timer_delete() * has been called, but that the corresponding thread hasn't * exited yet. @@ -171,25 +172,23 @@ thr_timer_table_free( thr_timer_table_t* t, thr_timer_t* timer ) } -static void -thr_timer_table_start_stop( thr_timer_table_t* t, int stop ) -{ - int nn; +static void thr_timer_table_start_stop(thr_timer_table_t* t, int stop) { + if (t == NULL) { + return; + } - pthread_mutex_lock(&t->lock); - - for (nn = 0; nn < MAX_THREAD_TIMERS; nn++) { - thr_timer_t* timer = &t->timers[nn]; - - if (TIMER_ID_IS_VALID(timer->id)) { - /* tell the thread to start/stop */ - pthread_mutex_lock(&timer->mutex); - timer->stopped = stop; - pthread_cond_signal( &timer->cond ); - pthread_mutex_unlock(&timer->mutex); - } + pthread_mutex_lock(&t->lock); + for (int nn = 0; nn < MAX_THREAD_TIMERS; ++nn) { + thr_timer_t* timer = &t->timers[nn]; + if (TIMER_ID_IS_VALID(timer->id)) { + // Tell the thread to start/stop. + pthread_mutex_lock(&timer->mutex); + timer->stopped = stop; + pthread_cond_signal( &timer->cond ); + pthread_mutex_unlock(&timer->mutex); } - pthread_mutex_unlock(&t->lock); + } + pthread_mutex_unlock(&t->lock); } @@ -234,23 +233,19 @@ thr_timer_table_from_id( thr_timer_table_t* t, * pretty infrequent */ -static pthread_once_t __timer_table_once = PTHREAD_ONCE_INIT; -static thr_timer_table_t* __timer_table; +static pthread_once_t __timer_table_once = PTHREAD_ONCE_INIT; +static thr_timer_table_t* __timer_table; -static void -__timer_table_init( void ) -{ - __timer_table = calloc(1,sizeof(*__timer_table)); - - if (__timer_table != NULL) - thr_timer_table_init( __timer_table ); +static void __timer_table_init(void) { + __timer_table = calloc(1, sizeof(*__timer_table)); + if (__timer_table != NULL) { + thr_timer_table_init(__timer_table); + } } -static thr_timer_table_t* -__timer_table_get(void) -{ - pthread_once( &__timer_table_once, __timer_table_init ); - return __timer_table; +static thr_timer_table_t* __timer_table_get(void) { + pthread_once(&__timer_table_once, __timer_table_init); + return __timer_table; } /** POSIX THREAD TIMERS CLEANUP ON FORK @@ -260,13 +255,9 @@ __timer_table_get(void) ** requirements: the timers of fork child processes must be ** disarmed but not deleted. **/ -__LIBC_HIDDEN__ void -__timer_table_start_stop( int stop ) -{ - if (__timer_table != NULL) { - thr_timer_table_t* table = __timer_table_get(); - thr_timer_table_start_stop(table, stop); - } +__LIBC_HIDDEN__ void __timer_table_start_stop(int stop) { + // We access __timer_table directly so we don't create it if it doesn't yet exist. + thr_timer_table_start_stop(__timer_table, stop); } static thr_timer_t* @@ -293,85 +284,76 @@ thr_timer_unlock( thr_timer_t* t ) /** POSIX TIMERS APIs */ -/* first, declare the syscall stubs */ -extern int __timer_create( clockid_t, struct sigevent*, timer_t* ); -extern int __timer_delete( timer_t ); -extern int __timer_gettime( timer_t, struct itimerspec* ); -extern int __timer_settime( timer_t, int, const struct itimerspec*, struct itimerspec* ); +extern int __timer_create(clockid_t, struct sigevent*, timer_t*); +extern int __timer_delete(timer_t); +extern int __timer_gettime(timer_t, struct itimerspec*); +extern int __timer_settime(timer_t, int, const struct itimerspec*, struct itimerspec*); extern int __timer_getoverrun(timer_t); -static void* timer_thread_start( void* ); +static void* timer_thread_start(void*); -/* then the wrappers themselves */ -int -timer_create( clockid_t clockid, struct sigevent* evp, timer_t *ptimerid) -{ - /* if not a SIGEV_THREAD timer, direct creation by the kernel */ - if (__likely(evp == NULL || evp->sigev_notify != SIGEV_THREAD)) - return __timer_create( clockid, evp, ptimerid ); +int timer_create(clockid_t clock_id, struct sigevent* evp, timer_t* timer_id) { + // If not a SIGEV_THREAD timer, the kernel can handle it without our help. + if (__likely(evp == NULL || evp->sigev_notify != SIGEV_THREAD)) { + return __timer_create(clock_id, evp, timer_id); + } - // check arguments - if (evp->sigev_notify_function == NULL) { - errno = EINVAL; - return -1; - } + // Check arguments. + if (evp->sigev_notify_function == NULL) { + errno = EINVAL; + return -1; + } - { - struct timespec dummy; + // Check that the clock id is supported by the kernel. + struct timespec dummy; + if (clock_gettime(clock_id, &dummy) < 0 && errno == EINVAL) { + return -1; + } - /* check that the clock id is supported by the kernel */ - if (clock_gettime( clockid, &dummy ) < 0 && errno == EINVAL ) - return -1; - } + // Create a new timer and its thread. + // TODO: use a single global thread for all timers. + thr_timer_table_t* table = __timer_table_get(); + thr_timer_t* timer = thr_timer_table_alloc(table); + if (timer == NULL) { + errno = ENOMEM; + return -1; + } - /* create a new timer and its thread */ - { - thr_timer_table_t* table = __timer_table_get(); - thr_timer_t* timer = thr_timer_table_alloc( table ); - struct sigevent evp0; + // Copy the thread attributes. + if (evp->sigev_notify_attributes == NULL) { + pthread_attr_init(&timer->attributes); + } else { + timer->attributes = ((pthread_attr_t*) evp->sigev_notify_attributes)[0]; + } - if (timer == NULL) { - errno = ENOMEM; - return -1; - } + // Posix says that the default is PTHREAD_CREATE_DETACHED and + // that PTHREAD_CREATE_JOINABLE has undefined behavior. + // So simply always use DETACHED :-) + pthread_attr_setdetachstate(&timer->attributes, PTHREAD_CREATE_DETACHED); - /* copy the thread attributes */ - if (evp->sigev_notify_attributes == NULL) { - pthread_attr_init(&timer->attributes); - } - else { - timer->attributes = ((pthread_attr_t*)evp->sigev_notify_attributes)[0]; - } + timer->callback = evp->sigev_notify_function; + timer->value = evp->sigev_value; + timer->clock = clock_id; - /* Posix says that the default is PTHREAD_CREATE_DETACHED and - * that PTHREAD_CREATE_JOINABLE has undefined behaviour. - * So simply always use DETACHED :-) - */ - pthread_attr_setdetachstate(&timer->attributes, PTHREAD_CREATE_DETACHED); + pthread_mutex_init(&timer->mutex, NULL); + pthread_cond_init(&timer->cond, NULL); - timer->callback = evp->sigev_notify_function; - timer->value = evp->sigev_value; - timer->clock = clockid; + timer->done = 0; + timer->stopped = 0; + timer->expires.tv_sec = timer->expires.tv_nsec = 0; + timer->period.tv_sec = timer->period.tv_nsec = 0; + timer->overruns = 0; - pthread_mutex_init( &timer->mutex, NULL ); - pthread_cond_init( &timer->cond, NULL ); + // Create the thread. + int rc = pthread_create(&timer->thread, &timer->attributes, timer_thread_start, timer); + if (rc != 0) { + thr_timer_table_free(table, timer); + errno = rc; + return -1; + } - timer->done = 0; - timer->stopped = 0; - timer->expires.tv_sec = timer->expires.tv_nsec = 0; - timer->period.tv_sec = timer->period.tv_nsec = 0; - timer->overruns = 0; - - /* create the thread */ - if (pthread_create( &timer->thread, &timer->attributes, timer_thread_start, timer ) < 0) { - thr_timer_table_free( __timer_table, timer ); - errno = ENOMEM; - return -1; - } - - *ptimerid = timer->id; - return 0; - } + *timer_id = timer->id; + return 0; } @@ -414,7 +396,7 @@ timer_gettime_internal( thr_timer_t* timer, struct timespec diff; diff = timer->expires; - if (!timespec_is_zero(&diff)) + if (!timespec_is_zero(&diff)) { struct timespec now; @@ -532,108 +514,93 @@ timer_getoverrun(timer_t id) } -static void* -timer_thread_start( void* _arg ) -{ - thr_timer_t* timer = _arg; +static void* timer_thread_start(void* arg) { + thr_timer_t* timer = arg; - thr_timer_lock( timer ); + thr_timer_lock(timer); - /* we loop until timer->done is set in timer_delete() */ - while (!timer->done) - { - struct timespec expires = timer->expires; - struct timespec period = timer->period; - struct timespec now; + // Give this thread a meaningful name. + char name[32]; + snprintf(name, sizeof(name), "POSIX interval timer 0x%08x", timer->id); + pthread_setname_np(pthread_self(), name); - /* if the timer is stopped or disarmed, wait indefinitely - * for a state change from timer_settime/_delete/_start_stop - */ - if ( timer->stopped || timespec_is_zero(&expires) ) - { - pthread_cond_wait( &timer->cond, &timer->mutex ); - continue; - } + // We loop until timer->done is set in timer_delete(). + while (!timer->done) { + struct timespec expires = timer->expires; + struct timespec period = timer->period; - /* otherwise, we need to do a timed wait until either a - * state change of the timer expiration time. - */ - clock_gettime(timer->clock, &now); - - if (timespec_cmp( &expires, &now ) > 0) - { - /* cool, there was no overrun, so compute the - * relative timeout as 'expires - now', then wait - */ - int ret; - struct timespec diff = expires; - timespec_sub( &diff, &now ); - - ret = __pthread_cond_timedwait_relative( - &timer->cond, &timer->mutex, &diff); - - /* if we didn't timeout, it means that a state change - * occured, so reloop to take care of it. - */ - if (ret != ETIMEDOUT) - continue; - } - else - { - /* overrun was detected before we could wait ! */ - if (!timespec_is_zero( &period ) ) - { - /* for periodic timers, compute total overrun count */ - do { - timespec_add( &expires, &period ); - if (timer->overruns < DELAYTIMER_MAX) - timer->overruns += 1; - } while ( timespec_cmp( &expires, &now ) < 0 ); - - /* backtrack the last one, because we're going to - * add the same value just a bit later */ - timespec_sub( &expires, &period ); - } - else - { - /* for non-periodic timer, things are simple */ - timer->overruns = 1; - } - } - - /* if we get there, a timeout was detected. - * first reload/disarm the timer has needed - */ - if ( !timespec_is_zero(&period) ) { - timespec_add( &expires, &period ); - } else { - timespec_zero( &expires ); - } - timer->expires = expires; - - /* now call the timer callback function. release the - * lock to allow the function to modify the timer setting - * or call timer_getoverrun(). - * - * NOTE: at this point we trust the callback not to be a - * total moron and pthread_kill() the timer thread - */ - thr_timer_unlock(timer); - timer->callback( timer->value ); - thr_timer_lock(timer); - - /* now clear the overruns counter. it only makes sense - * within the callback */ - timer->overruns = 0; + // If the timer is stopped or disarmed, wait indefinitely + // for a state change from timer_settime/_delete/_start_stop. + if (timer->stopped || timespec_is_zero(&expires)) { + pthread_cond_wait(&timer->cond, &timer->mutex); + continue; } - thr_timer_unlock( timer ); + // Otherwise, we need to do a timed wait until either a + // state change of the timer expiration time. + struct timespec now; + clock_gettime(timer->clock, &now); - /* free the timer object now. there is no need to call - * __timer_table_get() since we're guaranteed that __timer_table - * is initialized in this thread - */ - thr_timer_table_free(__timer_table, timer); + if (timespec_cmp(&expires, &now) > 0) { + // Cool, there was no overrun, so compute the + // relative timeout as 'expires - now', then wait. + struct timespec diff = expires; + timespec_sub(&diff, &now); - return NULL; + int ret = __pthread_cond_timedwait_relative(&timer->cond, &timer->mutex, &diff); + + // If we didn't time out, it means that a state change + // occurred, so loop to take care of it. + if (ret != ETIMEDOUT) { + continue; + } + } else { + // Overrun was detected before we could wait! + if (!timespec_is_zero(&period)) { + // For periodic timers, compute total overrun count. + do { + timespec_add(&expires, &period); + if (timer->overruns < DELAYTIMER_MAX) { + timer->overruns += 1; + } + } while (timespec_cmp(&expires, &now) < 0); + + // Backtrack the last one, because we're going to + // add the same value just a bit later. + timespec_sub(&expires, &period); + } else { + // For non-periodic timers, things are simple. + timer->overruns = 1; + } + } + + // If we get here, a timeout was detected. + // First reload/disarm the timer as needed. + if (!timespec_is_zero(&period)) { + timespec_add(&expires, &period); + } else { + timespec_zero(&expires); + } + timer->expires = expires; + + // Now call the timer callback function. Release the + // lock to allow the function to modify the timer setting + // or call timer_getoverrun(). + // NOTE: at this point we trust the callback not to be a + // total moron and pthread_kill() the timer thread + thr_timer_unlock(timer); + timer->callback(timer->value); + thr_timer_lock(timer); + + // Now clear the overruns counter. it only makes sense + // within the callback. + timer->overruns = 0; + } + + thr_timer_unlock(timer); + + // Free the timer object. + thr_timer_table_free(__timer_table_get(), timer); + + return NULL; }