1 /* Copyright (C) 1994-2019 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, see
16 <https://www.gnu.org/licenses/>. */
23 #include <hurd/signal.h>
24 #include <hurd/sigpreempt.h>
25 #include <hurd/msg_request.h>
26 #include <mach/message.h>
28 /* XXX Temporary cheezoid implementation of ITIMER_REAL/SIGALRM. */
30 spin_lock_t _hurd_itimer_lock
= SPIN_LOCK_INITIALIZER
;
31 struct itimerval _hurd_itimerval
; /* Current state of the timer. */
32 mach_port_t _hurd_itimer_port
; /* Port the timer thread blocks on. */
33 thread_t _hurd_itimer_thread
; /* Thread waiting for timeout. */
34 int _hurd_itimer_thread_suspended
; /* Nonzero if that thread is suspended. */
35 vm_address_t _hurd_itimer_thread_stack_base
; /* Base of its stack. */
36 vm_size_t _hurd_itimer_thread_stack_size
; /* Size of its stack. */
37 struct timeval _hurd_itimer_started
; /* Time the thread started waiting. */
40 quantize_timeval (struct timeval
*tv
)
42 static time_t quantum
= -1;
45 quantum
= 1000000 / __getclktck ();
47 tv
->tv_usec
= ((tv
->tv_usec
+ (quantum
- 1)) / quantum
) * quantum
;
48 if (tv
->tv_usec
>= 1000000)
51 tv
->tv_usec
-= 1000000;
56 subtract_timeval (struct timeval
*from
, const struct timeval
*subtract
)
58 from
->tv_usec
-= subtract
->tv_usec
;
59 from
->tv_sec
-= subtract
->tv_sec
;
60 while (from
->tv_usec
< 0)
63 from
->tv_usec
+= 1000000;
67 /* Function run by the itimer thread.
68 This code must be very careful not ever to require a MiG reply port. */
76 /* The only message we ever expect to receive is the reply from the
77 signal thread to a sig_post call we did. We never examine the
81 mach_msg_header_t header
;
85 /* Wait for a message on a port that noone sends to. The purpose is
86 the receive timeout. Notice interrupts so that if we are
87 thread_abort'd, we will loop around and fetch new values from
89 err
= __mach_msg (&msg
.header
,
90 MACH_RCV_MSG
|MACH_RCV_TIMEOUT
|MACH_RCV_INTERRUPT
,
91 0, 0, _hurd_itimer_port
,
92 _hurd_itimerval
.it_value
.tv_sec
* 1000
93 + _hurd_itimerval
.it_value
.tv_usec
/ 1000,
97 case MACH_RCV_TIMED_OUT
:
98 /* We got the expected timeout. Send a message to the signal
99 thread to tell it to post a SIGALRM signal. We use
100 _hurd_itimer_port as the reply port just so we will block until
101 the signal thread has frobnicated things to reload the itimer or
102 has terminated this thread. */
103 __msg_sig_post_request (_hurd_msgport
,
105 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
106 SIGALRM
, 0, __mach_task_self ());
109 case MACH_RCV_INTERRUPTED
:
110 /* We were thread_abort'd. This is to tell us that
111 _hurd_itimerval has changed and we need to reexamine it
112 and start waiting with the new timeout value. */
115 case MACH_MSG_SUCCESS
:
116 /* We got the reply message from the sig_post_request above.
117 Ignore it and reexamine the timer value. */
118 __mach_msg_destroy (&msg
.header
); /* Just in case. */
122 /* Unexpected lossage. Oh well, keep trying. */
129 /* Forward declaration. */
130 static int setitimer_locked (const struct itimerval
*new,
131 struct itimerval
*old
, void *crit
,
135 restart_itimer (struct hurd_signal_preemptor
*preemptor
,
136 struct hurd_sigstate
*ss
,
137 int *signo
, struct hurd_signal_detail
*detail
)
139 /* This function gets called in the signal thread
140 each time a SIGALRM is arriving (even if blocked). */
143 /* Either reload or disable the itimer. */
144 __spin_lock (&_hurd_itimer_lock
);
145 it
.it_value
= it
.it_interval
= _hurd_itimerval
.it_interval
;
146 setitimer_locked (&it
, NULL
, NULL
, 1);
148 /* Continue with normal delivery (or hold, etc.) of SIGALRM. */
153 /* Called before any normal SIGALRM signal is delivered.
154 Reload the itimer, or disable the itimer. */
157 setitimer_locked (const struct itimerval
*new, struct itimerval
*old
,
158 void *crit
, int hurd_siglocked
)
160 struct itimerval newval
;
161 struct timeval now
, remaining
, elapsed
;
162 struct timeval old_interval
;
165 inline void kill_itimer_thread (void)
167 __thread_terminate (_hurd_itimer_thread
);
168 __vm_deallocate (__mach_task_self (),
169 _hurd_itimer_thread_stack_base
,
170 _hurd_itimer_thread_stack_size
);
171 _hurd_itimer_thread
= MACH_PORT_NULL
;
176 /* Just return the current value in OLD without changing anything.
177 This is what BSD does, even though it's not documented. */
179 *old
= _hurd_itimerval
;
180 spin_unlock (&_hurd_itimer_lock
);
181 _hurd_critical_section_unlock (crit
);
186 quantize_timeval (&newval
.it_interval
);
187 quantize_timeval (&newval
.it_value
);
188 if ((newval
.it_value
.tv_sec
| newval
.it_value
.tv_usec
) != 0)
190 /* Make sure the itimer thread is set up. */
192 /* Set up a signal preemptor global for all threads to
193 run `restart_itimer' each time a SIGALRM would arrive. */
194 static struct hurd_signal_preemptor preemptor
=
196 __sigmask (SIGALRM
), SI_TIMER
, SI_TIMER
,
200 __mutex_lock (&_hurd_siglock
);
201 if (! preemptor
.next
&& _hurdsig_preemptors
!= &preemptor
)
203 preemptor
.next
= _hurdsig_preemptors
;
204 _hurdsig_preemptors
= &preemptor
;
205 _hurdsig_preempted_set
|= preemptor
.signals
;
208 __mutex_unlock (&_hurd_siglock
);
210 if (_hurd_itimer_port
== MACH_PORT_NULL
)
212 /* Allocate a receive right that the itimer thread will
213 block waiting for a message on. */
214 if (err
= __mach_port_allocate (__mach_task_self (),
215 MACH_PORT_RIGHT_RECEIVE
,
220 if (_hurd_itimer_thread
== MACH_PORT_NULL
)
222 /* Start up the itimer thread running `timer_thread' (below). */
223 if (err
= __thread_create (__mach_task_self (),
224 &_hurd_itimer_thread
))
226 _hurd_itimer_thread_stack_base
= 0; /* Anywhere. */
227 _hurd_itimer_thread_stack_size
= __vm_page_size
; /* Small stack. */
228 if ((err
= __mach_setup_thread (__mach_task_self (),
231 &_hurd_itimer_thread_stack_base
,
232 &_hurd_itimer_thread_stack_size
))
233 || (err
= __mach_setup_tls(_hurd_itimer_thread
)))
235 __thread_terminate (_hurd_itimer_thread
);
236 _hurd_itimer_thread
= MACH_PORT_NULL
;
239 _hurd_itimer_thread_suspended
= 1;
243 if ((newval
.it_value
.tv_sec
| newval
.it_value
.tv_usec
) != 0 || old
!= NULL
)
245 /* Calculate how much time is remaining for the pending alarm. */
246 if (__gettimeofday (&now
, NULL
) < 0)
248 __spin_unlock (&_hurd_itimer_lock
);
249 _hurd_critical_section_unlock (crit
);
253 subtract_timeval (&elapsed
, &_hurd_itimer_started
);
254 remaining
= _hurd_itimerval
.it_value
;
255 if (timercmp (&remaining
, &elapsed
, <))
257 /* Hmm. The timer should have just gone off, but has not been reset.
258 This is a possible timing glitch. The alarm will signal soon. */
260 remaining
.tv_sec
= 0;
261 remaining
.tv_usec
= 0;
264 subtract_timeval (&remaining
, &elapsed
);
266 /* Remember the old reload interval before changing it. */
267 old_interval
= _hurd_itimerval
.it_interval
;
269 /* Record the starting time that the timer interval relates to. */
270 _hurd_itimer_started
= now
;
273 /* Load the new itimer value. */
274 _hurd_itimerval
= newval
;
276 if ((newval
.it_value
.tv_sec
| newval
.it_value
.tv_usec
) == 0)
278 /* Disable the itimer. */
279 if (_hurd_itimer_thread
&& !_hurd_itimer_thread_suspended
)
281 /* Suspend the itimer thread so it does nothing. Then abort its
282 kernel context so that when the thread is resumed, mach_msg
283 will return to timer_thread (below) and it will fetch new
284 values from _hurd_itimerval. */
285 if ((err
= __thread_suspend (_hurd_itimer_thread
))
286 || (err
= __thread_abort (_hurd_itimer_thread
)))
287 /* If we can't save it for later, nuke it. */
288 kill_itimer_thread ();
290 _hurd_itimer_thread_suspended
= 1;
293 /* See if the timeout changed. If so, we must alert the itimer thread. */
294 else if (remaining
.tv_sec
!= newval
.it_value
.tv_sec
295 || remaining
.tv_usec
!= newval
.it_value
.tv_usec
)
297 /* The timeout value is changing. Tell the itimer thread to
298 reexamine it and start counting down. If the itimer thread is
299 marked as suspended, either we just created it, or it was
300 suspended and thread_abort'd last time the itimer was disabled;
301 either way it will wake up and start waiting for the new timeout
302 value when we resume it. If it is not suspended, the itimer
303 thread is waiting to deliver a pending alarm that we will override
304 (since it would come later than the new alarm being set);
305 thread_abort will make mach_msg return MACH_RCV_INTERRUPTED, so it
306 will loop around and use the new timeout value. */
307 if (err
= (_hurd_itimer_thread_suspended
308 ? __thread_resume
: __thread_abort
) (_hurd_itimer_thread
))
310 kill_itimer_thread ();
313 _hurd_itimer_thread_suspended
= 0;
316 __spin_unlock (&_hurd_itimer_lock
);
317 _hurd_critical_section_unlock (crit
);
321 old
->it_value
= remaining
;
322 old
->it_interval
= old_interval
;
327 __spin_unlock (&_hurd_itimer_lock
);
328 _hurd_critical_section_unlock (crit
);
329 return __hurd_fail (err
);
332 /* Set the timer WHICH to *NEW. If OLD is not NULL,
333 set *OLD to the old value of timer WHICH.
334 Returns 0 on success, -1 on errors. */
336 __setitimer (enum __itimer_which which
, const struct itimerval
*new,
337 struct itimerval
*old
)
344 return __hurd_fail (EINVAL
);
348 return __hurd_fail (ENOSYS
);
354 crit
= _hurd_critical_section_lock ();
355 __spin_lock (&_hurd_itimer_lock
);
356 return setitimer_locked (new, old
, crit
, 0);
362 /* We must restart the itimer in the child. */
366 __spin_lock (&_hurd_itimer_lock
);
367 _hurd_itimer_thread
= MACH_PORT_NULL
;
368 it
= _hurd_itimerval
;
369 it
.it_value
= it
.it_interval
;
371 setitimer_locked (&it
, NULL
, NULL
, 0);
373 (void) &fork_itimer
; /* Avoid gcc optimizing out the function. */
375 text_set_element (_hurd_fork_child_hook
, fork_itimer
);
377 weak_alias (__setitimer
, setitimer
)