3 Copyright 2004, 2005 Red Hat, Inc.
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
20 #define TT_MAGIC 0x513e4a1c
29 long long interval_us
;
32 struct timer_tracker
*next
;
33 int settime (int, const itimerspec
*, itimerspec
*);
34 void gettime (itimerspec
*);
35 timer_tracker (clockid_t
, const sigevent
*);
37 friend void fixup_timers_after_fork ();
40 timer_tracker NO_COPY
ttstart (CLOCK_REALTIME
, NULL
);
42 class lock_timer_tracker
46 lock_timer_tracker ();
47 ~lock_timer_tracker ();
50 muto NO_COPY
lock_timer_tracker::protect
;
52 lock_timer_tracker::lock_timer_tracker ()
54 protect
.init ("timer_protect")->acquire ();
57 lock_timer_tracker::~lock_timer_tracker ()
63 timer_tracker::cancel ()
69 if (WaitForSingleObject (syncthread
, INFINITE
) != WAIT_OBJECT_0
)
70 api_fatal ("WFSO failed waiting for timer thread, %E");
74 timer_tracker::~timer_tracker ()
78 CloseHandle (hcancel
);
84 CloseHandle (syncthread
);
88 timer_tracker::timer_tracker (clockid_t c
, const sigevent
*e
)
94 evp
.sigev_notify
= SIGEV_SIGNAL
;
95 evp
.sigev_signo
= SIGALRM
;
96 evp
.sigev_value
.sival_ptr
= this;
101 if (this != &ttstart
)
103 lock_timer_tracker here
;
110 to_us (const timespec
& ts
)
112 long long res
= ts
.tv_sec
;
114 res
+= ts
.tv_nsec
/ 1000 + ((ts
.tv_nsec
% 1000) ? 1 : 0);
119 timer_thread (VOID
*x
)
121 timer_tracker
*tt
= ((timer_tracker
*) x
);
123 long long sleepto_us
= tt
->sleepto_us
;
128 /* Account for delays in starting thread
129 and sending the signal */
131 sleep_us
= sleepto_us
- now
;
134 tt
->sleepto_us
= sleepto_us
;
135 sleep_ms
= (sleep_us
+ 999) / 1000;
139 tt
->sleepto_us
= now
;
143 debug_printf ("%p waiting for %u ms", x
, sleep_ms
);
144 switch (WaitForSingleObject (tt
->hcancel
, sleep_ms
))
147 debug_printf ("timed out");
150 debug_printf ("%p cancelled", x
);
153 debug_printf ("%p wait failed, %E", x
);
157 switch (tt
->evp
.sigev_notify
)
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
);
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
;
179 pthread_attr_init(attr
= &default_attr
);
180 pthread_attr_setdetachstate (attr
, PTHREAD_CREATE_DETACHED
);
183 int rc
= pthread_create (¬ify_thread
, attr
,
184 (void * (*) (void *)) tt
->evp
.sigev_notify_function
,
185 tt
->evp
.sigev_value
.sival_ptr
);
188 debug_printf ("thread creation failed, %E");
191 // FIXME: pthread_join?
195 if (!tt
->interval_us
)
198 sleepto_us
= tt
->sleepto_us
+ tt
->interval_us
;
199 debug_printf ("looping");
203 _my_tls
._ctinfo
->auto_release (); /* automatically return the cygthread to the cygthread pool */
208 it_bad (const timespec
& t
)
210 if (t
.tv_nsec
< 0 || t
.tv_nsec
>= 1000000000 || t
.tv_sec
< 0)
219 timer_tracker::settime (int in_flags
, const itimerspec
*value
, itimerspec
*ovalue
)
228 if (efault
.faulted (EFAULT
)
229 || it_bad (value
->it_value
)
230 || it_bad (value
->it_interval
))
233 long long now
= in_flags
& TIMER_ABSTIME
? 0 : gtod
.usecs ();
235 lock_timer_tracker here
;
241 if (!value
->it_value
.tv_sec
&& !value
->it_value
.tv_nsec
)
242 interval_us
= sleepto_us
= 0;
245 sleepto_us
= now
+ to_us (value
->it_value
);
246 interval_us
= to_us (value
->it_interval
);
247 it_interval
= value
->it_interval
;
249 hcancel
= CreateEvent (&sec_none_nih
, TRUE
, FALSE
, NULL
);
251 ResetEvent (hcancel
);
253 syncthread
= CreateEvent (&sec_none_nih
, TRUE
, FALSE
, NULL
);
255 ResetEvent (syncthread
);
256 new cygthread (timer_thread
, this, "itimer", syncthread
);
263 timer_tracker::gettime (itimerspec
*ovalue
)
266 memset (ovalue
, 0, sizeof (*ovalue
));
269 ovalue
->it_interval
= it_interval
;
270 long long now
= gtod
.usecs ();
271 long long left_us
= sleepto_us
- now
;
274 ovalue
->it_value
.tv_sec
= left_us
/ 1000000;
275 ovalue
->it_value
.tv_nsec
= (left_us
% 1000000) * 1000;
280 timer_gettime (timer_t timerid
, struct itimerspec
*ovalue
)
283 if (efault
.faulted (EFAULT
))
286 timer_tracker
*tt
= (timer_tracker
*) timerid
;
287 if (tt
->magic
!= TT_MAGIC
)
293 tt
->gettime (ovalue
);
298 timer_create (clockid_t clock_id
, struct sigevent
*evp
, timer_t
*timerid
)
301 if (efault
.faulted (EFAULT
))
303 if (clock_id
!= CLOCK_REALTIME
)
309 *timerid
= (timer_t
) new timer_tracker (clock_id
, evp
);
314 timer_settime (timer_t timerid
, int flags
, const struct itimerspec
*value
,
315 struct itimerspec
*ovalue
)
317 timer_tracker
*tt
= (timer_tracker
*) timerid
;
319 if (efault
.faulted (EFAULT
))
321 if (tt
->magic
!= TT_MAGIC
)
327 return tt
->settime (flags
, value
, ovalue
);
331 timer_delete (timer_t timerid
)
333 timer_tracker
*in_tt
= (timer_tracker
*) timerid
;
335 if (efault
.faulted (EFAULT
))
337 if (in_tt
->magic
!= TT_MAGIC
)
343 lock_timer_tracker here
;
344 for (timer_tracker
*tt
= &ttstart
; tt
->next
!= NULL
; tt
= tt
->next
)
345 if (tt
->next
== in_tt
)
347 tt
->next
= in_tt
->next
;
356 fixup_timers_after_fork ()
358 ttstart
.hcancel
= ttstart
.syncthread
= NULL
;
359 for (timer_tracker
*tt
= &ttstart
; tt
->next
!= NULL
; /* nothing */)
361 timer_tracker
*deleteme
= tt
->next
;
362 tt
->next
= deleteme
->next
;
363 deleteme
->hcancel
= deleteme
->syncthread
= NULL
;
370 setitimer (int which
, const struct itimerval
*value
, struct itimerval
*ovalue
)
372 if (which
!= ITIMER_REAL
)
377 struct itimerspec spec_value
, spec_ovalue
;
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
);
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;
391 syscall_printf ("%d = setitimer ()", ret
);
397 getitimer (int which
, struct itimerval
*ovalue
)
399 if (which
!= ITIMER_REAL
)
405 if (efault
.faulted (EFAULT
))
407 struct itimerspec spec_ovalue
;
408 int ret
= timer_gettime ((timer_t
) &ttstart
, &spec_ovalue
);
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;
416 syscall_printf ("%d = getitimer ()", ret
);
420 /* FIXME: POSIX - alarm survives exec */
421 extern "C" unsigned int
422 alarm (unsigned int seconds
)
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
);
436 extern "C" useconds_t
437 ualarm (useconds_t value
, useconds_t interval
)
439 struct itimerspec timer
= {}, otimer
;
440 /* ualarm cannot fail.
441 Interpret negative arguments as zero */
444 timer
.it_value
.tv_sec
= (unsigned int) value
/ 1000000;
445 timer
.it_value
.tv_nsec
= ((unsigned int) value
% 1000000) * 1000;
449 timer
.it_interval
.tv_sec
= (unsigned int) interval
/ 1000000;
450 timer
.it_interval
.tv_nsec
= ((unsigned int) interval
% 1000000) * 1000;
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
);