]> git.ipfire.org Git - thirdparty/glibc.git/blob - sysdeps/mach/hurd/setitimer.c
Prefer https to http for gnu.org and fsf.org URLs
[thirdparty/glibc.git] / sysdeps / mach / hurd / setitimer.c
1 /* Copyright (C) 1994-2019 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
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.
8
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.
13
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/>. */
17
18 #include <stddef.h>
19 #include <errno.h>
20 #include <sys/time.h>
21 #include <time.h>
22 #include <hurd.h>
23 #include <hurd/signal.h>
24 #include <hurd/sigpreempt.h>
25 #include <hurd/msg_request.h>
26 #include <mach/message.h>
27
28 /* XXX Temporary cheezoid implementation of ITIMER_REAL/SIGALRM. */
29
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. */
38
39 static void
40 quantize_timeval (struct timeval *tv)
41 {
42 static time_t quantum = -1;
43
44 if (quantum == -1)
45 quantum = 1000000 / __getclktck ();
46
47 tv->tv_usec = ((tv->tv_usec + (quantum - 1)) / quantum) * quantum;
48 if (tv->tv_usec >= 1000000)
49 {
50 ++tv->tv_sec;
51 tv->tv_usec -= 1000000;
52 }
53 }
54
55 static inline void
56 subtract_timeval (struct timeval *from, const struct timeval *subtract)
57 {
58 from->tv_usec -= subtract->tv_usec;
59 from->tv_sec -= subtract->tv_sec;
60 while (from->tv_usec < 0)
61 {
62 --from->tv_sec;
63 from->tv_usec += 1000000;
64 }
65 }
66
67 /* Function run by the itimer thread.
68 This code must be very careful not ever to require a MiG reply port. */
69
70 static void
71 timer_thread (void)
72 {
73 while (1)
74 {
75 error_t err;
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
78 contents. */
79 struct
80 {
81 mach_msg_header_t header;
82 error_t return_code;
83 } msg;
84
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
88 _hurd_itimerval. */
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,
94 MACH_PORT_NULL);
95 switch (err)
96 {
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,
104 _hurd_itimer_port,
105 MACH_MSG_TYPE_MAKE_SEND_ONCE,
106 SIGALRM, 0, __mach_task_self ());
107 break;
108
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. */
113 break;
114
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. */
119 break;
120
121 default:
122 /* Unexpected lossage. Oh well, keep trying. */
123 break;
124 }
125 }
126 }
127
128
129 /* Forward declaration. */
130 static int setitimer_locked (const struct itimerval *new,
131 struct itimerval *old, void *crit,
132 int hurd_siglocked);
133
134 static sighandler_t
135 restart_itimer (struct hurd_signal_preemptor *preemptor,
136 struct hurd_sigstate *ss,
137 int *signo, struct hurd_signal_detail *detail)
138 {
139 /* This function gets called in the signal thread
140 each time a SIGALRM is arriving (even if blocked). */
141 struct itimerval it;
142
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);
147
148 /* Continue with normal delivery (or hold, etc.) of SIGALRM. */
149 return SIG_ERR;
150 }
151
152
153 /* Called before any normal SIGALRM signal is delivered.
154 Reload the itimer, or disable the itimer. */
155
156 static int
157 setitimer_locked (const struct itimerval *new, struct itimerval *old,
158 void *crit, int hurd_siglocked)
159 {
160 struct itimerval newval;
161 struct timeval now, remaining, elapsed;
162 struct timeval old_interval;
163 error_t err;
164
165 inline void kill_itimer_thread (void)
166 {
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;
172 }
173
174 if (!new)
175 {
176 /* Just return the current value in OLD without changing anything.
177 This is what BSD does, even though it's not documented. */
178 if (old)
179 *old = _hurd_itimerval;
180 spin_unlock (&_hurd_itimer_lock);
181 _hurd_critical_section_unlock (crit);
182 return 0;
183 }
184
185 newval = *new;
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)
189 {
190 /* Make sure the itimer thread is set up. */
191
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 =
195 {
196 __sigmask (SIGALRM), SI_TIMER, SI_TIMER,
197 &restart_itimer,
198 };
199 if (!hurd_siglocked)
200 __mutex_lock (&_hurd_siglock);
201 if (! preemptor.next && _hurdsig_preemptors != &preemptor)
202 {
203 preemptor.next = _hurdsig_preemptors;
204 _hurdsig_preemptors = &preemptor;
205 _hurdsig_preempted_set |= preemptor.signals;
206 }
207 if (!hurd_siglocked)
208 __mutex_unlock (&_hurd_siglock);
209
210 if (_hurd_itimer_port == MACH_PORT_NULL)
211 {
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,
216 &_hurd_itimer_port))
217 goto out;
218 }
219
220 if (_hurd_itimer_thread == MACH_PORT_NULL)
221 {
222 /* Start up the itimer thread running `timer_thread' (below). */
223 if (err = __thread_create (__mach_task_self (),
224 &_hurd_itimer_thread))
225 goto out;
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 (),
229 _hurd_itimer_thread,
230 &timer_thread,
231 &_hurd_itimer_thread_stack_base,
232 &_hurd_itimer_thread_stack_size))
233 || (err = __mach_setup_tls(_hurd_itimer_thread)))
234 {
235 __thread_terminate (_hurd_itimer_thread);
236 _hurd_itimer_thread = MACH_PORT_NULL;
237 goto out;
238 }
239 _hurd_itimer_thread_suspended = 1;
240 }
241 }
242
243 if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0 || old != NULL)
244 {
245 /* Calculate how much time is remaining for the pending alarm. */
246 if (__gettimeofday (&now, NULL) < 0)
247 {
248 __spin_unlock (&_hurd_itimer_lock);
249 _hurd_critical_section_unlock (crit);
250 return -1;
251 }
252 elapsed = now;
253 subtract_timeval (&elapsed, &_hurd_itimer_started);
254 remaining = _hurd_itimerval.it_value;
255 if (timercmp (&remaining, &elapsed, <))
256 {
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. */
259 /* XXX wrong */
260 remaining.tv_sec = 0;
261 remaining.tv_usec = 0;
262 }
263 else
264 subtract_timeval (&remaining, &elapsed);
265
266 /* Remember the old reload interval before changing it. */
267 old_interval = _hurd_itimerval.it_interval;
268
269 /* Record the starting time that the timer interval relates to. */
270 _hurd_itimer_started = now;
271 }
272
273 /* Load the new itimer value. */
274 _hurd_itimerval = newval;
275
276 if ((newval.it_value.tv_sec | newval.it_value.tv_usec) == 0)
277 {
278 /* Disable the itimer. */
279 if (_hurd_itimer_thread && !_hurd_itimer_thread_suspended)
280 {
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 ();
289 else
290 _hurd_itimer_thread_suspended = 1;
291 }
292 }
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)
296 {
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))
309 {
310 kill_itimer_thread ();
311 goto out;
312 }
313 _hurd_itimer_thread_suspended = 0;
314 }
315
316 __spin_unlock (&_hurd_itimer_lock);
317 _hurd_critical_section_unlock (crit);
318
319 if (old != NULL)
320 {
321 old->it_value = remaining;
322 old->it_interval = old_interval;
323 }
324 return 0;
325
326 out:
327 __spin_unlock (&_hurd_itimer_lock);
328 _hurd_critical_section_unlock (crit);
329 return __hurd_fail (err);
330 }
331
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. */
335 int
336 __setitimer (enum __itimer_which which, const struct itimerval *new,
337 struct itimerval *old)
338 {
339 void *crit;
340
341 switch (which)
342 {
343 default:
344 return __hurd_fail (EINVAL);
345
346 case ITIMER_VIRTUAL:
347 case ITIMER_PROF:
348 return __hurd_fail (ENOSYS);
349
350 case ITIMER_REAL:
351 break;
352 }
353
354 crit = _hurd_critical_section_lock ();
355 __spin_lock (&_hurd_itimer_lock);
356 return setitimer_locked (new, old, crit, 0);
357 }
358 \f
359 static void
360 fork_itimer (void)
361 {
362 /* We must restart the itimer in the child. */
363
364 struct itimerval it;
365
366 __spin_lock (&_hurd_itimer_lock);
367 _hurd_itimer_thread = MACH_PORT_NULL;
368 it = _hurd_itimerval;
369 it.it_value = it.it_interval;
370
371 setitimer_locked (&it, NULL, NULL, 0);
372
373 (void) &fork_itimer; /* Avoid gcc optimizing out the function. */
374 }
375 text_set_element (_hurd_fork_child_hook, fork_itimer);
376
377 weak_alias (__setitimer, setitimer)