OSDN Git Service

gas/opcodes: blackfin: move dsp mac func defines to common header
[pf3gnuchains/sourceware.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 }