OSDN Git Service

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