]> git.ipfire.org Git - thirdparty/glibc.git/blob - sysdeps/mach/hurd/setitimer.c
f5dfb7da84fce34d6b6b6ced32cbc68d2293c906
[thirdparty/glibc.git] / sysdeps / mach / hurd / setitimer.c
1 /* Copyright (C) 1994-2018 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 <http://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
133 static sighandler_t
134 restart_itimer (struct hurd_signal_preemptor *preemptor,
135 struct hurd_sigstate *ss,
136 int *signo, struct hurd_signal_detail *detail)
137 {
138 /* This function gets called in the signal thread
139 each time a SIGALRM is arriving (even if blocked). */
140 struct itimerval it;
141
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);
146
147 /* Continue with normal delivery (or hold, etc.) of SIGALRM. */
148 return SIG_ERR;
149 }
150
151
152 /* Called before any normal SIGALRM signal is delivered.
153 Reload the itimer, or disable the itimer. */
154
155 static int
156 setitimer_locked (const struct itimerval *new, struct itimerval *old,
157 void *crit)
158 {
159 struct itimerval newval;
160 struct timeval now, remaining, elapsed;
161 struct timeval old_interval;
162 error_t err;
163
164 inline void kill_itimer_thread (void)
165 {
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;
171 }
172
173 if (!new)
174 {
175 /* Just return the current value in OLD without changing anything.
176 This is what BSD does, even though it's not documented. */
177 if (old)
178 *old = _hurd_itimerval;
179 spin_unlock (&_hurd_itimer_lock);
180 _hurd_critical_section_unlock (crit);
181 return 0;
182 }
183
184 newval = *new;
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)
188 {
189 /* Make sure the itimer thread is set up. */
190
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 =
194 {
195 __sigmask (SIGALRM), 0, 0,
196 &restart_itimer,
197 };
198 __mutex_lock (&_hurd_siglock);
199 if (! preemptor.next && _hurdsig_preemptors != &preemptor)
200 {
201 preemptor.next = _hurdsig_preemptors;
202 _hurdsig_preemptors = &preemptor;
203 }
204 __mutex_unlock (&_hurd_siglock);
205
206 if (_hurd_itimer_port == MACH_PORT_NULL)
207 {
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,
212 &_hurd_itimer_port))
213 goto out;
214 }
215
216 if (_hurd_itimer_thread == MACH_PORT_NULL)
217 {
218 /* Start up the itimer thread running `timer_thread' (below). */
219 if (err = __thread_create (__mach_task_self (),
220 &_hurd_itimer_thread))
221 goto out;
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 (),
225 _hurd_itimer_thread,
226 &timer_thread,
227 &_hurd_itimer_thread_stack_base,
228 &_hurd_itimer_thread_stack_size))
229 || (err = __mach_setup_tls(_hurd_itimer_thread)))
230 {
231 __thread_terminate (_hurd_itimer_thread);
232 _hurd_itimer_thread = MACH_PORT_NULL;
233 goto out;
234 }
235 _hurd_itimer_thread_suspended = 1;
236 }
237 }
238
239 if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0 || old != NULL)
240 {
241 /* Calculate how much time is remaining for the pending alarm. */
242 if (__gettimeofday (&now, NULL) < 0)
243 {
244 __spin_unlock (&_hurd_itimer_lock);
245 _hurd_critical_section_unlock (crit);
246 return -1;
247 }
248 elapsed = now;
249 subtract_timeval (&elapsed, &_hurd_itimer_started);
250 remaining = _hurd_itimerval.it_value;
251 if (timercmp (&remaining, &elapsed, <))
252 {
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. */
255 /* XXX wrong */
256 remaining.tv_sec = 0;
257 remaining.tv_usec = 0;
258 }
259 else
260 subtract_timeval (&remaining, &elapsed);
261
262 /* Remember the old reload interval before changing it. */
263 old_interval = _hurd_itimerval.it_interval;
264
265 /* Record the starting time that the timer interval relates to. */
266 _hurd_itimer_started = now;
267 }
268
269 /* Load the new itimer value. */
270 _hurd_itimerval = newval;
271
272 if ((newval.it_value.tv_sec | newval.it_value.tv_usec) == 0)
273 {
274 /* Disable the itimer. */
275 if (_hurd_itimer_thread && !_hurd_itimer_thread_suspended)
276 {
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 ();
285 else
286 _hurd_itimer_thread_suspended = 1;
287 }
288 }
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)
292 {
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))
305 {
306 kill_itimer_thread ();
307 goto out;
308 }
309 _hurd_itimer_thread_suspended = 0;
310 }
311
312 __spin_unlock (&_hurd_itimer_lock);
313 _hurd_critical_section_unlock (crit);
314
315 if (old != NULL)
316 {
317 old->it_value = remaining;
318 old->it_interval = old_interval;
319 }
320 return 0;
321
322 out:
323 __spin_unlock (&_hurd_itimer_lock);
324 _hurd_critical_section_unlock (crit);
325 return __hurd_fail (err);
326 }
327
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. */
331 int
332 __setitimer (enum __itimer_which which, const struct itimerval *new,
333 struct itimerval *old)
334 {
335 void *crit;
336
337 switch (which)
338 {
339 default:
340 return __hurd_fail (EINVAL);
341
342 case ITIMER_VIRTUAL:
343 case ITIMER_PROF:
344 return __hurd_fail (ENOSYS);
345
346 case ITIMER_REAL:
347 break;
348 }
349
350 crit = _hurd_critical_section_lock ();
351 __spin_lock (&_hurd_itimer_lock);
352 return setitimer_locked (new, old, crit);
353 }
354 \f
355 static void
356 fork_itimer (void)
357 {
358 /* We must restart the itimer in the child. */
359
360 struct itimerval it;
361
362 __spin_lock (&_hurd_itimer_lock);
363 _hurd_itimer_thread = MACH_PORT_NULL;
364 it = _hurd_itimerval;
365 it.it_value = it.it_interval;
366
367 setitimer_locked (&it, NULL, NULL);
368
369 (void) &fork_itimer; /* Avoid gcc optimizing out the function. */
370 }
371 text_set_element (_hurd_fork_child_hook, fork_itimer);
372
373 weak_alias (__setitimer, setitimer)