• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright (C) 2008 The Android Open Source Project
3   * All rights reserved.
4   *
5   * Redistribution and use in source and binary forms, with or without
6   * modification, are permitted provided that the following conditions
7   * are met:
8   *  * Redistributions of source code must retain the above copyright
9   *    notice, this list of conditions and the following disclaimer.
10   *  * Redistributions in binary form must reproduce the above copyright
11   *    notice, this list of conditions and the following disclaimer in
12   *    the documentation and/or other materials provided with the
13   *    distribution.
14   *
15   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19   * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22   * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23   * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26   * SUCH DAMAGE.
27   */
28  #include "pthread_internal.h"
29  #include <linux/time.h>
30  #include <string.h>
31  #include <errno.h>
32  
33  /* This file implements the support required to implement SIGEV_THREAD posix
34   * timers. See the following pages for additionnal details:
35   *
36   * www.opengroup.org/onlinepubs/000095399/functions/timer_create.html
37   * www.opengroup.org/onlinepubs/000095399/functions/timer_settime.html
38   * www.opengroup.org/onlinepubs/000095399/functions/xsh_chap02_04.html#tag_02_04_01
39   *
40   * The Linux kernel doesn't support these, so we need to implement them in the
41   * C library. We use a very basic scheme where each timer is associated to a
42   * thread that will loop, waiting for timeouts or messages from the program
43   * corresponding to calls to timer_settime() and timer_delete().
44   *
45   * Note also an important thing: Posix mandates that in the case of fork(),
46   * the timers of the child process should be disarmed, but not deleted.
47   * this is implemented by providing a fork() wrapper (see bionic/fork.c) which
48   * stops all timers before the fork, and only re-start them in case of error
49   * or in the parent process.
50   *
51   * the stop/start is implemented by the __timer_table_start_stop() function
52   * below.
53   */
54  
55  /* normal (i.e. non-SIGEV_THREAD) timer ids are created directly by the kernel
56   * and are passed as is to/from the caller.
57   *
58   * on the other hand, a SIGEV_THREAD timer ID will have its TIMER_ID_WRAP_BIT
59   * always set to 1. In this implementation, this is always bit 31, which is
60   * guaranteed to never be used by kernel-provided timer ids
61   *
62   * (see code in <kernel>/lib/idr.c, used to manage IDs, to see why)
63   */
64  
65  #define  TIMER_ID_WRAP_BIT        0x80000000
66  #define  TIMER_ID_WRAP(id)        ((timer_t)((id) |  TIMER_ID_WRAP_BIT))
67  #define  TIMER_ID_UNWRAP(id)      ((timer_t)((id) & ~TIMER_ID_WRAP_BIT))
68  #define  TIMER_ID_IS_WRAPPED(id)  (((id) & TIMER_ID_WRAP_BIT) != 0)
69  
70  /* this value is used internally to indicate a 'free' or 'zombie'
71   * thr_timer structure. Here, 'zombie' means that timer_delete()
72   * has been called, but that the corresponding thread hasn't
73   * exited yet.
74   */
75  #define  TIMER_ID_NONE            ((timer_t)0xffffffff)
76  
77  /* True iff a timer id is valid */
78  #define  TIMER_ID_IS_VALID(id)    ((id) != TIMER_ID_NONE)
79  
80  /* the maximum value of overrun counters */
81  #define  DELAYTIMER_MAX    0x7fffffff
82  
83  #define  __likely(x)   __builtin_expect(!!(x),1)
84  #define  __unlikely(x) __builtin_expect(!!(x),0)
85  
86  typedef struct thr_timer          thr_timer_t;
87  typedef struct thr_timer_table    thr_timer_table_t;
88  
89  /* The Posix spec says the function receives an unsigned parameter, but
90   * it's really a 'union sigval' a.k.a. sigval_t */
91  typedef void (*thr_timer_func_t)( sigval_t );
92  
93  struct thr_timer {
94      thr_timer_t*       next;     /* next in free list */
95      timer_t            id;       /* TIMER_ID_NONE iff free or dying */
96      clockid_t          clock;
97      pthread_t          thread;
98      pthread_attr_t     attributes;
99      thr_timer_func_t   callback;
100      sigval_t           value;
101  
102      /* the following are used to communicate between
103       * the timer thread and the timer_XXX() functions
104       */
105      pthread_mutex_t           mutex;     /* lock */
106      pthread_cond_t            cond;      /* signal a state change to thread */
107      int volatile              done;      /* set by timer_delete */
108      int volatile              stopped;   /* set by _start_stop() */
109      struct timespec volatile  expires;   /* next expiration time, or 0 */
110      struct timespec volatile  period;    /* reload value, or 0 */
111      int volatile              overruns;  /* current number of overruns */
112  };
113  
114  #define  MAX_THREAD_TIMERS  32
115  
116  struct thr_timer_table {
117      pthread_mutex_t  lock;
118      thr_timer_t*     free_timer;
119      thr_timer_t      timers[ MAX_THREAD_TIMERS ];
120  };
121  
122  /** GLOBAL TABLE OF THREAD TIMERS
123   **/
124  
125  static void
thr_timer_table_init(thr_timer_table_t * t)126  thr_timer_table_init( thr_timer_table_t*  t )
127  {
128      int  nn;
129  
130      memset(t, 0, sizeof *t);
131      pthread_mutex_init( &t->lock, NULL );
132  
133      for (nn = 0; nn < MAX_THREAD_TIMERS; nn++)
134          t->timers[nn].id = TIMER_ID_NONE;
135  
136      t->free_timer = &t->timers[0];
137      for (nn = 1; nn < MAX_THREAD_TIMERS; nn++)
138          t->timers[nn-1].next = &t->timers[nn];
139  }
140  
141  
142  static thr_timer_t*
thr_timer_table_alloc(thr_timer_table_t * t)143  thr_timer_table_alloc( thr_timer_table_t*  t )
144  {
145      thr_timer_t*  timer;
146  
147      if (t == NULL)
148          return NULL;
149  
150      pthread_mutex_lock(&t->lock);
151      timer = t->free_timer;
152      if (timer != NULL) {
153          t->free_timer = timer->next;
154          timer->next   = NULL;
155          timer->id     = TIMER_ID_WRAP((timer - t->timers));
156      }
157      pthread_mutex_unlock(&t->lock);
158      return timer;
159  }
160  
161  
162  static void
thr_timer_table_free(thr_timer_table_t * t,thr_timer_t * timer)163  thr_timer_table_free( thr_timer_table_t*  t, thr_timer_t*  timer )
164  {
165      pthread_mutex_lock( &t->lock );
166      timer->id     = TIMER_ID_NONE;
167      timer->thread = 0;
168      timer->next   = t->free_timer;
169      t->free_timer = timer;
170      pthread_mutex_unlock( &t->lock );
171  }
172  
173  
174  static void
thr_timer_table_start_stop(thr_timer_table_t * t,int stop)175  thr_timer_table_start_stop( thr_timer_table_t*  t, int  stop )
176  {
177      int  nn;
178  
179      pthread_mutex_lock(&t->lock);
180  
181      for (nn = 0; nn < MAX_THREAD_TIMERS; nn++) {
182          thr_timer_t*  timer  = &t->timers[nn];
183  
184          if (TIMER_ID_IS_VALID(timer->id)) {
185              /* tell the thread to start/stop */
186              pthread_mutex_lock(&timer->mutex);
187              timer->stopped = stop;
188              pthread_cond_signal( &timer->cond );
189              pthread_mutex_unlock(&timer->mutex);
190          }
191      }
192      pthread_mutex_unlock(&t->lock);
193  }
194  
195  
196  /* convert a timer_id into the corresponding thr_timer_t* pointer
197   * returns NULL if the id is not wrapped or is invalid/free
198   */
199  static thr_timer_t*
thr_timer_table_from_id(thr_timer_table_t * t,timer_t id,int remove)200  thr_timer_table_from_id( thr_timer_table_t*  t,
201                           timer_t             id,
202                           int                 remove )
203  {
204      unsigned      index;
205      thr_timer_t*  timer;
206  
207      if (t == NULL || !TIMER_ID_IS_WRAPPED(id))
208          return NULL;
209  
210      index = (unsigned) TIMER_ID_UNWRAP(id);
211      if (index >= MAX_THREAD_TIMERS)
212          return NULL;
213  
214      pthread_mutex_lock(&t->lock);
215  
216      timer = &t->timers[index];
217  
218      if (!TIMER_ID_IS_VALID(timer->id)) {
219          timer = NULL;
220      } else {
221          /* if we're removing this timer, clear the id
222           * right now to prevent another thread to
223           * use the same id after the unlock */
224          if (remove)
225              timer->id = TIMER_ID_NONE;
226      }
227      pthread_mutex_unlock(&t->lock);
228  
229      return timer;
230  }
231  
232  /* the static timer table - we only create it if the process
233   * really wants to use SIGEV_THREAD timers, which should be
234   * pretty infrequent
235   */
236  
237  static pthread_once_t      __timer_table_once = PTHREAD_ONCE_INIT;
238  static thr_timer_table_t*  __timer_table;
239  
240  static void
__timer_table_init(void)241  __timer_table_init( void )
242  {
243      __timer_table = calloc(1,sizeof(*__timer_table));
244  
245      if (__timer_table != NULL)
246          thr_timer_table_init( __timer_table );
247  }
248  
249  static thr_timer_table_t*
__timer_table_get(void)250  __timer_table_get(void)
251  {
252      pthread_once( &__timer_table_once, __timer_table_init );
253      return __timer_table;
254  }
255  
256  /** POSIX THREAD TIMERS CLEANUP ON FORK
257   **
258   ** this should be called from the 'fork()' wrapper to stop/start
259   ** all active thread timers. this is used to implement a Posix
260   ** requirements: the timers of fork child processes must be
261   ** disarmed but not deleted.
262   **/
263  __LIBC_HIDDEN__ void
__timer_table_start_stop(int stop)264  __timer_table_start_stop( int  stop )
265  {
266      if (__timer_table != NULL) {
267          thr_timer_table_t*  table = __timer_table_get();
268          thr_timer_table_start_stop(table, stop);
269      }
270  }
271  
272  static thr_timer_t*
thr_timer_from_id(timer_t id)273  thr_timer_from_id( timer_t   id )
274  {
275      thr_timer_table_t*  table = __timer_table_get();
276      thr_timer_t*        timer = thr_timer_table_from_id( table, id, 0 );
277  
278      return timer;
279  }
280  
281  
282  static __inline__ void
thr_timer_lock(thr_timer_t * t)283  thr_timer_lock( thr_timer_t*  t )
284  {
285      pthread_mutex_lock(&t->mutex);
286  }
287  
288  static __inline__ void
thr_timer_unlock(thr_timer_t * t)289  thr_timer_unlock( thr_timer_t*  t )
290  {
291      pthread_mutex_unlock(&t->mutex);
292  }
293  
294  /** POSIX TIMERS APIs */
295  
296  /* first, declare the syscall stubs */
297  extern int __timer_create( clockid_t, struct sigevent*, timer_t* );
298  extern int __timer_delete( timer_t );
299  extern int __timer_gettime( timer_t, struct itimerspec* );
300  extern int __timer_settime( timer_t, int, const struct itimerspec*, struct itimerspec* );
301  extern int __timer_getoverrun(timer_t);
302  
303  static void*  timer_thread_start( void* );
304  
305  /* then the wrappers themselves */
306  int
timer_create(clockid_t clockid,struct sigevent * evp,timer_t * ptimerid)307  timer_create( clockid_t  clockid, struct sigevent*  evp, timer_t  *ptimerid)
308  {
309      /* if not a SIGEV_THREAD timer, direct creation by the kernel */
310      if (__likely(evp == NULL || evp->sigev_notify != SIGEV_THREAD))
311          return __timer_create( clockid, evp, ptimerid );
312  
313      // check arguments
314      if (evp->sigev_notify_function == NULL) {
315          errno = EINVAL;
316          return -1;
317      }
318  
319      {
320          struct timespec  dummy;
321  
322          /* check that the clock id is supported by the kernel */
323          if (clock_gettime( clockid, &dummy ) < 0 && errno == EINVAL )
324              return -1;
325      }
326  
327      /* create a new timer and its thread */
328      {
329          thr_timer_table_t*  table = __timer_table_get();
330          thr_timer_t*        timer = thr_timer_table_alloc( table );
331          struct sigevent     evp0;
332  
333          if (timer == NULL) {
334              errno = ENOMEM;
335              return -1;
336          }
337  
338          /* copy the thread attributes */
339          if (evp->sigev_notify_attributes == NULL) {
340              pthread_attr_init(&timer->attributes);
341          }
342          else {
343              timer->attributes = ((pthread_attr_t*)evp->sigev_notify_attributes)[0];
344          }
345  
346          /* Posix says that the default is PTHREAD_CREATE_DETACHED and
347           * that PTHREAD_CREATE_JOINABLE has undefined behaviour.
348           * So simply always use DETACHED :-)
349           */
350          pthread_attr_setdetachstate(&timer->attributes, PTHREAD_CREATE_DETACHED);
351  
352          timer->callback = evp->sigev_notify_function;
353          timer->value    = evp->sigev_value;
354          timer->clock    = clockid;
355  
356          pthread_mutex_init( &timer->mutex, NULL );
357          pthread_cond_init( &timer->cond, NULL );
358  
359          timer->done           = 0;
360          timer->stopped        = 0;
361          timer->expires.tv_sec = timer->expires.tv_nsec = 0;
362          timer->period.tv_sec  = timer->period.tv_nsec  = 0;
363          timer->overruns       = 0;
364  
365          /* create the thread */
366          if (pthread_create( &timer->thread, &timer->attributes, timer_thread_start, timer ) < 0) {
367              thr_timer_table_free( __timer_table, timer );
368              errno = ENOMEM;
369              return -1;
370          }
371  
372          *ptimerid = timer->id;
373          return 0;
374      }
375  }
376  
377  
378  int
timer_delete(timer_t id)379  timer_delete( timer_t  id )
380  {
381      if ( __likely(!TIMER_ID_IS_WRAPPED(id)) )
382          return __timer_delete( id );
383      else
384      {
385          thr_timer_table_t*  table = __timer_table_get();
386          thr_timer_t*        timer = thr_timer_table_from_id(table, id, 1);
387  
388          if (timer == NULL) {
389              errno = EINVAL;
390              return -1;
391          }
392  
393          /* tell the timer's thread to stop */
394          thr_timer_lock(timer);
395          timer->done = 1;
396          pthread_cond_signal( &timer->cond );
397          thr_timer_unlock(timer);
398  
399          /* NOTE: the thread will call __timer_table_free() to free the
400           * timer object. the '1' parameter to thr_timer_table_from_id
401           * above ensured that the object and its timer_id cannot be
402           * reused before that.
403           */
404          return 0;
405      }
406  }
407  
408  /* return the relative time until the next expiration, or 0 if
409   * the timer is disarmed */
410  static void
timer_gettime_internal(thr_timer_t * timer,struct itimerspec * spec)411  timer_gettime_internal( thr_timer_t*        timer,
412                          struct itimerspec*  spec)
413  {
414      struct timespec  diff;
415  
416      diff = timer->expires;
417      if (!timespec_is_zero(&diff))
418      {
419          struct timespec  now;
420  
421          clock_gettime( timer->clock, &now );
422          timespec_sub(&diff, &now);
423  
424          /* in case of overrun, return 0 */
425          if (timespec_cmp0(&diff) < 0) {
426              timespec_zero(&diff);
427          }
428      }
429  
430      spec->it_value    = diff;
431      spec->it_interval = timer->period;
432  }
433  
434  
435  int
timer_gettime(timer_t id,struct itimerspec * ospec)436  timer_gettime( timer_t  id, struct itimerspec*  ospec )
437  {
438      if (ospec == NULL) {
439          errno = EINVAL;
440          return -1;
441      }
442  
443      if ( __likely(!TIMER_ID_IS_WRAPPED(id)) ) {
444          return __timer_gettime( id, ospec );
445      } else {
446          thr_timer_t*  timer = thr_timer_from_id(id);
447  
448          if (timer == NULL) {
449              errno = EINVAL;
450              return -1;
451          }
452          thr_timer_lock(timer);
453          timer_gettime_internal( timer, ospec );
454          thr_timer_unlock(timer);
455      }
456      return 0;
457  }
458  
459  
460  int
timer_settime(timer_t id,int flags,const struct itimerspec * spec,struct itimerspec * ospec)461  timer_settime( timer_t                   id,
462                 int                       flags,
463                 const struct itimerspec*  spec,
464                 struct itimerspec*        ospec )
465  {
466      if (spec == NULL) {
467          errno = EINVAL;
468          return -1;
469      }
470  
471      if ( __likely(!TIMER_ID_IS_WRAPPED(id)) ) {
472          return __timer_settime( id, flags, spec, ospec );
473      } else {
474          thr_timer_t*        timer = thr_timer_from_id(id);
475          struct timespec     expires, now;
476  
477          if (timer == NULL) {
478              errno = EINVAL;
479              return -1;
480          }
481          thr_timer_lock(timer);
482  
483          /* return current timer value if ospec isn't NULL */
484          if (ospec != NULL) {
485              timer_gettime_internal(timer, ospec );
486          }
487  
488          /* compute next expiration time. note that if the
489           * new it_interval is 0, we should disarm the timer
490           */
491          expires = spec->it_value;
492          if (!timespec_is_zero(&expires)) {
493              clock_gettime( timer->clock, &now );
494              if (!(flags & TIMER_ABSTIME)) {
495                  timespec_add(&expires, &now);
496              } else {
497                  if (timespec_cmp(&expires, &now) < 0)
498                      expires = now;
499              }
500          }
501          timer->expires = expires;
502          timer->period  = spec->it_interval;
503          thr_timer_unlock( timer );
504  
505          /* signal the change to the thread */
506          pthread_cond_signal( &timer->cond );
507      }
508      return 0;
509  }
510  
511  
512  int
timer_getoverrun(timer_t id)513  timer_getoverrun(timer_t  id)
514  {
515      if ( __likely(!TIMER_ID_IS_WRAPPED(id)) ) {
516          return __timer_getoverrun( id );
517      } else {
518          thr_timer_t*  timer = thr_timer_from_id(id);
519          int           result;
520  
521          if (timer == NULL) {
522              errno = EINVAL;
523              return -1;
524          }
525  
526          thr_timer_lock(timer);
527          result = timer->overruns;
528          thr_timer_unlock(timer);
529  
530          return result;
531      }
532  }
533  
534  
535  static void*
timer_thread_start(void * _arg)536  timer_thread_start( void*  _arg )
537  {
538      thr_timer_t*  timer = _arg;
539  
540      thr_timer_lock( timer );
541  
542      /* we loop until timer->done is set in timer_delete() */
543      while (!timer->done)
544      {
545          struct timespec   expires = timer->expires;
546          struct timespec   period  = timer->period;
547          struct timespec   now;
548  
549          /* if the timer is stopped or disarmed, wait indefinitely
550           * for a state change from timer_settime/_delete/_start_stop
551           */
552          if ( timer->stopped || timespec_is_zero(&expires) )
553          {
554              pthread_cond_wait( &timer->cond, &timer->mutex );
555              continue;
556          }
557  
558          /* otherwise, we need to do a timed wait until either a
559          * state change of the timer expiration time.
560          */
561          clock_gettime(timer->clock, &now);
562  
563          if (timespec_cmp( &expires, &now ) > 0)
564          {
565              /* cool, there was no overrun, so compute the
566               * relative timeout as 'expires - now', then wait
567               */
568              int              ret;
569              struct timespec  diff = expires;
570              timespec_sub( &diff, &now );
571  
572              ret = __pthread_cond_timedwait_relative(
573                          &timer->cond, &timer->mutex, &diff);
574  
575              /* if we didn't timeout, it means that a state change
576                  * occured, so reloop to take care of it.
577                  */
578              if (ret != ETIMEDOUT)
579                  continue;
580          }
581          else
582          {
583              /* overrun was detected before we could wait ! */
584              if (!timespec_is_zero( &period ) )
585              {
586                  /* for periodic timers, compute total overrun count */
587                  do {
588                      timespec_add( &expires, &period );
589                      if (timer->overruns < DELAYTIMER_MAX)
590                          timer->overruns += 1;
591                  } while ( timespec_cmp( &expires, &now ) < 0 );
592  
593                  /* backtrack the last one, because we're going to
594                   * add the same value just a bit later */
595                  timespec_sub( &expires, &period );
596              }
597              else
598              {
599                  /* for non-periodic timer, things are simple */
600                  timer->overruns = 1;
601              }
602          }
603  
604          /* if we get there, a timeout was detected.
605           * first reload/disarm the timer has needed
606           */
607          if ( !timespec_is_zero(&period) ) {
608              timespec_add( &expires, &period );
609          } else {
610              timespec_zero( &expires );
611          }
612          timer->expires = expires;
613  
614          /* now call the timer callback function. release the
615           * lock to allow the function to modify the timer setting
616           * or call timer_getoverrun().
617           *
618           * NOTE: at this point we trust the callback not to be a
619           *       total moron and pthread_kill() the timer thread
620           */
621          thr_timer_unlock(timer);
622          timer->callback( timer->value );
623          thr_timer_lock(timer);
624  
625          /* now clear the overruns counter. it only makes sense
626           * within the callback */
627          timer->overruns = 0;
628      }
629  
630      thr_timer_unlock( timer );
631  
632      /* free the timer object now. there is no need to call
633       * __timer_table_get() since we're guaranteed that __timer_table
634       * is initialized in this thread
635       */
636      thr_timer_table_free(__timer_table, timer);
637  
638      return NULL;
639  }
640