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
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. */
41 quantize_timeval (struct timeval
*tv
)
43 static time_t quantum
= -1;
46 quantum
= 1000000 / __getclktck ();
48 tv
->tv_usec
= ((tv
->tv_usec
+ (quantum
- 1)) / quantum
) * quantum
;
49 if (tv
->tv_usec
>= 1000000)
52 tv
->tv_usec
-= 1000000;
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)
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. */
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
82 mach_msg_header_t header
;
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
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,
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
,
106 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
107 SIGALRM
, 0, __mach_task_self ());
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. */
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. */
123 /* Unexpected lossage. Oh well, keep trying. */
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). */
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. */
152 /* Called before any normal SIGALRM signal is delivered.
153 Reload the itimer, or disable the itimer. */
156 setitimer_locked (const struct itimerval
*new, struct itimerval
*old
,
159 struct itimerval newval
;
160 struct timeval now
, remaining
, elapsed
;
161 struct timeval old_interval
;
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
;
175 /* Just return the current value in OLD without changing anything.
176 This is what BSD does, even though it's not documented. */
178 *old
= _hurd_itimerval
;
179 spin_unlock (&_hurd_itimer_lock
);
180 _hurd_critical_section_unlock (crit
);
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,
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
,
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 (),
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
;
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
);
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. */
255 remaining
.tv_sec
= 0;
256 remaining
.tv_usec
= 0;
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 ();
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 ();
308 _hurd_itimer_thread_suspended
= 0;
311 __spin_unlock (&_hurd_itimer_lock
);
312 _hurd_critical_section_unlock (crit
);
316 old
->it_value
= remaining
;
317 old
->it_interval
= old_interval
;
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
)
339 return __hurd_fail (EINVAL
);
343 return __hurd_fail (ENOSYS
);
349 crit
= _hurd_critical_section_lock ();
350 __spin_lock (&_hurd_itimer_lock
);
351 return setitimer_locked (new, old
, crit
);
357 /* We must restart the itimer in the child. */
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
)