Back to index

glibc  2.9
timer_routines.c
Go to the documentation of this file.
00001 /* Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public License as
00007    published by the Free Software Foundation; either version 2.1 of the
00008    License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; see the file COPYING.LIB.  If not,
00017    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018    Boston, MA 02111-1307, USA.  */
00019 
00020 #include <errno.h>
00021 #include <setjmp.h>
00022 #include <signal.h>
00023 #include <stdbool.h>
00024 #include <sysdep.h>
00025 #include <kernel-features.h>
00026 #include <nptl/pthreadP.h>
00027 #include "kernel-posix-timers.h"
00028 
00029 
00030 /* List of active SIGEV_THREAD timers.  */
00031 struct timer *__active_timer_sigev_thread;
00032 /* Lock for the __active_timer_sigev_thread.  */
00033 pthread_mutex_t __active_timer_sigev_thread_lock = PTHREAD_MUTEX_INITIALIZER;
00034 
00035 
00036 struct thread_start_data
00037 {
00038   void (*thrfunc) (sigval_t);
00039   sigval_t sival;
00040 };
00041 
00042 
00043 #ifdef __NR_timer_create
00044 /* Helper thread to call the user-provided function.  */
00045 static void *
00046 timer_sigev_thread (void *arg)
00047 {
00048   /* The parent thread has all signals blocked.  This is a bit
00049      surprising for user code, although valid.  We unblock all
00050      signals.  */
00051   sigset_t ss;
00052   sigemptyset (&ss);
00053   INTERNAL_SYSCALL_DECL (err);
00054   INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_SETMASK, &ss, NULL, _NSIG / 8);
00055 
00056   struct thread_start_data *td = (struct thread_start_data *) arg;
00057 
00058   void (*thrfunc) (sigval_t) = td->thrfunc;
00059   sigval_t sival = td->sival;
00060 
00061   /* The TD object was allocated in timer_helper_thread.  */
00062   free (td);
00063 
00064   /* Call the user-provided function.  */
00065   thrfunc (sival);
00066 
00067   return NULL;
00068 }
00069 
00070 
00071 /* Helper function to support starting threads for SIGEV_THREAD.  */
00072 static void *
00073 timer_helper_thread (void *arg)
00074 {
00075   /* Wait for the SIGTIMER signal, allowing the setXid signal, and
00076      none else.  */
00077   sigset_t ss;
00078   sigemptyset (&ss);
00079   __sigaddset (&ss, SIGTIMER);
00080 
00081   /* Endless loop of waiting for signals.  The loop is only ended when
00082      the thread is canceled.  */
00083   while (1)
00084     {
00085       siginfo_t si;
00086 
00087       /* sigwaitinfo cannot be used here, since it deletes
00088         SIGCANCEL == SIGTIMER from the set.  */
00089 
00090       int oldtype = LIBC_CANCEL_ASYNC ();
00091 
00092       /* XXX The size argument hopefully will have to be changed to the
00093         real size of the user-level sigset_t.  */
00094       int result = INLINE_SYSCALL (rt_sigtimedwait, 4, &ss, &si, NULL,
00095                                _NSIG / 8);
00096 
00097       LIBC_CANCEL_RESET (oldtype);
00098 
00099       if (result > 0)
00100        {
00101          if (si.si_code == SI_TIMER)
00102            {
00103              struct timer *tk = (struct timer *) si.si_ptr;
00104 
00105              /* Check the timer is still used and will not go away
00106                while we are reading the values here.  */
00107              pthread_mutex_lock (&__active_timer_sigev_thread_lock);
00108 
00109              struct timer *runp = __active_timer_sigev_thread;
00110              while (runp != NULL)
00111               if (runp == tk)
00112                 break;
00113               else
00114                 runp = runp->next;
00115 
00116              if (runp != NULL)
00117               {
00118                 struct thread_start_data *td = malloc (sizeof (*td));
00119 
00120                 /* There is not much we can do if the allocation fails.  */
00121                 if (td != NULL)
00122                   {
00123                     /* This is the signal we are waiting for.  */
00124                     td->thrfunc = tk->thrfunc;
00125                     td->sival = tk->sival;
00126 
00127                     pthread_t th;
00128                     (void) pthread_create (&th, &tk->attr,
00129                                         timer_sigev_thread, td);
00130                   }
00131               }
00132 
00133              pthread_mutex_unlock (&__active_timer_sigev_thread_lock);
00134            }
00135          else if (si.si_code == SI_TKILL)
00136            /* The thread is canceled.  */
00137            pthread_exit (NULL);
00138        }
00139     }
00140 }
00141 
00142 
00143 /* Control variable for helper thread creation.  */
00144 pthread_once_t __helper_once attribute_hidden;
00145 
00146 
00147 /* TID of the helper thread.  */
00148 pid_t __helper_tid attribute_hidden;
00149 
00150 
00151 /* Reset variables so that after a fork a new helper thread gets started.  */
00152 static void
00153 reset_helper_control (void)
00154 {
00155   __helper_once = PTHREAD_ONCE_INIT;
00156   __helper_tid = 0;
00157 }
00158 
00159 
00160 void
00161 attribute_hidden
00162 __start_helper_thread (void)
00163 {
00164   /* The helper thread needs only very little resources
00165      and should go away automatically when canceled.  */
00166   pthread_attr_t attr;
00167   (void) pthread_attr_init (&attr);
00168   (void) pthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN);
00169 
00170   /* Block all signals in the helper thread but SIGSETXID.  To do this
00171      thoroughly we temporarily have to block all signals here.  The
00172      helper can lose wakeups if SIGCANCEL is not blocked throughout,
00173      but sigfillset omits it SIGSETXID.  So, we add SIGCANCEL back
00174      explicitly here.  */
00175   sigset_t ss;
00176   sigset_t oss;
00177   sigfillset (&ss);
00178   __sigaddset (&ss, SIGCANCEL);
00179   INTERNAL_SYSCALL_DECL (err);
00180   INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_SETMASK, &ss, &oss, _NSIG / 8);
00181 
00182   /* Create the helper thread for this timer.  */
00183   pthread_t th;
00184   int res = pthread_create (&th, &attr, timer_helper_thread, NULL);
00185   if (res == 0)
00186     /* We managed to start the helper thread.  */
00187     __helper_tid = ((struct pthread *) th)->tid;
00188 
00189   /* Restore the signal mask.  */
00190   INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_SETMASK, &oss, NULL,
00191                   _NSIG / 8);
00192 
00193   /* No need for the attribute anymore.  */
00194   (void) pthread_attr_destroy (&attr);
00195 
00196   /* We have to make sure that after fork()ing a new helper thread can
00197      be created.  */
00198   pthread_atfork (NULL, NULL, reset_helper_control);
00199 }
00200 #endif
00201 
00202 #ifndef __ASSUME_POSIX_TIMERS
00203 # include <nptl/sysdeps/pthread/timer_routines.c>
00204 #endif