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