(CFLAGS-tst-align.c): Add -mpreferred-stack-boundary=4.
[glibc.git] / sysdeps / mach / hurd / setitimer.c
blob9fef56287d77ff6b8dd73d6a74cf071619317f46
1 /* Copyright (C) 1994,1995,1996,1997,2000,2001 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
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.
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.
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. */
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>
29 /* XXX Temporary cheezoid implementation of ITIMER_REAL/SIGALRM. */
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. */
40 static void
41 quantize_timeval (struct timeval *tv)
43 static time_t quantum = -1;
45 if (quantum == -1)
46 quantum = 1000000 / __getclktck ();
48 tv->tv_usec = ((tv->tv_usec + (quantum - 1)) / quantum) * quantum;
49 if (tv->tv_usec >= 1000000)
51 ++tv->tv_sec;
52 tv->tv_usec -= 1000000;
56 static inline void
57 subtract_timeval (struct timeval *from, const struct timeval *subtract)
59 from->tv_usec -= subtract->tv_usec;
60 from->tv_sec -= subtract->tv_sec;
61 while (from->tv_usec < 0)
63 --from->tv_sec;
64 from->tv_usec += 1000000;
68 /* Function run by the itimer thread.
69 This code must be very careful not ever to require a MiG reply port. */
71 static void
72 timer_thread (void)
74 while (1)
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
82 mach_msg_header_t header;
83 error_t return_code;
84 } msg;
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)
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;
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;
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;
122 default:
123 /* Unexpected lossage. Oh well, keep trying. */
124 break;
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)
135 static int setitimer_locked (const struct itimerval *new,
136 struct itimerval *old, void *crit);
138 /* This function gets called in the signal thread
139 each time a SIGALRM is arriving (even if blocked). */
140 struct itimerval it;
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);
147 /* Continue with normal delivery (or hold, etc.) of SIGALRM. */
148 return SIG_ERR;
152 /* Called before any normal SIGALRM signal is delivered.
153 Reload the itimer, or disable the itimer. */
155 static int
156 setitimer_locked (const struct itimerval *new, struct itimerval *old,
157 void *crit)
159 struct itimerval newval;
160 struct timeval now, remaining, elapsed;
161 struct timeval old_interval;
162 error_t err;
164 inline void kill_itimer_thread (void)
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;
173 if (!new)
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;
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)
189 /* Make sure the itimer thread is set up. */
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 =
195 __sigmask (SIGALRM), 0, 0,
196 &restart_itimer,
198 __mutex_lock (&_hurd_siglock);
199 if (! preemptor.next && _hurdsig_preemptors != &preemptor)
201 preemptor.next = _hurdsig_preemptors;
202 _hurdsig_preemptors = &preemptor;
204 __mutex_unlock (&_hurd_siglock);
206 if (_hurd_itimer_port == MACH_PORT_NULL)
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;
216 if (_hurd_itimer_thread == MACH_PORT_NULL)
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))
230 __thread_terminate (_hurd_itimer_thread);
231 _hurd_itimer_thread = MACH_PORT_NULL;
232 goto out;
234 _hurd_itimer_thread_suspended = 1;
238 if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0 || old != NULL)
240 /* Calculate how much time is remaining for the pending alarm. */
241 if (__gettimeofday (&now, NULL) < 0)
243 __spin_unlock (&_hurd_itimer_lock);
244 _hurd_critical_section_unlock (crit);
245 return -1;
247 elapsed = now;
248 subtract_timeval (&elapsed, &_hurd_itimer_started);
249 remaining = _hurd_itimerval.it_value;
250 if (timercmp (&remaining, &elapsed, <))
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;
258 else
259 subtract_timeval (&remaining, &elapsed);
261 /* Remember the old reload interval before changing it. */
262 old_interval = _hurd_itimerval.it_interval;
264 /* Record the starting time that the timer interval relates to. */
265 _hurd_itimer_started = now;
268 /* Load the new itimer value. */
269 _hurd_itimerval = newval;
271 if ((newval.it_value.tv_sec | newval.it_value.tv_usec) == 0)
273 /* Disable the itimer. */
274 if (_hurd_itimer_thread && !_hurd_itimer_thread_suspended)
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;
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)
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))
305 kill_itimer_thread ();
306 goto out;
308 _hurd_itimer_thread_suspended = 0;
311 __spin_unlock (&_hurd_itimer_lock);
312 _hurd_critical_section_unlock (crit);
314 if (old != NULL)
316 old->it_value = remaining;
317 old->it_interval = old_interval;
319 return 0;
321 out:
322 __spin_unlock (&_hurd_itimer_lock);
323 _hurd_critical_section_unlock (crit);
324 return __hurd_fail (err);
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. */
331 __setitimer (enum __itimer_which which, const struct itimerval *new,
332 struct itimerval *old)
334 void *crit;
336 switch (which)
338 default:
339 return __hurd_fail (EINVAL);
341 case ITIMER_VIRTUAL:
342 case ITIMER_PROF:
343 return __hurd_fail (ENOSYS);
345 case ITIMER_REAL:
346 break;
349 crit = _hurd_critical_section_lock ();
350 __spin_lock (&_hurd_itimer_lock);
351 return setitimer_locked (new, old, crit);
354 static void
355 fork_itimer (void)
357 /* We must restart the itimer in the child. */
359 struct itimerval it;
361 __spin_lock (&_hurd_itimer_lock);
362 _hurd_itimer_thread = MACH_PORT_NULL;
363 it = _hurd_itimerval;
364 it.it_value = it.it_interval;
366 setitimer_locked (&it, NULL, NULL);
368 (void) &fork_itimer; /* Avoid gcc optimizing out the function. */
370 text_set_element (_hurd_fork_child_hook, fork_itimer);
372 weak_alias (__setitimer, setitimer)