11dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/*
21dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
31dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * All rights reserved.
41dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project *
51dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * Redistribution and use in source and binary forms, with or without
61dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * modification, are permitted provided that the following conditions
71dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * are met:
81dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project *  * Redistributions of source code must retain the above copyright
91dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project *    notice, this list of conditions and the following disclaimer.
101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project *  * Redistributions in binary form must reproduce the above copyright
111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project *    notice, this list of conditions and the following disclaimer in
12470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes *    the documentation and/or other materials provided with the
131dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project *    distribution.
141dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project *
151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
161dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
171dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
181dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
191dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
201dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
231dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
241dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
251dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * SUCH DAMAGE.
271dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project */
28470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
291dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#include "pthread_internal.h"
30470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
31470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes#include <errno.h>
321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#include <linux/time.h>
33470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes#include <stdio.h>
341dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#include <string.h>
351dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
36470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// Normal (i.e. non-SIGEV_THREAD) timers are created directly by the kernel
37470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// and are passed as is to/from the caller.
38470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes//
39470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// This file also implements the support required for SIGEV_THREAD ("POSIX interval")
40470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// timers. See the following pages for additional details:
41470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes//
42470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// www.opengroup.org/onlinepubs/000095399/functions/timer_create.html
43470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// www.opengroup.org/onlinepubs/000095399/functions/timer_settime.html
44470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// www.opengroup.org/onlinepubs/000095399/functions/xsh_chap02_04.html#tag_02_04_01
45470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes//
46470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// The Linux kernel doesn't support these, so we need to implement them in the
47470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// C library. We use a very basic scheme where each timer is associated to a
48470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// thread that will loop, waiting for timeouts or messages from the program
49470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// corresponding to calls to timer_settime() and timer_delete().
50470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes//
51470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// Note also an important thing: Posix mandates that in the case of fork(),
52470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// the timers of the child process should be disarmed, but not deleted.
53470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// this is implemented by providing a fork() wrapper (see bionic/fork.c) which
54470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// stops all timers before the fork, and only re-start them in case of error
55470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// or in the parent process.
56470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes//
57470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// This stop/start is implemented by the __timer_table_start_stop() function
58470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// below.
59470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes//
60470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// A SIGEV_THREAD timer ID will always have its TIMER_ID_WRAP_BIT
61470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// set to 1. In this implementation, this is always bit 31, which is
62470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// guaranteed to never be used by kernel-provided timer ids
63470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes//
64470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes// (See code in <kernel>/lib/idr.c, used to manage IDs, to see why.)
651dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
661dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#define  TIMER_ID_WRAP_BIT        0x80000000
671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#define  TIMER_ID_WRAP(id)        ((timer_t)((id) |  TIMER_ID_WRAP_BIT))
681dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#define  TIMER_ID_UNWRAP(id)      ((timer_t)((id) & ~TIMER_ID_WRAP_BIT))
691dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#define  TIMER_ID_IS_WRAPPED(id)  (((id) & TIMER_ID_WRAP_BIT) != 0)
701dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
71470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes/* this value is used internally to indicate a 'free' or 'zombie'
721dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * thr_timer structure. Here, 'zombie' means that timer_delete()
731dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * has been called, but that the corresponding thread hasn't
741dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * exited yet.
751dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project */
761dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#define  TIMER_ID_NONE            ((timer_t)0xffffffff)
771dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
781dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/* True iff a timer id is valid */
791dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#define  TIMER_ID_IS_VALID(id)    ((id) != TIMER_ID_NONE)
801dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
811dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/* the maximum value of overrun counters */
821dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#define  DELAYTIMER_MAX    0x7fffffff
831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
841dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#define  __likely(x)   __builtin_expect(!!(x),1)
851dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#define  __unlikely(x) __builtin_expect(!!(x),0)
861dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
871dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projecttypedef struct thr_timer          thr_timer_t;
881dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projecttypedef struct thr_timer_table    thr_timer_table_t;
891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
901dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/* The Posix spec says the function receives an unsigned parameter, but
911dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * it's really a 'union sigval' a.k.a. sigval_t */
921dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projecttypedef void (*thr_timer_func_t)( sigval_t );
931dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectstruct thr_timer {
951dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    thr_timer_t*       next;     /* next in free list */
961dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    timer_t            id;       /* TIMER_ID_NONE iff free or dying */
971dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    clockid_t          clock;
981dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_t          thread;
991dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_attr_t     attributes;
1001dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    thr_timer_func_t   callback;
1011dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    sigval_t           value;
1021dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1031dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    /* the following are used to communicate between
1041dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project     * the timer thread and the timer_XXX() functions
1051dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project     */
1061dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_mutex_t           mutex;     /* lock */
1071dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_cond_t            cond;      /* signal a state change to thread */
1081dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    int volatile              done;      /* set by timer_delete */
1091dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    int volatile              stopped;   /* set by _start_stop() */
1101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    struct timespec volatile  expires;   /* next expiration time, or 0 */
1111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    struct timespec volatile  period;    /* reload value, or 0 */
1121dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    int volatile              overruns;  /* current number of overruns */
1131dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project};
1141dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#define  MAX_THREAD_TIMERS  32
1161dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1171dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectstruct thr_timer_table {
1181dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_mutex_t  lock;
1191dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    thr_timer_t*     free_timer;
1201dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    thr_timer_t      timers[ MAX_THREAD_TIMERS ];
1211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project};
1221dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1231dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/** GLOBAL TABLE OF THREAD TIMERS
1241dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project **/
1251dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectstatic void
1271dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectthr_timer_table_init( thr_timer_table_t*  t )
1281dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{
1291dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    int  nn;
1301dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1311dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    memset(t, 0, sizeof *t);
1321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_mutex_init( &t->lock, NULL );
1331dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1341dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    for (nn = 0; nn < MAX_THREAD_TIMERS; nn++)
1351dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        t->timers[nn].id = TIMER_ID_NONE;
1361dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1371dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    t->free_timer = &t->timers[0];
1381dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    for (nn = 1; nn < MAX_THREAD_TIMERS; nn++)
1391dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        t->timers[nn-1].next = &t->timers[nn];
1401dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
1411dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1421dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1431dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectstatic thr_timer_t*
1441dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectthr_timer_table_alloc( thr_timer_table_t*  t )
1451dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{
1461dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    thr_timer_t*  timer;
1471dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1481dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    if (t == NULL)
1491dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        return NULL;
1501dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1511dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_mutex_lock(&t->lock);
1521dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    timer = t->free_timer;
1531dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    if (timer != NULL) {
1541dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        t->free_timer = timer->next;
1551dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        timer->next   = NULL;
1561dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        timer->id     = TIMER_ID_WRAP((timer - t->timers));
1571dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    }
1581dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_mutex_unlock(&t->lock);
1591dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    return timer;
1601dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
1611dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1621dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1631dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectstatic void
1641dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectthr_timer_table_free( thr_timer_table_t*  t, thr_timer_t*  timer )
1651dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{
1661dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_mutex_lock( &t->lock );
1671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    timer->id     = TIMER_ID_NONE;
1681dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    timer->thread = 0;
1691dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    timer->next   = t->free_timer;
1701dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    t->free_timer = timer;
1711dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_mutex_unlock( &t->lock );
1721dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
1731dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1741dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
175470631ed79538ce912edb94505dee3e24af8db89Elliott Hughesstatic void thr_timer_table_start_stop(thr_timer_table_t* t, int stop) {
176470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  if (t == NULL) {
177470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    return;
178470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  }
179470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
180470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  pthread_mutex_lock(&t->lock);
181470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  for (int nn = 0; nn < MAX_THREAD_TIMERS; ++nn) {
182470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    thr_timer_t*  timer  = &t->timers[nn];
183470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    if (TIMER_ID_IS_VALID(timer->id)) {
184470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      // Tell the thread to start/stop.
185470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      pthread_mutex_lock(&timer->mutex);
186470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      timer->stopped = stop;
187470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      pthread_cond_signal( &timer->cond );
188470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      pthread_mutex_unlock(&timer->mutex);
1891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    }
190470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  }
191470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  pthread_mutex_unlock(&t->lock);
1921dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
1931dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
1951dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/* convert a timer_id into the corresponding thr_timer_t* pointer
1961dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * returns NULL if the id is not wrapped or is invalid/free
1971dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project */
1981dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectstatic thr_timer_t*
1991dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectthr_timer_table_from_id( thr_timer_table_t*  t,
2001dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project                         timer_t             id,
2011dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project                         int                 remove )
2021dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{
2031dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    unsigned      index;
2041dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    thr_timer_t*  timer;
2051dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2061dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    if (t == NULL || !TIMER_ID_IS_WRAPPED(id))
2071dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        return NULL;
2081dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2091dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    index = (unsigned) TIMER_ID_UNWRAP(id);
2101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    if (index >= MAX_THREAD_TIMERS)
2111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        return NULL;
2121dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2131dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_mutex_lock(&t->lock);
2141dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    timer = &t->timers[index];
2161dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2171dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    if (!TIMER_ID_IS_VALID(timer->id)) {
2181dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        timer = NULL;
2191dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    } else {
2201dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        /* if we're removing this timer, clear the id
2211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project         * right now to prevent another thread to
2221dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project         * use the same id after the unlock */
2231dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        if (remove)
2241dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            timer->id = TIMER_ID_NONE;
2251dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    }
2261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_mutex_unlock(&t->lock);
2271dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2281dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    return timer;
2291dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
2301dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2311dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/* the static timer table - we only create it if the process
2321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * really wants to use SIGEV_THREAD timers, which should be
2331dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * pretty infrequent
2341dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project */
2351dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
236470631ed79538ce912edb94505dee3e24af8db89Elliott Hughesstatic pthread_once_t __timer_table_once = PTHREAD_ONCE_INIT;
237470631ed79538ce912edb94505dee3e24af8db89Elliott Hughesstatic thr_timer_table_t* __timer_table;
2381dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
239470631ed79538ce912edb94505dee3e24af8db89Elliott Hughesstatic void __timer_table_init(void) {
240470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  __timer_table = calloc(1, sizeof(*__timer_table));
241470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  if (__timer_table != NULL) {
242470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    thr_timer_table_init(__timer_table);
243470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  }
2441dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
2451dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
246470631ed79538ce912edb94505dee3e24af8db89Elliott Hughesstatic thr_timer_table_t* __timer_table_get(void) {
247470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  pthread_once(&__timer_table_once, __timer_table_init);
248470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  return __timer_table;
2491dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
2501dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2511dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/** POSIX THREAD TIMERS CLEANUP ON FORK
2521dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project **
2531dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project ** this should be called from the 'fork()' wrapper to stop/start
2541dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project ** all active thread timers. this is used to implement a Posix
2551dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project ** requirements: the timers of fork child processes must be
2561dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project ** disarmed but not deleted.
2571dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project **/
258470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes__LIBC_HIDDEN__ void __timer_table_start_stop(int stop) {
259470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // We access __timer_table directly so we don't create it if it doesn't yet exist.
260470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  thr_timer_table_start_stop(__timer_table, stop);
2611dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
2621dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2631dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectstatic thr_timer_t*
2641dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectthr_timer_from_id( timer_t   id )
2651dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{
2661dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    thr_timer_table_t*  table = __timer_table_get();
2671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    thr_timer_t*        timer = thr_timer_table_from_id( table, id, 0 );
2681dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2691dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    return timer;
2701dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
2711dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2721dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2731dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectstatic __inline__ void
2741dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectthr_timer_lock( thr_timer_t*  t )
2751dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{
2761dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_mutex_lock(&t->mutex);
2771dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
2781dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2791dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectstatic __inline__ void
2801dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectthr_timer_unlock( thr_timer_t*  t )
2811dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{
2821dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    pthread_mutex_unlock(&t->mutex);
2831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
2841dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
2851dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/** POSIX TIMERS APIs */
2861dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
287470631ed79538ce912edb94505dee3e24af8db89Elliott Hughesextern int __timer_create(clockid_t, struct sigevent*, timer_t*);
288470631ed79538ce912edb94505dee3e24af8db89Elliott Hughesextern int __timer_delete(timer_t);
289470631ed79538ce912edb94505dee3e24af8db89Elliott Hughesextern int __timer_gettime(timer_t, struct itimerspec*);
290470631ed79538ce912edb94505dee3e24af8db89Elliott Hughesextern int __timer_settime(timer_t, int, const struct itimerspec*, struct itimerspec*);
2911dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectextern int __timer_getoverrun(timer_t);
2921dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
293470631ed79538ce912edb94505dee3e24af8db89Elliott Hughesstatic void* timer_thread_start(void*);
294470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
295470631ed79538ce912edb94505dee3e24af8db89Elliott Hughesint timer_create(clockid_t clock_id, struct sigevent* evp, timer_t* timer_id) {
296470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // If not a SIGEV_THREAD timer, the kernel can handle it without our help.
297470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  if (__likely(evp == NULL || evp->sigev_notify != SIGEV_THREAD)) {
298470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    return __timer_create(clock_id, evp, timer_id);
299470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  }
300470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
301470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // Check arguments.
302470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  if (evp->sigev_notify_function == NULL) {
303470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    errno = EINVAL;
304470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    return -1;
305470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  }
306470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
307470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // Check that the clock id is supported by the kernel.
308470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  struct timespec dummy;
309470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  if (clock_gettime(clock_id, &dummy) < 0 && errno == EINVAL) {
310470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    return -1;
311470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  }
312470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
313470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // Create a new timer and its thread.
314470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // TODO: use a single global thread for all timers.
315470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  thr_timer_table_t* table = __timer_table_get();
316470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  thr_timer_t* timer = thr_timer_table_alloc(table);
317470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  if (timer == NULL) {
318470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    errno = ENOMEM;
319470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    return -1;
320470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  }
321470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
322470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // Copy the thread attributes.
323470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  if (evp->sigev_notify_attributes == NULL) {
324470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    pthread_attr_init(&timer->attributes);
325470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  } else {
326470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    timer->attributes = ((pthread_attr_t*) evp->sigev_notify_attributes)[0];
327470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  }
328470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
329470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // Posix says that the default is PTHREAD_CREATE_DETACHED and
330470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // that PTHREAD_CREATE_JOINABLE has undefined behavior.
331470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // So simply always use DETACHED :-)
332470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  pthread_attr_setdetachstate(&timer->attributes, PTHREAD_CREATE_DETACHED);
333470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
334470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  timer->callback = evp->sigev_notify_function;
335470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  timer->value = evp->sigev_value;
336470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  timer->clock = clock_id;
337470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
338470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  pthread_mutex_init(&timer->mutex, NULL);
339470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  pthread_cond_init(&timer->cond, NULL);
340470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
341470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  timer->done = 0;
342470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  timer->stopped = 0;
343470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  timer->expires.tv_sec = timer->expires.tv_nsec = 0;
344470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  timer->period.tv_sec = timer->period.tv_nsec  = 0;
345470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  timer->overruns = 0;
346470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
347470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // Create the thread.
348470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  int rc = pthread_create(&timer->thread, &timer->attributes, timer_thread_start, timer);
349470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  if (rc != 0) {
350470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    thr_timer_table_free(table, timer);
351470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    errno = rc;
352470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    return -1;
353470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  }
354470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
355470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  *timer_id = timer->id;
356470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  return 0;
3571dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
3581dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
3591dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
3601dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint
3611dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projecttimer_delete( timer_t  id )
3621dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{
3631dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    if ( __likely(!TIMER_ID_IS_WRAPPED(id)) )
3641dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        return __timer_delete( id );
3651dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    else
3661dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    {
3671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        thr_timer_table_t*  table = __timer_table_get();
3681dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        thr_timer_t*        timer = thr_timer_table_from_id(table, id, 1);
3691dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
3701dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        if (timer == NULL) {
3711dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            errno = EINVAL;
3721dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            return -1;
3731dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        }
3741dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
3751dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        /* tell the timer's thread to stop */
3761dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        thr_timer_lock(timer);
3771dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        timer->done = 1;
3781dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        pthread_cond_signal( &timer->cond );
3791dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        thr_timer_unlock(timer);
3801dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
3811dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        /* NOTE: the thread will call __timer_table_free() to free the
3821dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project         * timer object. the '1' parameter to thr_timer_table_from_id
3831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project         * above ensured that the object and its timer_id cannot be
3841dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project         * reused before that.
3851dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project         */
3861dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        return 0;
3871dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    }
3881dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
3891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
3901dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/* return the relative time until the next expiration, or 0 if
3911dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * the timer is disarmed */
3921dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectstatic void
3931dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projecttimer_gettime_internal( thr_timer_t*        timer,
3941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project                        struct itimerspec*  spec)
3951dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{
3961dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    struct timespec  diff;
3971dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
3981dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    diff = timer->expires;
399470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    if (!timespec_is_zero(&diff))
4001dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    {
4011dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        struct timespec  now;
4021dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4031dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        clock_gettime( timer->clock, &now );
4041dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        timespec_sub(&diff, &now);
4051dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4061dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        /* in case of overrun, return 0 */
4071dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        if (timespec_cmp0(&diff) < 0) {
4081dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            timespec_zero(&diff);
4091dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        }
4101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    }
4111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4121dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    spec->it_value    = diff;
4131dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    spec->it_interval = timer->period;
4141dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
4151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4161dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4171dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint
4181dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projecttimer_gettime( timer_t  id, struct itimerspec*  ospec )
4191dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{
4201dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    if (ospec == NULL) {
4211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        errno = EINVAL;
4221dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        return -1;
4231dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    }
4241dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4251dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    if ( __likely(!TIMER_ID_IS_WRAPPED(id)) ) {
4261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        return __timer_gettime( id, ospec );
4271dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    } else {
4281dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        thr_timer_t*  timer = thr_timer_from_id(id);
4291dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4301dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        if (timer == NULL) {
4311dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            errno = EINVAL;
4321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            return -1;
4331dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        }
4341dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        thr_timer_lock(timer);
4351dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        timer_gettime_internal( timer, ospec );
4361dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        thr_timer_unlock(timer);
4371dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    }
4381dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    return 0;
4391dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
4401dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4411dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4421dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint
4431dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projecttimer_settime( timer_t                   id,
4441dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project               int                       flags,
4451dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project               const struct itimerspec*  spec,
4461dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project               struct itimerspec*        ospec )
4471dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{
4481dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    if (spec == NULL) {
4491dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        errno = EINVAL;
4501dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        return -1;
4511dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    }
4521dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4531dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    if ( __likely(!TIMER_ID_IS_WRAPPED(id)) ) {
4541dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        return __timer_settime( id, flags, spec, ospec );
4551dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    } else {
4561dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        thr_timer_t*        timer = thr_timer_from_id(id);
4571dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        struct timespec     expires, now;
4581dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4591dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        if (timer == NULL) {
4601dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            errno = EINVAL;
4611dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            return -1;
4621dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        }
4631dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        thr_timer_lock(timer);
4641dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4651dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        /* return current timer value if ospec isn't NULL */
4661dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        if (ospec != NULL) {
4671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            timer_gettime_internal(timer, ospec );
4681dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        }
4691dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4701dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        /* compute next expiration time. note that if the
4711dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project         * new it_interval is 0, we should disarm the timer
4721dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project         */
4731dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        expires = spec->it_value;
4741dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        if (!timespec_is_zero(&expires)) {
4751dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            clock_gettime( timer->clock, &now );
4761dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            if (!(flags & TIMER_ABSTIME)) {
4771dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project                timespec_add(&expires, &now);
4781dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            } else {
4791dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project                if (timespec_cmp(&expires, &now) < 0)
4801dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project                    expires = now;
4811dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            }
4821dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        }
4831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        timer->expires = expires;
4841dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        timer->period  = spec->it_interval;
4851dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        thr_timer_unlock( timer );
4861dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4871dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        /* signal the change to the thread */
4881dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        pthread_cond_signal( &timer->cond );
4891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    }
4901dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    return 0;
4911dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
4921dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4931dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
4941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint
4951dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projecttimer_getoverrun(timer_t  id)
4961dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{
4971dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    if ( __likely(!TIMER_ID_IS_WRAPPED(id)) ) {
4981dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        return __timer_getoverrun( id );
4991dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    } else {
5001dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        thr_timer_t*  timer = thr_timer_from_id(id);
5011dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        int           result;
5021dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
5031dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        if (timer == NULL) {
5041dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            errno = EINVAL;
5051dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project            return -1;
5061dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        }
5071dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
5081dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        thr_timer_lock(timer);
5091dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        result = timer->overruns;
5101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        thr_timer_unlock(timer);
5111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
5121dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project        return result;
5131dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    }
5141dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
5151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
5161dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
517470631ed79538ce912edb94505dee3e24af8db89Elliott Hughesstatic void* timer_thread_start(void* arg) {
518470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  thr_timer_t* timer = arg;
5191dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
520470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  thr_timer_lock(timer);
5211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
522470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // Give this thread a meaningful name.
523470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  char name[32];
524470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  snprintf(name, sizeof(name), "POSIX interval timer 0x%08x", timer->id);
525470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  pthread_setname_np(pthread_self(), name);
5261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
527470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // We loop until timer->done is set in timer_delete().
528470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  while (!timer->done) {
529470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    struct timespec expires = timer->expires;
530470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    struct timespec period = timer->period;
5311dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
532470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    // If the timer is stopped or disarmed, wait indefinitely
533470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    // for a state change from timer_settime/_delete/_start_stop.
534470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    if (timer->stopped || timespec_is_zero(&expires)) {
535470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      pthread_cond_wait(&timer->cond, &timer->mutex);
536470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      continue;
537470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    }
5381dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
539470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    // Otherwise, we need to do a timed wait until either a
540470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    // state change of the timer expiration time.
541470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    struct timespec now;
542470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    clock_gettime(timer->clock, &now);
5431dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
544470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    if (timespec_cmp(&expires, &now) > 0) {
545470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      // Cool, there was no overrun, so compute the
546470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      // relative timeout as 'expires - now', then wait.
547470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      struct timespec diff = expires;
548470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      timespec_sub(&diff, &now);
549470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
550470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      int ret = __pthread_cond_timedwait_relative(&timer->cond, &timer->mutex, &diff);
551470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
552470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      // If we didn't time out, it means that a state change
553470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      // occurred, so loop to take care of it.
554470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      if (ret != ETIMEDOUT) {
555470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes        continue;
556470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      }
557470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    } else {
558470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      // Overrun was detected before we could wait!
559470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      if (!timespec_is_zero(&period)) {
560470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes        // For periodic timers, compute total overrun count.
561470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes        do {
562470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes          timespec_add(&expires, &period);
563470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes          if (timer->overruns < DELAYTIMER_MAX) {
564470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes            timer->overruns += 1;
565470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes          }
566470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes        } while (timespec_cmp(&expires, &now) < 0);
567470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
568470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes        // Backtrack the last one, because we're going to
569470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes        // add the same value just a bit later.
570470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes        timespec_sub(&expires, &period);
571470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      } else {
572470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes        // For non-periodic timers, things are simple.
573470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes        timer->overruns = 1;
574470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      }
575470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    }
5761dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
577470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    // If we get here, a timeout was detected.
578470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    // First reload/disarm the timer as needed.
579470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    if (!timespec_is_zero(&period)) {
580470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      timespec_add(&expires, &period);
581470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    } else {
582470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes      timespec_zero(&expires);
5831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    }
584470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    timer->expires = expires;
5851dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
586470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    // Now call the timer callback function. Release the
587470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    // lock to allow the function to modify the timer setting
588470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    // or call timer_getoverrun().
589470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    // NOTE: at this point we trust the callback not to be a
590470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    //      total moron and pthread_kill() the timer thread
591470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    thr_timer_unlock(timer);
592470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    timer->callback(timer->value);
593470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    thr_timer_lock(timer);
5941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
595470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    // Now clear the overruns counter. it only makes sense
596470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    // within the callback.
597470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes    timer->overruns = 0;
598470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  }
599470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
600470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  thr_timer_unlock(timer);
601470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes
602470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  // Free the timer object.
603470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  thr_timer_table_free(__timer_table_get(), timer);
6041dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
605470631ed79538ce912edb94505dee3e24af8db89Elliott Hughes  return NULL;
6061dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
607