Back to index

glibc  2.9
setitimer.c
Go to the documentation of this file.
00001 /* Copyright (C) 1994,1995,1996,1997,2000,2001,2005
00002        Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
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
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the 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; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <stddef.h>
00021 #include <errno.h>
00022 #include <sys/time.h>
00023 #include <time.h>
00024 #include <hurd.h>
00025 #include <hurd/signal.h>
00026 #include <hurd/sigpreempt.h>
00027 #include <hurd/msg_request.h>
00028 #include <mach/message.h>
00029 
00030 /* XXX Temporary cheezoid implementation of ITIMER_REAL/SIGALRM.  */
00031 
00032 spin_lock_t _hurd_itimer_lock = SPIN_LOCK_INITIALIZER;
00033 struct itimerval _hurd_itimerval; /* Current state of the timer.  */
00034 mach_port_t _hurd_itimer_port;     /* Port the timer thread blocks on.  */
00035 thread_t _hurd_itimer_thread;      /* Thread waiting for timeout.  */
00036 int _hurd_itimer_thread_suspended; /* Nonzero if that thread is suspended.  */
00037 vm_address_t _hurd_itimer_thread_stack_base; /* Base of its stack.  */
00038 vm_address_t _hurd_itimer_thread_stack_size; /* Size of its stack.  */
00039 struct timeval _hurd_itimer_started; /* Time the thread started waiting.  */
00040 
00041 static void
00042 quantize_timeval (struct timeval *tv)
00043 {
00044   static time_t quantum = -1;
00045 
00046   if (quantum == -1)
00047     quantum = 1000000 / __getclktck ();
00048 
00049   tv->tv_usec = ((tv->tv_usec + (quantum - 1)) / quantum) * quantum;
00050   if (tv->tv_usec >= 1000000)
00051     {
00052       ++tv->tv_sec;
00053       tv->tv_usec -= 1000000;
00054     }
00055 }
00056 
00057 static inline void
00058 subtract_timeval (struct timeval *from, const struct timeval *subtract)
00059 {
00060   from->tv_usec -= subtract->tv_usec;
00061   from->tv_sec -= subtract->tv_sec;
00062   while (from->tv_usec < 0)
00063     {
00064       --from->tv_sec;
00065       from->tv_usec += 1000000;
00066     }
00067 }
00068 
00069 /* Function run by the itimer thread.
00070    This code must be very careful not ever to require a MiG reply port.  */
00071 
00072 static void
00073 timer_thread (void)
00074 {
00075   while (1)
00076     {
00077       error_t err;
00078       /* The only message we ever expect to receive is the reply from the
00079          signal thread to a sig_post call we did.  We never examine the
00080         contents.  */
00081       struct
00082        {
00083          mach_msg_header_t header;
00084          error_t return_code;
00085        } msg;
00086 
00087       /* Wait for a message on a port that noone sends to.  The purpose is
00088         the receive timeout.  Notice interrupts so that if we are
00089         thread_abort'd, we will loop around and fetch new values from
00090         _hurd_itimerval.  */
00091       err = __mach_msg (&msg.header,
00092                      MACH_RCV_MSG|MACH_RCV_TIMEOUT|MACH_RCV_INTERRUPT,
00093                      0, 0, _hurd_itimer_port,
00094                      _hurd_itimerval.it_value.tv_sec * 1000 +
00095                      _hurd_itimerval.it_value.tv_usec / 1000,
00096                      MACH_PORT_NULL);
00097       switch (err)
00098        {
00099        case MACH_RCV_TIMED_OUT:
00100          /* We got the expected timeout.  Send a message to the signal
00101             thread to tell it to post a SIGALRM signal.  We use
00102             _hurd_itimer_port as the reply port just so we will block until
00103             the signal thread has frobnicated things to reload the itimer or
00104             has terminated this thread.  */
00105          __msg_sig_post_request (_hurd_msgport,
00106                               _hurd_itimer_port,
00107                               MACH_MSG_TYPE_MAKE_SEND_ONCE,
00108                               SIGALRM, 0, __mach_task_self ());
00109          break;
00110 
00111        case MACH_RCV_INTERRUPTED:
00112          /* We were thread_abort'd.  This is to tell us that
00113             _hurd_itimerval has changed and we need to reexamine it
00114             and start waiting with the new timeout value.  */
00115          break;
00116 
00117        case MACH_MSG_SUCCESS:
00118          /* We got the reply message from the sig_post_request above.
00119             Ignore it and reexamine the timer value.  */
00120          __mach_msg_destroy (&msg.header); /* Just in case.  */
00121          break;
00122 
00123        default:
00124          /* Unexpected lossage.  Oh well, keep trying.  */
00125          break;
00126        }
00127     }
00128 }
00129 
00130 
00131 /* Forward declaration.  */
00132 static int setitimer_locked (const struct itimerval *new,
00133                           struct itimerval *old, void *crit);
00134 
00135 static sighandler_t
00136 restart_itimer (struct hurd_signal_preemptor *preemptor,
00137               struct hurd_sigstate *ss,
00138               int *signo, struct hurd_signal_detail *detail)
00139 {
00140   /* This function gets called in the signal thread
00141      each time a SIGALRM is arriving (even if blocked).  */
00142   struct itimerval it;
00143 
00144   /* Either reload or disable the itimer.  */
00145   __spin_lock (&_hurd_itimer_lock);
00146   it.it_value = it.it_interval = _hurd_itimerval.it_interval;
00147   setitimer_locked (&it, NULL, NULL);
00148 
00149   /* Continue with normal delivery (or hold, etc.) of SIGALRM.  */
00150   return SIG_ERR;
00151 }
00152 
00153 
00154 /* Called before any normal SIGALRM signal is delivered.
00155    Reload the itimer, or disable the itimer.  */
00156 
00157 static int
00158 setitimer_locked (const struct itimerval *new, struct itimerval *old,
00159                 void *crit)
00160 {
00161   struct itimerval newval;
00162   struct timeval now, remaining, elapsed;
00163   struct timeval old_interval;
00164   error_t err;
00165 
00166   inline void kill_itimer_thread (void)
00167     {
00168       __thread_terminate (_hurd_itimer_thread);
00169       __vm_deallocate (__mach_task_self (),
00170                      _hurd_itimer_thread_stack_base,
00171                      _hurd_itimer_thread_stack_size);
00172       _hurd_itimer_thread = MACH_PORT_NULL;
00173     }
00174 
00175   if (!new)
00176     {
00177       /* Just return the current value in OLD without changing anything.
00178         This is what BSD does, even though it's not documented. */
00179       if (old)
00180        *old = _hurd_itimerval;
00181       spin_unlock (&_hurd_itimer_lock);
00182       _hurd_critical_section_unlock (crit);
00183       return 0;
00184     }
00185 
00186   newval = *new;
00187   quantize_timeval (&newval.it_interval);
00188   quantize_timeval (&newval.it_value);
00189   if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0)
00190     {
00191       /* Make sure the itimer thread is set up.  */
00192 
00193       /* Set up a signal preemptor global for all threads to
00194         run `restart_itimer' each time a SIGALRM would arrive.  */
00195       static struct hurd_signal_preemptor preemptor =
00196        {
00197          __sigmask (SIGALRM), 0, 0,
00198          &restart_itimer,
00199        };
00200       __mutex_lock (&_hurd_siglock);
00201       if (! preemptor.next && _hurdsig_preemptors != &preemptor)
00202        {
00203          preemptor.next = _hurdsig_preemptors;
00204          _hurdsig_preemptors = &preemptor;
00205        }
00206       __mutex_unlock (&_hurd_siglock);
00207 
00208       if (_hurd_itimer_port == MACH_PORT_NULL)
00209        {
00210          /* Allocate a receive right that the itimer thread will
00211             block waiting for a message on.  */
00212          if (err = __mach_port_allocate (__mach_task_self (),
00213                                      MACH_PORT_RIGHT_RECEIVE,
00214                                      &_hurd_itimer_port))
00215            goto out;
00216        }
00217 
00218       if (_hurd_itimer_thread == MACH_PORT_NULL)
00219        {
00220          /* Start up the itimer thread running `timer_thread' (below).  */
00221          if (err = __thread_create (__mach_task_self (),
00222                                  &_hurd_itimer_thread))
00223            return __hurd_fail (err);
00224          _hurd_itimer_thread_stack_base = 0; /* Anywhere.  */
00225          _hurd_itimer_thread_stack_size = __vm_page_size; /* Small stack.  */
00226          if (err = __mach_setup_thread (__mach_task_self (),
00227                                     _hurd_itimer_thread,
00228                                     &timer_thread,
00229                                     &_hurd_itimer_thread_stack_base,
00230                                     &_hurd_itimer_thread_stack_size))
00231            {
00232              __thread_terminate (_hurd_itimer_thread);
00233              _hurd_itimer_thread = MACH_PORT_NULL;
00234              goto out;
00235            }
00236          _hurd_itimer_thread_suspended = 1;
00237        }
00238     }
00239 
00240   if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0 || old != NULL)
00241     {
00242       /* Calculate how much time is remaining for the pending alarm.  */
00243       if (__gettimeofday (&now, NULL) < 0)
00244        {
00245          __spin_unlock (&_hurd_itimer_lock);
00246          _hurd_critical_section_unlock (crit);
00247          return -1;
00248        }
00249       elapsed = now;
00250       subtract_timeval (&elapsed, &_hurd_itimer_started);
00251       remaining = _hurd_itimerval.it_value;
00252       if (timercmp (&remaining, &elapsed, <))
00253        {
00254          /* Hmm.  The timer should have just gone off, but has not been reset.
00255             This is a possible timing glitch.  The alarm will signal soon. */
00256          /* XXX wrong */
00257          remaining.tv_sec = 0;
00258          remaining.tv_usec = 0;
00259        }
00260       else
00261        subtract_timeval (&remaining, &elapsed);
00262 
00263       /* Remember the old reload interval before changing it.  */
00264       old_interval = _hurd_itimerval.it_interval;
00265 
00266       /* Record the starting time that the timer interval relates to.  */
00267       _hurd_itimer_started = now;
00268     }
00269 
00270   /* Load the new itimer value.  */
00271   _hurd_itimerval = newval;
00272 
00273   if ((newval.it_value.tv_sec | newval.it_value.tv_usec) == 0)
00274     {
00275       /* Disable the itimer.  */
00276       if (_hurd_itimer_thread && !_hurd_itimer_thread_suspended)
00277        {
00278          /* Suspend the itimer thread so it does nothing.  Then abort its
00279             kernel context so that when the thread is resumed, mach_msg
00280             will return to timer_thread (below) and it will fetch new
00281             values from _hurd_itimerval.  */
00282          if ((err = __thread_suspend (_hurd_itimer_thread)) ||
00283              (err = __thread_abort (_hurd_itimer_thread)))
00284            /* If we can't save it for later, nuke it.  */
00285            kill_itimer_thread ();
00286          else
00287            _hurd_itimer_thread_suspended = 1;
00288        }
00289     }
00290   /* See if the timeout changed.  If so, we must alert the itimer thread.  */
00291   else if (remaining.tv_sec != newval.it_value.tv_sec ||
00292           remaining.tv_usec != newval.it_value.tv_usec)
00293     {
00294       /* The timeout value is changing.  Tell the itimer thread to
00295         reexamine it and start counting down.  If the itimer thread is
00296         marked as suspended, either we just created it, or it was
00297         suspended and thread_abort'd last time the itimer was disabled;
00298         either way it will wake up and start waiting for the new timeout
00299         value when we resume it.  If it is not suspended, the itimer
00300         thread is waiting to deliver a pending alarm that we will override
00301         (since it would come later than the new alarm being set);
00302         thread_abort will make mach_msg return MACH_RCV_INTERRUPTED, so it
00303         will loop around and use the new timeout value.  */
00304       if (err = (_hurd_itimer_thread_suspended
00305                ? __thread_resume : __thread_abort) (_hurd_itimer_thread))
00306        {
00307          kill_itimer_thread ();
00308          goto out;
00309        }
00310       _hurd_itimer_thread_suspended = 0;
00311     }
00312 
00313   __spin_unlock (&_hurd_itimer_lock);
00314   _hurd_critical_section_unlock (crit);
00315 
00316   if (old != NULL)
00317     {
00318       old->it_value = remaining;
00319       old->it_interval = old_interval;
00320     }
00321   return 0;
00322 
00323  out:
00324   __spin_unlock (&_hurd_itimer_lock);
00325   _hurd_critical_section_unlock (crit);
00326   return __hurd_fail (err);
00327 }
00328 
00329 /* Set the timer WHICH to *NEW.  If OLD is not NULL,
00330    set *OLD to the old value of timer WHICH.
00331    Returns 0 on success, -1 on errors.  */
00332 int
00333 __setitimer (enum __itimer_which which, const struct itimerval *new,
00334             struct itimerval *old)
00335 {
00336   void *crit;
00337 
00338   switch (which)
00339     {
00340     default:
00341       return __hurd_fail (EINVAL);
00342 
00343     case ITIMER_VIRTUAL:
00344     case ITIMER_PROF:
00345       return __hurd_fail (ENOSYS);
00346 
00347     case ITIMER_REAL:
00348       break;
00349     }
00350 
00351   crit = _hurd_critical_section_lock ();
00352   __spin_lock (&_hurd_itimer_lock);
00353   return setitimer_locked (new, old, crit);
00354 }
00355 
00356 static void
00357 fork_itimer (void)
00358 {
00359   /* We must restart the itimer in the child.  */
00360 
00361   struct itimerval it;
00362 
00363   __spin_lock (&_hurd_itimer_lock);
00364   _hurd_itimer_thread = MACH_PORT_NULL;
00365   it = _hurd_itimerval;
00366   it.it_value = it.it_interval;
00367 
00368   setitimer_locked (&it, NULL, NULL);
00369 
00370   (void) &fork_itimer;             /* Avoid gcc optimizing out the function.  */
00371 }
00372 text_set_element (_hurd_fork_child_hook, fork_itimer);
00373 
00374 weak_alias (__setitimer, setitimer)