1
/* Copyright (C) 1994-2012 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
<http://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_address_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, SI_TIMER, __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);
134
restart_itimer (struct hurd_signal_preemptor *preemptor,
135
struct hurd_sigstate *ss,
136
int *signo, struct hurd_signal_detail *detail)
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))
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))
229
|| (err = __mach_setup_tls(_hurd_itimer_thread)))
231
__thread_terminate (_hurd_itimer_thread);
232
_hurd_itimer_thread = MACH_PORT_NULL;
235
_hurd_itimer_thread_suspended = 1;
239
if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0 || old != NULL)
241
/* Calculate how much time is remaining for the pending alarm. */
242
if (__gettimeofday (&now, NULL) < 0)
244
__spin_unlock (&_hurd_itimer_lock);
245
_hurd_critical_section_unlock (crit);
249
subtract_timeval (&elapsed, &_hurd_itimer_started);
250
remaining = _hurd_itimerval.it_value;
251
if (timercmp (&remaining, &elapsed, <))
253
/* Hmm. The timer should have just gone off, but has not been reset.
254
This is a possible timing glitch. The alarm will signal soon. */
256
remaining.tv_sec = 0;
257
remaining.tv_usec = 0;
260
subtract_timeval (&remaining, &elapsed);
262
/* Remember the old reload interval before changing it. */
263
old_interval = _hurd_itimerval.it_interval;
265
/* Record the starting time that the timer interval relates to. */
266
_hurd_itimer_started = now;
269
/* Load the new itimer value. */
270
_hurd_itimerval = newval;
272
if ((newval.it_value.tv_sec | newval.it_value.tv_usec) == 0)
274
/* Disable the itimer. */
275
if (_hurd_itimer_thread && !_hurd_itimer_thread_suspended)
277
/* Suspend the itimer thread so it does nothing. Then abort its
278
kernel context so that when the thread is resumed, mach_msg
279
will return to timer_thread (below) and it will fetch new
280
values from _hurd_itimerval. */
281
if ((err = __thread_suspend (_hurd_itimer_thread)) ||
282
(err = __thread_abort (_hurd_itimer_thread)))
283
/* If we can't save it for later, nuke it. */
284
kill_itimer_thread ();
286
_hurd_itimer_thread_suspended = 1;
289
/* See if the timeout changed. If so, we must alert the itimer thread. */
290
else if (remaining.tv_sec != newval.it_value.tv_sec ||
291
remaining.tv_usec != newval.it_value.tv_usec)
293
/* The timeout value is changing. Tell the itimer thread to
294
reexamine it and start counting down. If the itimer thread is
295
marked as suspended, either we just created it, or it was
296
suspended and thread_abort'd last time the itimer was disabled;
297
either way it will wake up and start waiting for the new timeout
298
value when we resume it. If it is not suspended, the itimer
299
thread is waiting to deliver a pending alarm that we will override
300
(since it would come later than the new alarm being set);
301
thread_abort will make mach_msg return MACH_RCV_INTERRUPTED, so it
302
will loop around and use the new timeout value. */
303
if (err = (_hurd_itimer_thread_suspended
304
? __thread_resume : __thread_abort) (_hurd_itimer_thread))
306
kill_itimer_thread ();
309
_hurd_itimer_thread_suspended = 0;
312
__spin_unlock (&_hurd_itimer_lock);
313
_hurd_critical_section_unlock (crit);
317
old->it_value = remaining;
318
old->it_interval = old_interval;
323
__spin_unlock (&_hurd_itimer_lock);
324
_hurd_critical_section_unlock (crit);
325
return __hurd_fail (err);
328
/* Set the timer WHICH to *NEW. If OLD is not NULL,
329
set *OLD to the old value of timer WHICH.
330
Returns 0 on success, -1 on errors. */
332
__setitimer (enum __itimer_which which, const struct itimerval *new,
333
struct itimerval *old)
340
return __hurd_fail (EINVAL);
344
return __hurd_fail (ENOSYS);
350
crit = _hurd_critical_section_lock ();
351
__spin_lock (&_hurd_itimer_lock);
352
return setitimer_locked (new, old, crit);
358
/* We must restart the itimer in the child. */
362
__spin_lock (&_hurd_itimer_lock);
363
_hurd_itimer_thread = MACH_PORT_NULL;
364
it = _hurd_itimerval;
365
it.it_value = it.it_interval;
367
setitimer_locked (&it, NULL, NULL);
369
(void) &fork_itimer; /* Avoid gcc optimizing out the function. */
371
text_set_element (_hurd_fork_child_hook, fork_itimer);
373
weak_alias (__setitimer, setitimer)