• 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