diff options
author | Christopher Faylor <me@cgf.cx> | 2005-03-27 01:57:38 +0000 |
---|---|---|
committer | Christopher Faylor <me@cgf.cx> | 2005-03-27 01:57:38 +0000 |
commit | ec98d19a08c2e4678e8a6f40fea0c9bbeaa4a2c7 (patch) | |
tree | 667b5bf4984c1caa5b3f7df1b2b296e16c394c73 /winsup/cygwin/timer.cc | |
parent | 9eba97c0d5d723ce151b38d4bc78bd6fe097336c (diff) | |
download | newlib-ec98d19a08c2e4678e8a6f40fea0c9bbeaa4a2c7.zip newlib-ec98d19a08c2e4678e8a6f40fea0c9bbeaa4a2c7.tar.gz newlib-ec98d19a08c2e4678e8a6f40fea0c9bbeaa4a2c7.tar.bz2 |
* wininfo.h (wininfo::timer_active): Delete.
(wininfo::itv): Ditto.
(wininfo::start_time): Ditto.
(wininfo::window_started): Ditto.
(wininfo::getitimer): Ditto.
(wininfo::setitimer): Ditto.
(wininfo::wininfo): Ditto.
(wininfo::lock): New method.
(wininfo::release): Ditto.
* window.cc: Use new lock/acquire wininfo methods throughout.
(wininfo::wininfo): Delete
(wininfo::getitimer): Ditto.
(wininfo::setitimer): Ditto.
(getitimer): Ditto.
(setitimer): Ditto.
(ualarm): Ditto.
(alarm): Ditto.
(wininfo::lock): Define new function.
(wininfo::release): Ditto.
(wininfo::process): Delete WM_TIMER handling.
* timer.cc (struct timetracker): Delete it, flags. Add it_interval,
interval_us, sleepto_us, running, init_muto(), syncthread, and gettime().
(ttstart): Make NO_COPY.
(lock_timer_tracker): New class.
(timer_tracker::timer_tracker): Distinguish ttstart case.
(timer_tracker::~timer_tracker): New destructor. Clean out events, and reset
magic.
(timer_tracker::init_muto): New method.
(to_us): Round up as per POSIX.
(timer_thread): Reorganize to match timer_tracker::settime and
timer_tracker::gettime. Call sig_send without wait. Call auto_release.
(timer_tracker::settime): Reorganize logic to avoid race. Call gettime to
recover old value.
(timer_tracker::gettime): New method.
(timer_create): Properly set errno on invalid timerid. Use new
lock_timer_tracker method.
(timer_delete): Ditto. Simplify code slightly.
(timer_gettime): New function.
(fixup_timers_after_fork): Reinit ttstart.
(getitimer): New implementation.
(setitimer): Ditto.
(ualarm): Ditto.
(alarm): Ditto.
* cygwin.din: Export timer_gettime.
* winsup.h: Remove has has_visible_window_station declaration.
* Makefile.in (DLL_OFILES): Add lsearch.o.
* cygthread.h (cygthread::notify_detached): New element.
(cygthread::cygthread): Take optional fourth argument signifying event to
signal on thread completion.
* cygthread.cc (cygthread::stub): Signal notify_detached event, if it exists.
(cygthread::cygthread): Initialize notify_detached from fourth argument.
(cygthread::detach): Wait for notify_detached field is present.
* lsearch.cc: New file.
* search.h: Ditto.
* include/cygwin/version.h: Bump API minor number to 126.
* cygwin.din: Export lsearch, lfind.
Diffstat (limited to 'winsup/cygwin/timer.cc')
-rw-r--r-- | winsup/cygwin/timer.cc | 337 |
1 files changed, 250 insertions, 87 deletions
diff --git a/winsup/cygwin/timer.cc b/winsup/cygwin/timer.cc index 6a53a57..1ab6616 100644 --- a/winsup/cygwin/timer.cc +++ b/winsup/cygwin/timer.cc @@ -22,27 +22,61 @@ details. */ #define TT_MAGIC 0x513e4a1c struct timer_tracker { - static muto *protect; unsigned magic; clockid_t clock_id; sigevent evp; - itimerspec it; + timespec it_interval; HANDLE cancel; - int flags; + HANDLE syncthread; + long long interval_us; + long long sleepto_us; cygthread *th; struct timer_tracker *next; int settime (int, const itimerspec *, itimerspec *); + void gettime (itimerspec *); timer_tracker (clockid_t, const sigevent *); - timer_tracker (); + timer_tracker () {}; + ~timer_tracker (); + friend void fixup_timers_after_fork (); }; -timer_tracker ttstart; +timer_tracker NO_COPY ttstart; -muto *timer_tracker::protect; +class lock_timer_tracker +{ + static muto *protect; +public: + lock_timer_tracker (); + ~lock_timer_tracker (); +}; -timer_tracker::timer_tracker () +muto NO_COPY *lock_timer_tracker::protect; + +lock_timer_tracker::lock_timer_tracker () { - new_muto (protect); + new_muto (protect)->acquire (); +} + +lock_timer_tracker::~lock_timer_tracker () +{ + protect->release (); +} + +timer_tracker::~timer_tracker () +{ + if (cancel) + { + SetEvent (cancel); + th->detach (); + CloseHandle (cancel); +#ifdef DEBUGGING + th = NULL; + cancel = NULL; +#endif + } + if (syncthread) + CloseHandle (syncthread); + magic = 0; } timer_tracker::timer_tracker (clockid_t c, const sigevent *e) @@ -56,84 +90,83 @@ timer_tracker::timer_tracker (clockid_t c, const sigevent *e) evp.sigev_value.sival_ptr = this; } clock_id = c; - cancel = NULL; - flags = 0; - memset (&it, 0, sizeof (it)); - protect->acquire (); - next = ttstart.next; - ttstart.next = this; - protect->release (); magic = TT_MAGIC; + if (this != &ttstart) + { + cancel = NULL; + lock_timer_tracker here; + next = ttstart.next; + ttstart.next = this; + } } static long long -to_us (timespec& ts) +to_us (const timespec& ts) { long long res = ts.tv_sec; res *= 1000000; - res += ts.tv_nsec / 1000 + ((ts.tv_nsec % 1000) >= 500 ? 1 : 0); + res += ts.tv_nsec / 1000 + ((ts.tv_nsec % 1000) ? 1 : 0); return res; } -static NO_COPY itimerspec itzero; -static NO_COPY timespec tzero; - static DWORD WINAPI timer_thread (VOID *x) { - timer_tracker *tp = ((timer_tracker *) x); - timer_tracker tt = *tp; - for (bool first = true; ; first = false) + timer_tracker *tt = ((timer_tracker *) x); + long long now; + long long sleepto_us = tt->sleepto_us; + while (1) { - long long sleep_us = to_us (first ? tt.it.it_value : tt.it.it_interval); - long long sleep_to = sleep_us; - long long now = gtod.usecs (false); - if (tt.flags & TIMER_ABSTIME) - sleep_us -= now; + long long sleep_us; + long sleep_ms; + /* Account for delays in starting thread + and sending the signal */ + now = gtod.usecs (false); + sleep_us = sleepto_us - now; + if (sleep_us > 0) + { + tt->sleepto_us = sleepto_us; + sleep_ms = (sleep_us + 999) / 1000; + } else - sleep_to += now; + { + tt->sleepto_us = now; + sleep_ms = 0; + } - DWORD sleep_ms = (sleep_us < 0) ? 0 : (sleep_us / 1000); - debug_printf ("%p waiting for %u ms, first %d", x, sleep_ms, first); - tp->it.it_value = tzero; - switch (WaitForSingleObject (tt.cancel, sleep_ms)) + debug_printf ("%p waiting for %u ms", x, sleep_ms); + switch (WaitForSingleObject (tt->cancel, sleep_ms)) { case WAIT_TIMEOUT: debug_printf ("timed out"); break; case WAIT_OBJECT_0: - now = gtod.usecs (false); - sleep_us = sleep_to - now; - if (sleep_us < 0) - sleep_us = 0; - tp->it.it_value.tv_sec = sleep_us / 1000000; - tp->it.it_value.tv_nsec = (sleep_us % 1000000) * 1000; - debug_printf ("%p cancelled, elapsed %D", x, sleep_us); + debug_printf ("%p cancelled", x); goto out; default: - debug_printf ("%p timer wait failed, %E", x); + debug_printf ("%p wait failed, %E", x); goto out; } - switch (tt.evp.sigev_notify) + switch (tt->evp.sigev_notify) { case SIGEV_SIGNAL: { siginfo_t si; memset (&si, 0, sizeof (si)); - si.si_signo = tt.evp.sigev_signo; - si.si_sigval.sival_ptr = tt.evp.sigev_value.sival_ptr; - debug_printf ("%p sending sig %d", x, tt.evp.sigev_signo); - sig_send (NULL, si); + si.si_signo = tt->evp.sigev_signo; + si.si_sigval.sival_ptr = tt->evp.sigev_value.sival_ptr; + debug_printf ("%p sending sig %d", x, tt->evp.sigev_signo); + sig_send (myself_nowait, si); break; } case SIGEV_THREAD: { pthread_t notify_thread; debug_printf ("%p starting thread", x); - int rc = pthread_create (¬ify_thread, tt.evp.sigev_notify_attributes, - (void * (*) (void *)) tt.evp.sigev_notify_function, - tt.evp.sigev_value.sival_ptr); + int rc = pthread_create (¬ify_thread, tt->evp.sigev_notify_attributes, + (void * (*) (void *)) tt->evp.sigev_notify_function, + tt->evp.sigev_value.sival_ptr); if (rc) { debug_printf ("thread creation failed, %E"); @@ -143,17 +176,15 @@ timer_thread (VOID *x) break; } } - if (!tt.it.it_interval.tv_sec && !tt.it.it_interval.tv_nsec) + if (!tt->interval_us) break; - tt.flags = 0; + + sleepto_us = tt->sleepto_us + tt->interval_us; debug_printf ("looping"); } out: - CloseHandle (tt.cancel); - // FIXME: race here but is it inevitable? - if (tt.cancel == tp->cancel) - tp->cancel = NULL; + _my_tls._ctinfo->auto_release (); /* automatically return the cygthread to the cygthread pool */ return 0; } @@ -177,48 +208,83 @@ timer_tracker::settime (int in_flags, const itimerspec *value, itimerspec *ovalu return -1; } - if (__check_invalid_read_ptr_errno (value, sizeof (*value))) + if (__check_invalid_read_ptr_errno (value, sizeof (*value)) + || it_bad (value->it_value) + || it_bad (value->it_interval) + || (ovalue && check_null_invalid_struct_errno (ovalue))) return -1; - if (ovalue && check_null_invalid_struct_errno (ovalue)) - return -1; + long long now = in_flags & TIMER_ABSTIME ? 0 : gtod.usecs (false); - itimerspec *elapsed; - if (!cancel) - elapsed = &itzero; - else + lock_timer_tracker here; + if (cancel) { SetEvent (cancel); // should be closed when the thread exits th->detach (); - elapsed = ⁢ } if (ovalue) - *ovalue = *elapsed; + gettime (ovalue); if (value->it_value.tv_sec || value->it_value.tv_nsec) { - if (it_bad (value->it_value)) - return -1; - if (it_bad (value->it_interval)) - return -1; - flags = in_flags; - cancel = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); - it = *value; - th = new cygthread (timer_thread, this, "itimer"); + sleepto_us = now + to_us (value->it_value); + interval_us = to_us (value->it_interval); + it_interval = value->it_interval; + if (!cancel) + cancel = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); + else + ResetEvent (cancel); + if (!syncthread) + syncthread = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); + else + ResetEvent (syncthread); + th = new cygthread (timer_thread, this, "itimer", syncthread); } return 0; } +void +timer_tracker::gettime (itimerspec *ovalue) +{ + if (!cancel) + memset (ovalue, 0, sizeof (*ovalue)); + else + { + ovalue->it_interval = it_interval; + long long now = gtod.usecs (false); + long long left_us = sleepto_us - now; + if (left_us < 0) + left_us = 0; + ovalue->it_value.tv_sec = left_us / 1000000; + ovalue->it_value.tv_nsec = (left_us % 1000000) * 1000; + } +} + extern "C" int -timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid) +timer_gettime (timer_t timerid, struct itimerspec *ovalue) { - if (evp && check_null_invalid_struct_errno (evp)) - return -1; - if (check_null_invalid_struct_errno (timerid)) + if (check_null_invalid_struct_errno (ovalue)) return -1; + timer_tracker *tt = (timer_tracker *) timerid; + if (check_null_invalid_struct (tt) || tt->magic != TT_MAGIC) + { + set_errno (EINVAL); + return -1; + } + + tt->gettime (ovalue); + return 0; +} + +extern "C" int +timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid) +{ + if (evp && check_null_invalid_struct_errno (evp) + || check_null_invalid_struct_errno (timerid)) + return -1; if (clock_id != CLOCK_REALTIME) { set_errno (EINVAL); @@ -234,8 +300,12 @@ timer_settime (timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue) { timer_tracker *tt = (timer_tracker *) timerid; - if (check_null_invalid_struct_errno (tt) || tt->magic != TT_MAGIC) - return -1; + if (check_null_invalid_struct (tt) || tt->magic != TT_MAGIC) + { + set_errno (EINVAL); + return -1; + } + return tt->settime (flags, value, ovalue); } @@ -243,21 +313,20 @@ extern "C" int timer_delete (timer_t timerid) { timer_tracker *in_tt = (timer_tracker *) timerid; - if (check_null_invalid_struct_errno (in_tt) || in_tt->magic != TT_MAGIC) - return -1; + if (check_null_invalid_struct (in_tt) || in_tt->magic != TT_MAGIC) + { + set_errno (EINVAL); + return -1; + } - timer_tracker::protect->acquire (); + lock_timer_tracker here; for (timer_tracker *tt = &ttstart; tt->next != NULL; tt = tt->next) if (tt->next == in_tt) { - timer_tracker *deleteme = tt->next; - tt->next = deleteme->next; - delete deleteme; - timer_tracker::protect->release (); + tt->next = in_tt->next; + delete in_tt; return 0; } - timer_tracker::protect->release (); - set_errno (EINVAL); return 0; } @@ -265,10 +334,104 @@ timer_delete (timer_t timerid) void fixup_timers_after_fork () { + ttstart.cancel = ttstart.syncthread = NULL; for (timer_tracker *tt = &ttstart; tt->next != NULL; /* nothing */) { timer_tracker *deleteme = tt->next; tt->next = deleteme->next; + deleteme->cancel = deleteme->syncthread = NULL; delete deleteme; } } + + +extern "C" int +setitimer (int which, const struct itimerval *value, struct itimerval *ovalue) +{ + if (which != ITIMER_REAL) + { + set_errno (EINVAL); + return -1; + } + struct itimerspec spec_value, spec_ovalue; + int ret; + spec_value.it_interval.tv_sec = value->it_interval.tv_sec; + spec_value.it_interval.tv_nsec = value->it_interval.tv_usec * 1000; + spec_value.it_value.tv_sec = value->it_value.tv_sec; + spec_value.it_value.tv_nsec = value->it_value.tv_usec * 1000; + ret = timer_settime ((timer_t) &ttstart, 0, &spec_value, &spec_ovalue); + if (!ret && ovalue) + { + ovalue->it_interval.tv_sec = spec_ovalue.it_interval.tv_sec; + ovalue->it_interval.tv_usec = spec_ovalue.it_interval.tv_nsec / 1000; + ovalue->it_value.tv_sec = spec_ovalue.it_value.tv_sec; + ovalue->it_value.tv_usec = spec_ovalue.it_value.tv_nsec / 1000; + } + syscall_printf ("%d = setitimer ()", ret); + return ret; +} + + +extern "C" int +getitimer (int which, struct itimerval *ovalue) +{ + if (which != ITIMER_REAL) + { + set_errno (EINVAL); + return -1; + } + if (ovalue == NULL) + { + set_errno (EFAULT); + return -1; + } + struct itimerspec spec_ovalue; + int ret = timer_gettime ((timer_t) &ttstart, &spec_ovalue); + if (!ret) + { + ovalue->it_interval.tv_sec = spec_ovalue.it_interval.tv_sec; + ovalue->it_interval.tv_usec = spec_ovalue.it_interval.tv_nsec / 1000; + ovalue->it_value.tv_sec = spec_ovalue.it_value.tv_sec; + ovalue->it_value.tv_usec = spec_ovalue.it_value.tv_nsec / 1000; + } + syscall_printf ("%d = getitimer ()", ret); + return ret; +} + +/* FIXME: POSIX - alarm survives exec */ +extern "C" unsigned int +alarm (unsigned int seconds) +{ + struct itimerspec newt = {}, oldt; + /* alarm cannot fail, but only needs not be + correct for arguments < 64k. Truncate */ + if (seconds > (HIRES_DELAY_MAX / 1000 - 1)) + seconds = (HIRES_DELAY_MAX / 1000 - 1); + newt.it_value.tv_sec = seconds; + timer_settime ((timer_t) &ttstart, 0, &newt, &oldt); + int ret = oldt.it_value.tv_sec + (oldt.it_value.tv_nsec > 0); + syscall_printf ("%d = alarm (%d)", ret, seconds); + return ret; +} + +extern "C" useconds_t +ualarm (useconds_t value, useconds_t interval) +{ + struct itimerspec timer = {}, otimer; + /* ualarm cannot fail. + Interpret negative arguments as zero */ + if (value > 0) + { + timer.it_value.tv_sec = (unsigned int) value / 1000000; + timer.it_value.tv_nsec = ((unsigned int) value % 1000000) * 1000; + } + if (interval > 0) + { + timer.it_interval.tv_sec = (unsigned int) interval / 1000000; + timer.it_interval.tv_nsec = ((unsigned int) interval % 1000000) * 1000; + } + timer_settime ((timer_t) &ttstart, 0, &timer, &otimer); + useconds_t ret = otimer.it_value.tv_sec * 1000000 + (otimer.it_value.tv_nsec + 999) / 1000; + syscall_printf ("%d = ualarm (%d , %d)", ret, value, interval); + return ret; +} |