root/plugin/timer/timer_create.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. timer_create_reset_on_fork
  2. timer_create_sigev_thread
  3. timer_sigev_thread
  4. timer_helper_thread
  5. start_helper_thread

   1 /* Copyright (C) 2003-2013 Free Software Foundation, Inc.
   2    This file is part of the GNU C Library.
   3    Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
   4 
   5    The GNU C Library is free software; you can redistribute it and/or
   6    modify it under the terms of the GNU Lesser General Public License as
   7    published by the Free Software Foundation; either version 2.1 of the
   8    License, or (at your option) any later version.
   9 
  10    The GNU C Library is distributed in the hope that it will be useful,
  11    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13    Lesser General Public License for more details.
  14 
  15    You should have received a copy of the GNU Lesser General Public
  16    License along with the GNU C Library; see the file COPYING.LIB.  If
  17    not, see <http://www.gnu.org/licenses/>.  */
  18 
  19 #include <errno.h>
  20 #include <pthread.h>
  21 #include <semaphore.h>
  22 #include <signal.h>
  23 #include <stdlib.h>
  24 #include <string.h>
  25 #include <time.h>
  26 #include <unistd.h>
  27 #include <sys/errno.h>
  28 #include <sys/syscall.h>
  29 
  30 #include "timerwrappers.h"
  31 
  32 #include "jalloc.h"
  33 #include "jassert.h"
  34 
  35 // As defined in libc.
  36 #define SIGCANCEL SIGRTMIN
  37 #define SIGTIMER SIGCANCEL
  38 
  39 /* Internal representation of timer.  */
  40 struct timer
  41 {
  42   /* Notification mechanism.  */
  43   int sigev_notify;
  44 
  45   /* Parameters for the thread to be started for SIGEV_THREAD.  */
  46   void (*thrfunc) (sigval_t);
  47   sigval_t sival;
  48   pthread_attr_t attr;
  49 
  50   /* Next element in list of active SIGEV_THREAD timers.  */
  51   struct timer *next;
  52 };
  53 
  54 struct thread_start_data
  55 {
  56   void (*thrfunc) (sigval_t);
  57   sigval_t sival;
  58 };
  59 
  60 /* List of active SIGEV_THREAD timers.  */
  61 struct timer *active_timer_sigev_thread;
  62 
  63 /* Lock for the active_timer_sigev_thread.  */
  64 pthread_mutex_t active_timer_sigev_thread_lock = PTHREAD_MUTEX_INITIALIZER;
  65 
  66 /* Control variable for helper thread creation.  */
  67 static pthread_once_t helper_once;
  68 
  69 /* TID of the helper thread.  */
  70 static pid_t helper_tid = 0;
  71 static sem_t helper_notification;
  72 
  73 static void *timer_helper_thread (void *arg);
  74 static void start_helper_thread (void);
  75 
  76 /* Reset variables so that after a fork a new helper thread gets started.  */
  77 static void timer_create_reset_on_fork(void)
  78 {
  79   helper_once = PTHREAD_ONCE_INIT;
  80   helper_tid = 0;
  81 }
  82 
  83 LIB_PRIVATE
  84 int timer_create_sigev_thread(clockid_t clock_id,
  85                               struct sigevent *evp,
  86                               timer_t *timerid,
  87                               struct sigevent *sevOut)
  88 {
  89   /* If the user wants notification via a thread we need to handle
  90      this special.  */
  91   JASSERT(evp == NULL || evp->sigev_notify == SIGEV_THREAD);
  92 
  93   /* Create the helper thread.  */
  94   pthread_once (&helper_once, start_helper_thread);
  95   sem_wait(&helper_notification);
  96   if (helper_tid == 0) {
  97     /* No resources to start the helper thread.  */
  98     errno = EAGAIN;
  99     return -1;
 100   }
 101 
 102   struct timer *newp;
 103   newp = (struct timer *) JALLOC_MALLOC(sizeof (struct timer));
 104   if (newp == NULL) {
 105     return -1;
 106   }
 107 
 108   /* Copy the thread parameters the user provided.  */
 109   newp->sival = evp->sigev_value;
 110   newp->thrfunc = evp->sigev_notify_function;
 111   newp->sigev_notify = SIGEV_THREAD;
 112 
 113   /* We cannot simply copy the thread attributes since the
 114      implementation might keep internal information for
 115      each instance.  */
 116   (void) pthread_attr_init (&newp->attr);
 117 
 118   // TODO: Copy attributes from evp->sigev_notify_attributes to newp->attr.
 119 
 120   /* In any case set the detach flag.  */
 121   (void) pthread_attr_setdetachstate (&newp->attr, PTHREAD_CREATE_DETACHED);
 122 
 123   /* Create the event structure for the kernel timer.  */
 124   sevOut->sigev_value.sival_ptr = newp;
 125   sevOut->sigev_signo = SIGTIMER;
 126   sevOut->sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
 127   sevOut->_sigev_un._tid = helper_tid;
 128 
 129   /* Create the timer.  */
 130   int res = _real_timer_create(clock_id, sevOut, timerid);
 131   if (res == 0) {
 132     /* Add to the queue of active timers with thread delivery.  */
 133     pthread_mutex_lock (&active_timer_sigev_thread_lock);
 134     newp->next = active_timer_sigev_thread;
 135     active_timer_sigev_thread = newp;
 136     pthread_mutex_unlock (&active_timer_sigev_thread_lock);
 137     return 0;
 138   }
 139 
 140   /* Free the resources.  */
 141   JALLOC_FREE(newp);
 142   return -1;
 143 }
 144 
 145 /* Helper thread to call the user-provided function.  */
 146 static void *timer_sigev_thread (void *arg)
 147 {
 148   /* The parent thread has all signals blocked.  This is a bit
 149      surprising for user code, although valid.  We unblock all
 150      signals.  */
 151   sigset_t ss;
 152   sigemptyset (&ss);
 153   pthread_sigmask(SIG_SETMASK, &ss, NULL);
 154 
 155   struct thread_start_data *td = (struct thread_start_data *) arg;
 156 
 157   void (*thrfunc) (sigval_t) = td->thrfunc;
 158   sigval_t sival = td->sival;
 159 
 160   /* The TD object was allocated in timer_helper_thread.  */
 161   JALLOC_FREE(td);
 162 
 163   /* Call the user-provided function.  */
 164   thrfunc (sival);
 165 
 166   return NULL;
 167 }
 168 
 169 
 170 /* Helper function to support starting threads for SIGEV_THREAD.  */
 171 static void *timer_helper_thread (void *arg)
 172 {
 173   helper_tid = syscall(SYS_gettid);
 174   sem_post(&helper_notification);
 175 
 176   /* Wait for the SIGTIMER signal, allowing the setXid signal, and
 177      none else.  */
 178   sigset_t ss;
 179   sigemptyset (&ss);
 180   sigaddset (&ss, SIGTIMER);
 181 
 182   /* Endless loop of waiting for signals.  The loop is only ended when
 183      the thread is canceled.  */
 184   while (1) {
 185     siginfo_t si;
 186 
 187     /* sigwaitinfo cannot be used here, since it deletes
 188        SIGCANCEL == SIGTIMER from the set.  */
 189 
 190     pthread_testcancel();
 191     //int oldtype = LIBC_CANCEL_ASYNC ();
 192 
 193     /* XXX The size argument hopefully will have to be changed to the
 194        real size of the user-level sigset_t.  */
 195     int result = sigtimedwait(&ss, &si, NULL);
 196 
 197     //LIBC_CANCEL_RESET (oldtype);
 198 
 199     if (result > 0) {
 200       if (si.si_code == SI_TIMER) {
 201         struct timer *tk = (struct timer *) si.si_ptr;
 202 
 203         /* Check the timer is still used and will not go away
 204            while we are reading the values here.  */
 205         pthread_mutex_lock (&active_timer_sigev_thread_lock);
 206 
 207         struct timer *runp = active_timer_sigev_thread;
 208         while (runp != NULL)
 209           if (runp == tk)
 210             break;
 211           else
 212             runp = runp->next;
 213 
 214         if (runp != NULL) {
 215           struct thread_start_data *td =
 216             (struct thread_start_data*) JALLOC_MALLOC(sizeof (*td));
 217 
 218           /* There is not much we can do if the allocation fails.  */
 219           if (td != NULL) {
 220             /* This is the signal we are waiting for.  */
 221             td->thrfunc = tk->thrfunc;
 222             td->sival = tk->sival;
 223 
 224             pthread_t th;
 225             (void) pthread_create (&th, &tk->attr, timer_sigev_thread, td);
 226           }
 227         }
 228 
 229         pthread_mutex_unlock (&active_timer_sigev_thread_lock);
 230       }
 231       else if (si.si_code == SI_TKILL)
 232         /* The thread is canceled.  */
 233         pthread_exit (NULL);
 234     }
 235   }
 236 }
 237 
 238 
 239 static void start_helper_thread (void)
 240 {
 241   sem_init(&helper_notification, 0, 0);
 242   /* The helper thread needs only very little resources
 243      and should go away automatically when canceled.  */
 244   pthread_attr_t attr;
 245   (void) pthread_attr_init (&attr);
 246   (void) pthread_attr_setstacksize (&attr, 2 * 1024 * 1024);
 247 
 248   /* Block all signals in the helper thread but SIGSETXID.  To do this
 249      thoroughly we temporarily have to block all signals here.  The
 250      helper can lose wakeups if SIGCANCEL is not blocked throughout,
 251      but sigfillset omits it SIGSETXID.  So, we add SIGCANCEL back
 252      explicitly here.  */
 253   sigset_t ss;
 254   sigset_t oss;
 255   sigfillset (&ss);
 256   sigaddset (&ss, SIGCANCEL);
 257   sigprocmask(SIG_SETMASK, &ss, &oss);
 258 
 259   /* Create the helper thread for this timer.  */
 260   pthread_t th;
 261   int res = pthread_create (&th, &attr, timer_helper_thread, NULL);
 262   JASSERT(res == 0);
 263   if (res != 0) {
 264     sem_post(&helper_notification);
 265   }
 266 
 267   /* Restore the signal mask.  */
 268   sigprocmask(SIG_SETMASK, &oss, NULL);
 269 
 270   /* No need for the attribute anymore.  */
 271   (void) pthread_attr_destroy (&attr);
 272 
 273   /* We have to make sure that after fork()ing a new helper thread can
 274      be created.  */
 275   pthread_atfork (NULL, NULL, timer_create_reset_on_fork);
 276 }

/* [<][>][^][v][top][bottom][index][help] */