Back to index

glibc  2.9
timer_create.c
Go to the documentation of this file.
00001 /* Copyright (C) 2003,2004, 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 <pthread.h>
00022 #include <signal.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <time.h>
00026 #include <sysdep.h>
00027 #include <kernel-features.h>
00028 #include <internaltypes.h>
00029 #include <nptl/pthreadP.h>
00030 #include "kernel-posix-timers.h"
00031 #include "kernel-posix-cpu-timers.h"
00032 
00033 
00034 #ifdef __NR_timer_create
00035 # ifndef __ASSUME_POSIX_TIMERS
00036 static int compat_timer_create (clockid_t clock_id, struct sigevent *evp,
00037                             timer_t *timerid);
00038 #  define timer_create static compat_timer_create
00039 #  include <nptl/sysdeps/pthread/timer_create.c>
00040 #  undef timer_create
00041 
00042 /* Nonzero if the system calls are not available.  */
00043 int __no_posix_timers attribute_hidden;
00044 # endif
00045 
00046 # ifdef timer_create_alias
00047 #  define timer_create timer_create_alias
00048 # endif
00049 
00050 
00051 int
00052 timer_create (clock_id, evp, timerid)
00053      clockid_t clock_id;
00054      struct sigevent *evp;
00055      timer_t *timerid;
00056 {
00057 # undef timer_create
00058 # ifndef __ASSUME_POSIX_TIMERS
00059   if  (__no_posix_timers >= 0)
00060 # endif
00061     {
00062       clockid_t syscall_clockid = (clock_id == CLOCK_PROCESS_CPUTIME_ID
00063                                ? MAKE_PROCESS_CPUCLOCK (0, CPUCLOCK_SCHED)
00064                                : clock_id == CLOCK_THREAD_CPUTIME_ID
00065                                ? MAKE_THREAD_CPUCLOCK (0, CPUCLOCK_SCHED)
00066                                : clock_id);
00067 
00068       /* If the user wants notification via a thread we need to handle
00069         this special.  */
00070       if (evp == NULL
00071          || __builtin_expect (evp->sigev_notify != SIGEV_THREAD, 1))
00072        {
00073          struct sigevent local_evp;
00074 
00075          /* We avoid allocating too much memory by basically
00076             using struct timer as a derived class with the
00077             first two elements being in the superclass.  We only
00078             need these two elements here.  */
00079          struct timer *newp = (struct timer *) malloc (offsetof (struct timer,
00080                                                           thrfunc));
00081          if (newp == NULL)
00082            /* No more memory.  */
00083            return -1;
00084 
00085          if (evp == NULL)
00086            {
00087              /* The kernel has to pass up the timer ID which is a
00088                userlevel object.  Therefore we cannot leave it up to
00089                the kernel to determine it.  */
00090              local_evp.sigev_notify = SIGEV_SIGNAL;
00091              local_evp.sigev_signo = SIGALRM;
00092              local_evp.sigev_value.sival_ptr = newp;
00093 
00094              evp = &local_evp;
00095            }
00096 
00097          kernel_timer_t ktimerid;
00098          int retval = INLINE_SYSCALL (timer_create, 3, syscall_clockid, evp,
00099                                    &ktimerid);
00100 
00101 # ifndef __ASSUME_POSIX_TIMERS
00102          if (retval != -1 || errno != ENOSYS)
00103 # endif
00104            {
00105 # ifndef __ASSUME_POSIX_TIMERS
00106              __no_posix_timers = 1;
00107 # endif
00108 
00109              if (retval != -1)
00110               {
00111                 newp->sigev_notify = (evp != NULL
00112                                    ? evp->sigev_notify : SIGEV_SIGNAL);
00113                 newp->ktimerid = ktimerid;
00114 
00115                 *timerid = (timer_t) newp;
00116               }
00117              else
00118               {
00119                 /* Cannot allocate the timer, fail.  */
00120                 free (newp);
00121                 retval = -1;
00122               }
00123 
00124              return retval;
00125            }
00126 
00127          free (newp);
00128 
00129 # ifndef __ASSUME_POSIX_TIMERS
00130          /* When we come here the syscall does not exist.  Make sure we
00131             do not try to use it again.  */
00132          __no_posix_timers = -1;
00133 # endif
00134        }
00135       else
00136        {
00137 # ifndef __ASSUME_POSIX_TIMERS
00138          /* Make sure we have the necessary kernel support.  */
00139          if (__no_posix_timers == 0)
00140            {
00141              INTERNAL_SYSCALL_DECL (err);
00142              struct timespec ts;
00143              int res;
00144              res = INTERNAL_SYSCALL (clock_getres, err, 2,
00145                                   CLOCK_REALTIME, &ts);
00146              __no_posix_timers = (INTERNAL_SYSCALL_ERROR_P (res, err)
00147                                ? -1 : 1);
00148            }
00149 
00150          if (__no_posix_timers > 0)
00151 # endif
00152            {
00153              /* Create the helper thread.  */
00154              pthread_once (&__helper_once, __start_helper_thread);
00155              if (__helper_tid == 0)
00156               {
00157                 /* No resources to start the helper thread.  */
00158                 __set_errno (EAGAIN);
00159                 return -1;
00160               }
00161 
00162              struct timer *newp;
00163              newp = (struct timer *) malloc (sizeof (struct timer));
00164              if (newp == NULL)
00165               return -1;
00166 
00167              /* Copy the thread parameters the user provided.  */
00168              newp->sival = evp->sigev_value;
00169              newp->thrfunc = evp->sigev_notify_function;
00170 
00171              /* We cannot simply copy the thread attributes since the
00172                implementation might keep internal information for
00173                each instance.  */
00174              (void) pthread_attr_init (&newp->attr);
00175              if (evp->sigev_notify_attributes != NULL)
00176               {
00177                 struct pthread_attr *nattr;
00178                 struct pthread_attr *oattr;
00179 
00180                 nattr = (struct pthread_attr *) &newp->attr;
00181                 oattr = (struct pthread_attr *) evp->sigev_notify_attributes;
00182 
00183                 nattr->schedparam = oattr->schedparam;
00184                 nattr->schedpolicy = oattr->schedpolicy;
00185                 nattr->flags = oattr->flags;
00186                 nattr->guardsize = oattr->guardsize;
00187                 nattr->stackaddr = oattr->stackaddr;
00188                 nattr->stacksize = oattr->stacksize;
00189               }
00190 
00191              /* In any case set the detach flag.  */
00192              (void) pthread_attr_setdetachstate (&newp->attr,
00193                                             PTHREAD_CREATE_DETACHED);
00194 
00195              /* Create the event structure for the kernel timer.  */
00196              struct sigevent sev =
00197               { .sigev_value.sival_ptr = newp,
00198                 .sigev_signo = SIGTIMER,
00199                 .sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID,
00200                 ._sigev_un = { ._pad = { [0] = __helper_tid } } };
00201 
00202              /* Create the timer.  */
00203              INTERNAL_SYSCALL_DECL (err);
00204              int res;
00205              res = INTERNAL_SYSCALL (timer_create, err, 3,
00206                                   syscall_clockid, &sev, &newp->ktimerid);
00207              if (! INTERNAL_SYSCALL_ERROR_P (res, err))
00208               {
00209                 /* Add to the queue of active timers with thread
00210                    delivery.  */
00211                 pthread_mutex_lock (&__active_timer_sigev_thread_lock);
00212                 newp->next = __active_timer_sigev_thread;
00213                 __active_timer_sigev_thread = newp;
00214                 pthread_mutex_unlock (&__active_timer_sigev_thread_lock);
00215 
00216                 *timerid = (timer_t) newp;
00217                 return 0;
00218               }
00219 
00220              /* Free the resources.  */
00221              free (newp);
00222 
00223              __set_errno (INTERNAL_SYSCALL_ERRNO (res, err));
00224 
00225              return -1;
00226            }
00227        }
00228     }
00229 
00230 # ifndef __ASSUME_POSIX_TIMERS
00231   /* Compatibility code.  */
00232   return compat_timer_create (clock_id, evp, timerid);
00233 # endif
00234 }
00235 #else
00236 # ifdef timer_create_alias
00237 #  define timer_create timer_create_alias
00238 # endif
00239 /* The new system calls are not available.  Use the userlevel
00240    implementation.  */
00241 # include <nptl/sysdeps/pthread/timer_create.c>
00242 #endif