Update to LGPL v2.1.
[jlayton/glibc.git] / sysdeps / mach / hurd / setitimer.c
1 /* Copyright (C) 1994,1995,1996,1997,2000,2001 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <stddef.h>
20 #include <errno.h>
21 #include <sys/time.h>
22 #include <time.h>
23 #include <hurd.h>
24 #include <hurd/signal.h>
25 #include <hurd/sigpreempt.h>
26 #include <hurd/msg_request.h>
27 #include <mach/message.h>
28
29 /* XXX Temporary cheezoid implementation of ITIMER_REAL/SIGALRM.  */
30
31 spin_lock_t _hurd_itimer_lock = SPIN_LOCK_INITIALIZER;
32 struct itimerval _hurd_itimerval; /* Current state of the timer.  */
33 mach_port_t _hurd_itimer_port;  /* Port the timer thread blocks on.  */
34 thread_t _hurd_itimer_thread;   /* Thread waiting for timeout.  */
35 int _hurd_itimer_thread_suspended; /* Nonzero if that thread is suspended.  */
36 vm_address_t _hurd_itimer_thread_stack_base; /* Base of its stack.  */
37 vm_address_t _hurd_itimer_thread_stack_size; /* Size of its stack.  */
38 struct timeval _hurd_itimer_started; /* Time the thread started waiting.  */
39
40 static void
41 quantize_timeval (struct timeval *tv)
42 {
43   static time_t quantum = -1;
44   
45   if (quantum == -1)
46     quantum = 1000000 / __getclktck ();
47
48   tv->tv_usec = ((tv->tv_usec + (quantum - 1)) / quantum) * quantum;
49   if (tv->tv_usec >= 1000000)
50     {
51       ++tv->tv_sec;
52       tv->tv_usec -= 1000000;
53     }
54 }
55
56 static inline void
57 subtract_timeval (struct timeval *from, const struct timeval *subtract)
58 {
59   from->tv_usec -= subtract->tv_usec;
60   from->tv_sec -= subtract->tv_sec;
61   while (from->tv_usec < 0)
62     {
63       --from->tv_sec;
64       from->tv_usec += 1000000;
65     }
66 }
67
68 /* Function run by the itimer thread.
69    This code must be very careful not ever to require a MiG reply port.  */
70
71 static void
72 timer_thread (void)
73 {
74   while (1)
75     {
76       error_t err;
77       /* The only message we ever expect to receive is the reply from the
78          signal thread to a sig_post call we did.  We never examine the
79          contents.  */
80       struct
81         {
82           mach_msg_header_t header;
83           error_t return_code;
84         } msg;
85
86       /* Wait for a message on a port that noone sends to.  The purpose is
87          the receive timeout.  Notice interrupts so that if we are
88          thread_abort'd, we will loop around and fetch new values from
89          _hurd_itimerval.  */
90       err = __mach_msg (&msg.header,
91                         MACH_RCV_MSG|MACH_RCV_TIMEOUT|MACH_RCV_INTERRUPT,
92                         0, 0, _hurd_itimer_port,
93                         _hurd_itimerval.it_value.tv_sec * 1000 +
94                         _hurd_itimerval.it_value.tv_usec / 1000,
95                         MACH_PORT_NULL);
96       switch (err)
97         {
98         case MACH_RCV_TIMED_OUT:
99           /* We got the expected timeout.  Send a message to the signal
100              thread to tell it to post a SIGALRM signal.  We use
101              _hurd_itimer_port as the reply port just so we will block until
102              the signal thread has frobnicated things to reload the itimer or
103              has terminated this thread.  */
104           __msg_sig_post_request (_hurd_msgport,
105                                   _hurd_itimer_port,
106                                   MACH_MSG_TYPE_MAKE_SEND_ONCE,
107                                   SIGALRM, 0, __mach_task_self ());
108           break;
109
110         case MACH_RCV_INTERRUPTED:
111           /* We were thread_abort'd.  This is to tell us that
112              _hurd_itimerval has changed and we need to reexamine it
113              and start waiting with the new timeout value.  */
114           break;
115
116         case MACH_MSG_SUCCESS:
117           /* We got the reply message from the sig_post_request above.
118              Ignore it and reexamine the timer value.  */
119           __mach_msg_destroy (&msg.header); /* Just in case.  */
120           break;
121
122         default:
123           /* Unexpected lossage.  Oh well, keep trying.  */
124           break;
125         }
126     }
127 }
128
129
130 static sighandler_t
131 restart_itimer (struct hurd_signal_preemptor *preemptor,
132                 struct hurd_sigstate *ss,
133                 int *signo, struct hurd_signal_detail *detail)
134 {
135   static int setitimer_locked (const struct itimerval *new,
136                                struct itimerval *old, void *crit);
137
138   /* This function gets called in the signal thread
139      each time a SIGALRM is arriving (even if blocked).  */
140   struct itimerval it;
141
142   /* Either reload or disable the itimer.  */
143   __spin_lock (&_hurd_itimer_lock);
144   it.it_value = it.it_interval = _hurd_itimerval.it_interval;
145   setitimer_locked (&it, NULL, NULL);
146
147   /* Continue with normal delivery (or hold, etc.) of SIGALRM.  */
148   return SIG_ERR;
149 }
150
151
152 /* Called before any normal SIGALRM signal is delivered.
153    Reload the itimer, or disable the itimer.  */
154
155 static int
156 setitimer_locked (const struct itimerval *new, struct itimerval *old,
157                   void *crit)
158 {
159   struct itimerval newval;
160   struct timeval now, remaining, elapsed;
161   struct timeval old_interval;
162   error_t err;
163
164   inline void kill_itimer_thread (void)
165     {
166       __thread_terminate (_hurd_itimer_thread);
167       __vm_deallocate (__mach_task_self (),
168                        _hurd_itimer_thread_stack_base,
169                        _hurd_itimer_thread_stack_size);
170       _hurd_itimer_thread = MACH_PORT_NULL;
171     }
172
173   if (!new)
174     {
175       /* Just return the current value in OLD without changing anything.
176          This is what BSD does, even though it's not documented. */
177       if (old)
178         *old = _hurd_itimerval;
179       spin_unlock (&_hurd_itimer_lock);
180       _hurd_critical_section_unlock (crit);
181       return 0;
182     }
183
184   newval = *new;
185   quantize_timeval (&newval.it_interval);
186   quantize_timeval (&newval.it_value);
187   if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0)
188     {
189       /* Make sure the itimer thread is set up.  */
190
191       /* Set up a signal preemptor global for all threads to
192          run `restart_itimer' each time a SIGALRM would arrive.  */
193       static struct hurd_signal_preemptor preemptor =
194         {
195           __sigmask (SIGALRM), 0, 0,
196           &restart_itimer,
197         };
198       __mutex_lock (&_hurd_siglock);
199       if (! preemptor.next && _hurdsig_preemptors != &preemptor)
200         {
201           preemptor.next = _hurdsig_preemptors;
202           _hurdsig_preemptors = &preemptor;
203         }
204       __mutex_unlock (&_hurd_siglock);
205
206       if (_hurd_itimer_port == MACH_PORT_NULL)
207         {
208           /* Allocate a receive right that the itimer thread will
209              block waiting for a message on.  */
210           if (err = __mach_port_allocate (__mach_task_self (),
211                                           MACH_PORT_RIGHT_RECEIVE,
212                                           &_hurd_itimer_port))
213             goto out;
214         }
215
216       if (_hurd_itimer_thread == MACH_PORT_NULL)
217         {
218           /* Start up the itimer thread running `timer_thread' (below).  */
219           if (err = __thread_create (__mach_task_self (),
220                                      &_hurd_itimer_thread))
221             return __hurd_fail (err);
222           _hurd_itimer_thread_stack_base = 0; /* Anywhere.  */
223           _hurd_itimer_thread_stack_size = __vm_page_size; /* Small stack.  */
224           if (err = __mach_setup_thread (__mach_task_self (),
225                                          _hurd_itimer_thread,
226                                          &timer_thread,
227                                          &_hurd_itimer_thread_stack_base,
228                                          &_hurd_itimer_thread_stack_size))
229             {
230               __thread_terminate (_hurd_itimer_thread);
231               _hurd_itimer_thread = MACH_PORT_NULL;
232               goto out;
233             }
234           _hurd_itimer_thread_suspended = 1;
235         }
236     }
237
238   if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0 || old != NULL)
239     {
240       /* Calculate how much time is remaining for the pending alarm.  */
241       if (__gettimeofday (&now, NULL) < 0)
242         {
243           __spin_unlock (&_hurd_itimer_lock);
244           _hurd_critical_section_unlock (crit);
245           return -1;
246         }
247       elapsed = now;
248       subtract_timeval (&elapsed, &_hurd_itimer_started);
249       remaining = _hurd_itimerval.it_value;
250       if (timercmp (&remaining, &elapsed, <))
251         {
252           /* Hmm.  The timer should have just gone off, but has not been reset.
253              This is a possible timing glitch.  The alarm will signal soon. */
254           /* XXX wrong */
255           remaining.tv_sec = 0;
256           remaining.tv_usec = 0;
257         }
258       else
259         subtract_timeval (&remaining, &elapsed);
260
261       /* Remember the old reload interval before changing it.  */
262       old_interval = _hurd_itimerval.it_interval;
263
264       /* Record the starting time that the timer interval relates to.  */
265       _hurd_itimer_started = now;
266     }
267
268   /* Load the new itimer value.  */
269   _hurd_itimerval = newval;
270
271   if ((newval.it_value.tv_sec | newval.it_value.tv_usec) == 0)
272     {
273       /* Disable the itimer.  */
274       if (_hurd_itimer_thread && !_hurd_itimer_thread_suspended)
275         {
276           /* Suspend the itimer thread so it does nothing.  Then abort its
277              kernel context so that when the thread is resumed, mach_msg
278              will return to timer_thread (below) and it will fetch new
279              values from _hurd_itimerval.  */
280           if ((err = __thread_suspend (_hurd_itimer_thread)) ||
281               (err = __thread_abort (_hurd_itimer_thread)))
282             /* If we can't save it for later, nuke it.  */
283             kill_itimer_thread ();
284           else
285             _hurd_itimer_thread_suspended = 1;
286         }
287     }
288   /* See if the timeout changed.  If so, we must alert the itimer thread.  */
289   else if (remaining.tv_sec != newval.it_value.tv_sec ||
290            remaining.tv_usec != newval.it_value.tv_usec)
291     {
292       /* The timeout value is changing.  Tell the itimer thread to
293          reexamine it and start counting down.  If the itimer thread is
294          marked as suspended, either we just created it, or it was
295          suspended and thread_abort'd last time the itimer was disabled;
296          either way it will wake up and start waiting for the new timeout
297          value when we resume it.  If it is not suspended, the itimer
298          thread is waiting to deliver a pending alarm that we will override
299          (since it would come later than the new alarm being set);
300          thread_abort will make mach_msg return MACH_RCV_INTERRUPTED, so it
301          will loop around and use the new timeout value.  */
302       if (err = (_hurd_itimer_thread_suspended
303                  ? __thread_resume : __thread_abort) (_hurd_itimer_thread))
304         {
305           kill_itimer_thread ();
306           goto out;
307         }
308       _hurd_itimer_thread_suspended = 0;
309     }
310
311   __spin_unlock (&_hurd_itimer_lock);
312   _hurd_critical_section_unlock (crit);
313
314   if (old != NULL)
315     {
316       old->it_value = remaining;
317       old->it_interval = old_interval;
318     }
319   return 0;
320
321  out:
322   __spin_unlock (&_hurd_itimer_lock);
323   _hurd_critical_section_unlock (crit);
324   return __hurd_fail (err);
325 }
326
327 /* Set the timer WHICH to *NEW.  If OLD is not NULL,
328    set *OLD to the old value of timer WHICH.
329    Returns 0 on success, -1 on errors.  */
330 int
331 __setitimer (enum __itimer_which which, const struct itimerval *new,
332              struct itimerval *old)
333 {
334   void *crit;
335
336   switch (which)
337     {
338     default:
339       return __hurd_fail (EINVAL);
340
341     case ITIMER_VIRTUAL:
342     case ITIMER_PROF:
343       return __hurd_fail (ENOSYS);
344
345     case ITIMER_REAL:
346       break;
347     }
348
349   crit = _hurd_critical_section_lock ();
350   __spin_lock (&_hurd_itimer_lock);
351   return setitimer_locked (new, old, crit);
352 }
353 \f
354 static void
355 fork_itimer (void)
356 {
357   /* We must restart the itimer in the child.  */
358
359   struct itimerval it;
360
361   __spin_lock (&_hurd_itimer_lock);
362   _hurd_itimer_thread = MACH_PORT_NULL;
363   it = _hurd_itimerval;
364   it.it_value = it.it_interval;
365
366   setitimer_locked (&it, NULL, NULL);
367
368   (void) &fork_itimer;          /* Avoid gcc optimizing out the function.  */
369 }
370 text_set_element (_hurd_fork_child_hook, fork_itimer);
371
372 weak_alias (__setitimer, setitimer)