]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygwin/timer.cc
91e71146c179a7be01e4185b5b27fd7d55232112
[newlib-cygwin.git] / winsup / cygwin / timer.cc
1 /* timer.cc
2
3 Copyright 2004, 2005 Red Hat, Inc.
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 #include "winsup.h"
12 #include "thread.h"
13 #include "cygtls.h"
14 #include "sigproc.h"
15 #include "path.h"
16 #include "fhandler.h"
17 #include "dtable.h"
18 #include "cygheap.h"
19
20 #define TT_MAGIC 0x513e4a1c
21 struct timer_tracker
22 {
23 unsigned magic;
24 clockid_t clock_id;
25 sigevent evp;
26 timespec it_interval;
27 HANDLE hcancel;
28 HANDLE syncthread;
29 long long interval_us;
30 long long sleepto_us;
31 bool cancel ();
32 struct timer_tracker *next;
33 int settime (int, const itimerspec *, itimerspec *);
34 void gettime (itimerspec *);
35 timer_tracker (clockid_t, const sigevent *);
36 ~timer_tracker ();
37 friend void fixup_timers_after_fork ();
38 };
39
40 timer_tracker NO_COPY ttstart (CLOCK_REALTIME, NULL);
41
42 class lock_timer_tracker
43 {
44 static muto protect;
45 public:
46 lock_timer_tracker ();
47 ~lock_timer_tracker ();
48 };
49
50 muto NO_COPY lock_timer_tracker::protect;
51
52 lock_timer_tracker::lock_timer_tracker ()
53 {
54 protect.init ("timer_protect")->acquire ();
55 }
56
57 lock_timer_tracker::~lock_timer_tracker ()
58 {
59 protect.release ();
60 }
61
62 bool
63 timer_tracker::cancel ()
64 {
65 if (!hcancel)
66 return false;
67
68 SetEvent (hcancel);
69 if (WaitForSingleObject (syncthread, INFINITE) != WAIT_OBJECT_0)
70 api_fatal ("WFSO failed waiting for timer thread, %E");
71 return true;
72 }
73
74 timer_tracker::~timer_tracker ()
75 {
76 if (cancel ())
77 {
78 CloseHandle (hcancel);
79 #ifdef DEBUGGING
80 hcancel = NULL;
81 #endif
82 }
83 if (syncthread)
84 CloseHandle (syncthread);
85 magic = 0;
86 }
87
88 timer_tracker::timer_tracker (clockid_t c, const sigevent *e)
89 {
90 if (e != NULL)
91 evp = *e;
92 else
93 {
94 evp.sigev_notify = SIGEV_SIGNAL;
95 evp.sigev_signo = SIGALRM;
96 evp.sigev_value.sival_ptr = this;
97 }
98 clock_id = c;
99 magic = TT_MAGIC;
100 hcancel = NULL;
101 if (this != &ttstart)
102 {
103 lock_timer_tracker here;
104 next = ttstart.next;
105 ttstart.next = this;
106 }
107 }
108
109 static long long
110 to_us (const timespec& ts)
111 {
112 long long res = ts.tv_sec;
113 res *= 1000000;
114 res += ts.tv_nsec / 1000 + ((ts.tv_nsec % 1000) ? 1 : 0);
115 return res;
116 }
117
118 static DWORD WINAPI
119 timer_thread (VOID *x)
120 {
121 timer_tracker *tt = ((timer_tracker *) x);
122 long long now;
123 long long sleepto_us = tt->sleepto_us;
124 while (1)
125 {
126 long long sleep_us;
127 long sleep_ms;
128 /* Account for delays in starting thread
129 and sending the signal */
130 now = gtod.usecs ();
131 sleep_us = sleepto_us - now;
132 if (sleep_us > 0)
133 {
134 tt->sleepto_us = sleepto_us;
135 sleep_ms = (sleep_us + 999) / 1000;
136 }
137 else
138 {
139 tt->sleepto_us = now;
140 sleep_ms = 0;
141 }
142
143 debug_printf ("%p waiting for %u ms", x, sleep_ms);
144 switch (WaitForSingleObject (tt->hcancel, sleep_ms))
145 {
146 case WAIT_TIMEOUT:
147 debug_printf ("timed out");
148 break;
149 case WAIT_OBJECT_0:
150 debug_printf ("%p cancelled", x);
151 goto out;
152 default:
153 debug_printf ("%p wait failed, %E", x);
154 goto out;
155 }
156
157 switch (tt->evp.sigev_notify)
158 {
159 case SIGEV_SIGNAL:
160 {
161 siginfo_t si = {0};
162 si.si_signo = tt->evp.sigev_signo;
163 si.si_sigval.sival_ptr = tt->evp.sigev_value.sival_ptr;
164 si.si_code = SI_TIMER;
165 debug_printf ("%p sending sig %d", x, tt->evp.sigev_signo);
166 sig_send (myself_nowait, si);
167 break;
168 }
169 case SIGEV_THREAD:
170 {
171 pthread_t notify_thread;
172 debug_printf ("%p starting thread", x);
173 pthread_attr_t *attr;
174 pthread_attr_t default_attr;
175 if (tt->evp.sigev_notify_attributes)
176 attr = tt->evp.sigev_notify_attributes;
177 else
178 {
179 pthread_attr_init(attr = &default_attr);
180 pthread_attr_setdetachstate (attr, PTHREAD_CREATE_DETACHED);
181 }
182
183 int rc = pthread_create (&notify_thread, attr,
184 (void * (*) (void *)) tt->evp.sigev_notify_function,
185 tt->evp.sigev_value.sival_ptr);
186 if (rc)
187 {
188 debug_printf ("thread creation failed, %E");
189 return 0;
190 }
191 // FIXME: pthread_join?
192 break;
193 }
194 }
195 if (!tt->interval_us)
196 break;
197
198 sleepto_us = tt->sleepto_us + tt->interval_us;
199 debug_printf ("looping");
200 }
201
202 out:
203 _my_tls._ctinfo->auto_release (); /* automatically return the cygthread to the cygthread pool */
204 return 0;
205 }
206
207 static bool
208 it_bad (const timespec& t)
209 {
210 if (t.tv_nsec < 0 || t.tv_nsec >= 1000000000 || t.tv_sec < 0)
211 {
212 set_errno (EINVAL);
213 return true;
214 }
215 return false;
216 }
217
218 int
219 timer_tracker::settime (int in_flags, const itimerspec *value, itimerspec *ovalue)
220 {
221 if (!value)
222 {
223 set_errno (EINVAL);
224 return -1;
225 }
226
227 myfault efault;
228 if (efault.faulted (EFAULT)
229 || it_bad (value->it_value)
230 || it_bad (value->it_interval))
231 return -1;
232
233 long long now = in_flags & TIMER_ABSTIME ? 0 : gtod.usecs ();
234
235 lock_timer_tracker here;
236 cancel ();
237
238 if (ovalue)
239 gettime (ovalue);
240
241 if (!value->it_value.tv_sec && !value->it_value.tv_nsec)
242 interval_us = sleepto_us = 0;
243 else
244 {
245 sleepto_us = now + to_us (value->it_value);
246 interval_us = to_us (value->it_interval);
247 it_interval = value->it_interval;
248 if (!hcancel)
249 hcancel = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
250 else
251 ResetEvent (hcancel);
252 if (!syncthread)
253 syncthread = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
254 else
255 ResetEvent (syncthread);
256 new cygthread (timer_thread, this, "itimer", syncthread);
257 }
258
259 return 0;
260 }
261
262 void
263 timer_tracker::gettime (itimerspec *ovalue)
264 {
265 if (!hcancel)
266 memset (ovalue, 0, sizeof (*ovalue));
267 else
268 {
269 ovalue->it_interval = it_interval;
270 long long now = gtod.usecs ();
271 long long left_us = sleepto_us - now;
272 if (left_us < 0)
273 left_us = 0;
274 ovalue->it_value.tv_sec = left_us / 1000000;
275 ovalue->it_value.tv_nsec = (left_us % 1000000) * 1000;
276 }
277 }
278
279 extern "C" int
280 timer_gettime (timer_t timerid, struct itimerspec *ovalue)
281 {
282 myfault efault;
283 if (efault.faulted (EFAULT))
284 return -1;
285
286 timer_tracker *tt = (timer_tracker *) timerid;
287 if (tt->magic != TT_MAGIC)
288 {
289 set_errno (EINVAL);
290 return -1;
291 }
292
293 tt->gettime (ovalue);
294 return 0;
295 }
296
297 extern "C" int
298 timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
299 {
300 myfault efault;
301 if (efault.faulted (EFAULT))
302 return -1;
303 if (clock_id != CLOCK_REALTIME)
304 {
305 set_errno (EINVAL);
306 return -1;
307 }
308
309 *timerid = (timer_t) new timer_tracker (clock_id, evp);
310 return 0;
311 }
312
313 extern "C" int
314 timer_settime (timer_t timerid, int flags, const struct itimerspec *value,
315 struct itimerspec *ovalue)
316 {
317 timer_tracker *tt = (timer_tracker *) timerid;
318 myfault efault;
319 if (efault.faulted (EFAULT))
320 return -1;
321 if (tt->magic != TT_MAGIC)
322 {
323 set_errno (EINVAL);
324 return -1;
325 }
326
327 return tt->settime (flags, value, ovalue);
328 }
329
330 extern "C" int
331 timer_delete (timer_t timerid)
332 {
333 timer_tracker *in_tt = (timer_tracker *) timerid;
334 myfault efault;
335 if (efault.faulted (EFAULT))
336 return -1;
337 if (in_tt->magic != TT_MAGIC)
338 {
339 set_errno (EINVAL);
340 return -1;
341 }
342
343 lock_timer_tracker here;
344 for (timer_tracker *tt = &ttstart; tt->next != NULL; tt = tt->next)
345 if (tt->next == in_tt)
346 {
347 tt->next = in_tt->next;
348 delete in_tt;
349 return 0;
350 }
351 set_errno (EINVAL);
352 return 0;
353 }
354
355 void
356 fixup_timers_after_fork ()
357 {
358 ttstart.hcancel = ttstart.syncthread = NULL;
359 for (timer_tracker *tt = &ttstart; tt->next != NULL; /* nothing */)
360 {
361 timer_tracker *deleteme = tt->next;
362 tt->next = deleteme->next;
363 deleteme->hcancel = deleteme->syncthread = NULL;
364 delete deleteme;
365 }
366 }
367
368
369 extern "C" int
370 setitimer (int which, const struct itimerval *value, struct itimerval *ovalue)
371 {
372 if (which != ITIMER_REAL)
373 {
374 set_errno (EINVAL);
375 return -1;
376 }
377 struct itimerspec spec_value, spec_ovalue;
378 int ret;
379 spec_value.it_interval.tv_sec = value->it_interval.tv_sec;
380 spec_value.it_interval.tv_nsec = value->it_interval.tv_usec * 1000;
381 spec_value.it_value.tv_sec = value->it_value.tv_sec;
382 spec_value.it_value.tv_nsec = value->it_value.tv_usec * 1000;
383 ret = timer_settime ((timer_t) &ttstart, 0, &spec_value, &spec_ovalue);
384 if (!ret && ovalue)
385 {
386 ovalue->it_interval.tv_sec = spec_ovalue.it_interval.tv_sec;
387 ovalue->it_interval.tv_usec = spec_ovalue.it_interval.tv_nsec / 1000;
388 ovalue->it_value.tv_sec = spec_ovalue.it_value.tv_sec;
389 ovalue->it_value.tv_usec = spec_ovalue.it_value.tv_nsec / 1000;
390 }
391 syscall_printf ("%d = setitimer ()", ret);
392 return ret;
393 }
394
395
396 extern "C" int
397 getitimer (int which, struct itimerval *ovalue)
398 {
399 if (which != ITIMER_REAL)
400 {
401 set_errno (EINVAL);
402 return -1;
403 }
404 myfault efault;
405 if (efault.faulted (EFAULT))
406 return -1;
407 struct itimerspec spec_ovalue;
408 int ret = timer_gettime ((timer_t) &ttstart, &spec_ovalue);
409 if (!ret)
410 {
411 ovalue->it_interval.tv_sec = spec_ovalue.it_interval.tv_sec;
412 ovalue->it_interval.tv_usec = spec_ovalue.it_interval.tv_nsec / 1000;
413 ovalue->it_value.tv_sec = spec_ovalue.it_value.tv_sec;
414 ovalue->it_value.tv_usec = spec_ovalue.it_value.tv_nsec / 1000;
415 }
416 syscall_printf ("%d = getitimer ()", ret);
417 return ret;
418 }
419
420 /* FIXME: POSIX - alarm survives exec */
421 extern "C" unsigned int
422 alarm (unsigned int seconds)
423 {
424 struct itimerspec newt = {}, oldt;
425 /* alarm cannot fail, but only needs not be
426 correct for arguments < 64k. Truncate */
427 if (seconds > (HIRES_DELAY_MAX / 1000 - 1))
428 seconds = (HIRES_DELAY_MAX / 1000 - 1);
429 newt.it_value.tv_sec = seconds;
430 timer_settime ((timer_t) &ttstart, 0, &newt, &oldt);
431 int ret = oldt.it_value.tv_sec + (oldt.it_value.tv_nsec > 0);
432 syscall_printf ("%d = alarm (%d)", ret, seconds);
433 return ret;
434 }
435
436 extern "C" useconds_t
437 ualarm (useconds_t value, useconds_t interval)
438 {
439 struct itimerspec timer = {}, otimer;
440 /* ualarm cannot fail.
441 Interpret negative arguments as zero */
442 if (value > 0)
443 {
444 timer.it_value.tv_sec = (unsigned int) value / 1000000;
445 timer.it_value.tv_nsec = ((unsigned int) value % 1000000) * 1000;
446 }
447 if (interval > 0)
448 {
449 timer.it_interval.tv_sec = (unsigned int) interval / 1000000;
450 timer.it_interval.tv_nsec = ((unsigned int) interval % 1000000) * 1000;
451 }
452 timer_settime ((timer_t) &ttstart, 0, &timer, &otimer);
453 useconds_t ret = otimer.it_value.tv_sec * 1000000 + (otimer.it_value.tv_nsec + 999) / 1000;
454 syscall_printf ("%d = ualarm (%d , %d)", ret, value, interval);
455 return ret;
456 }
This page took 0.055618 seconds and 4 git commands to generate.