mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-01-15 11:00:04 +08:00
267e201dae
cygthread to handle extra argument, throughout. * cygthread.h (cygthread::callproc): Declare new method. (cygthread::cygthread): Add optional length argument to allow copying arguments to executing thread. * cygthread.cc (cygthread::callproc): Define new method. (cygthread::stub): Use callfunc to invoke thread func to allow potentially allocating stack memory which will be returned. (cygthread::simplestub): Ditto. (cygthread::cygthread): Accept arglen argument. Reset ev here prior to activating thread. Wait for ev after activating thread if we're copying contents to the thread. Wait until the end before setting h, to allow thread synchronization. (cygthread::release): Don't reset ev here. Rely on that happening the next time the thread is activated. * pinfo.h (commune_process): Rename declaration from _pinfo::commune_process. * pinfo.cc (commune_process): Ditto for definition. Modify slightly to allow running as a separate cygthread. * sigproc.cc (child_info::sync): Always wait for both subproc_ready and any hProcess if we have a cygwin parent. (talktome): Change argument to be a pointer to siginfo_t. Contiguously allocate whole siginfo_t structure + any needed extra for eventual passing to commune_process thread. (wait_sig): Accommodate change in talktome argument. * pipe.cc (fhandler_pipe::fixup_after_exec): Remove debugging.
449 lines
10 KiB
C++
449 lines
10 KiB
C++
/* timer.cc
|
|
|
|
Copyright 2004, 2005 Red Hat, Inc.
|
|
|
|
This file is part of Cygwin.
|
|
|
|
This software is a copyrighted work licensed under the terms of the
|
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
|
details. */
|
|
|
|
#include "winsup.h"
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include "cygerrno.h"
|
|
#include "security.h"
|
|
#include "hires.h"
|
|
#include "thread.h"
|
|
#include "cygtls.h"
|
|
#include "sigproc.h"
|
|
#include "sync.h"
|
|
|
|
#define TT_MAGIC 0x513e4a1c
|
|
struct timer_tracker
|
|
{
|
|
unsigned magic;
|
|
clockid_t clock_id;
|
|
sigevent evp;
|
|
timespec it_interval;
|
|
HANDLE hcancel;
|
|
HANDLE syncthread;
|
|
long long interval_us;
|
|
long long sleepto_us;
|
|
bool cancel ();
|
|
struct timer_tracker *next;
|
|
int settime (int, const itimerspec *, itimerspec *);
|
|
void gettime (itimerspec *);
|
|
timer_tracker (clockid_t, const sigevent *);
|
|
~timer_tracker ();
|
|
friend void fixup_timers_after_fork ();
|
|
};
|
|
|
|
timer_tracker NO_COPY ttstart (CLOCK_REALTIME, NULL);
|
|
|
|
class lock_timer_tracker
|
|
{
|
|
static muto protect;
|
|
public:
|
|
lock_timer_tracker ();
|
|
~lock_timer_tracker ();
|
|
};
|
|
|
|
muto NO_COPY lock_timer_tracker::protect;
|
|
|
|
lock_timer_tracker::lock_timer_tracker ()
|
|
{
|
|
protect.init ("timer_protect")->acquire ();
|
|
}
|
|
|
|
lock_timer_tracker::~lock_timer_tracker ()
|
|
{
|
|
protect.release ();
|
|
}
|
|
|
|
bool
|
|
timer_tracker::cancel ()
|
|
{
|
|
if (!hcancel)
|
|
return false;
|
|
|
|
SetEvent (hcancel);
|
|
if (WaitForSingleObject (syncthread, INFINITE) != WAIT_OBJECT_0)
|
|
api_fatal ("WFSO failed waiting for timer thread, %E");
|
|
return true;
|
|
}
|
|
|
|
timer_tracker::~timer_tracker ()
|
|
{
|
|
if (cancel ())
|
|
{
|
|
CloseHandle (hcancel);
|
|
#ifdef DEBUGGING
|
|
hcancel = NULL;
|
|
#endif
|
|
}
|
|
if (syncthread)
|
|
CloseHandle (syncthread);
|
|
magic = 0;
|
|
}
|
|
|
|
timer_tracker::timer_tracker (clockid_t c, const sigevent *e)
|
|
{
|
|
if (e != NULL)
|
|
evp = *e;
|
|
else
|
|
{
|
|
evp.sigev_notify = SIGEV_SIGNAL;
|
|
evp.sigev_signo = SIGALRM;
|
|
evp.sigev_value.sival_ptr = this;
|
|
}
|
|
clock_id = c;
|
|
magic = TT_MAGIC;
|
|
hcancel = NULL;
|
|
if (this != &ttstart)
|
|
{
|
|
lock_timer_tracker here;
|
|
next = ttstart.next;
|
|
ttstart.next = this;
|
|
}
|
|
}
|
|
|
|
static long long
|
|
to_us (const timespec& ts)
|
|
{
|
|
long long res = ts.tv_sec;
|
|
res *= 1000000;
|
|
res += ts.tv_nsec / 1000 + ((ts.tv_nsec % 1000) ? 1 : 0);
|
|
return res;
|
|
}
|
|
|
|
static DWORD WINAPI
|
|
timer_thread (VOID *x)
|
|
{
|
|
timer_tracker *tt = ((timer_tracker *) x);
|
|
long long now;
|
|
long long sleepto_us = tt->sleepto_us;
|
|
while (1)
|
|
{
|
|
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
|
|
{
|
|
tt->sleepto_us = now;
|
|
sleep_ms = 0;
|
|
}
|
|
|
|
debug_printf ("%p waiting for %u ms", x, sleep_ms);
|
|
switch (WaitForSingleObject (tt->hcancel, sleep_ms))
|
|
{
|
|
case WAIT_TIMEOUT:
|
|
debug_printf ("timed out");
|
|
break;
|
|
case WAIT_OBJECT_0:
|
|
debug_printf ("%p cancelled", x);
|
|
goto out;
|
|
default:
|
|
debug_printf ("%p wait failed, %E", x);
|
|
goto out;
|
|
}
|
|
|
|
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 (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);
|
|
if (rc)
|
|
{
|
|
debug_printf ("thread creation failed, %E");
|
|
return 0;
|
|
}
|
|
// FIXME: pthread_join?
|
|
break;
|
|
}
|
|
}
|
|
if (!tt->interval_us)
|
|
break;
|
|
|
|
sleepto_us = tt->sleepto_us + tt->interval_us;
|
|
debug_printf ("looping");
|
|
}
|
|
|
|
out:
|
|
_my_tls._ctinfo->auto_release (); /* automatically return the cygthread to the cygthread pool */
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
it_bad (const timespec& t)
|
|
{
|
|
if (t.tv_nsec < 0 || t.tv_nsec >= 1000000000 || t.tv_sec < 0)
|
|
{
|
|
set_errno (EINVAL);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int
|
|
timer_tracker::settime (int in_flags, const itimerspec *value, itimerspec *ovalue)
|
|
{
|
|
if (!value)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
myfault efault;
|
|
if (efault.faulted (EFAULT)
|
|
|| it_bad (value->it_value)
|
|
|| it_bad (value->it_interval))
|
|
return -1;
|
|
|
|
long long now = in_flags & TIMER_ABSTIME ? 0 : gtod.usecs (false);
|
|
|
|
lock_timer_tracker here;
|
|
cancel ();
|
|
|
|
if (ovalue)
|
|
gettime (ovalue);
|
|
|
|
if (!value->it_value.tv_sec && !value->it_value.tv_nsec)
|
|
interval_us = sleepto_us = 0;
|
|
else
|
|
{
|
|
sleepto_us = now + to_us (value->it_value);
|
|
interval_us = to_us (value->it_interval);
|
|
it_interval = value->it_interval;
|
|
if (!hcancel)
|
|
hcancel = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
|
|
else
|
|
ResetEvent (hcancel);
|
|
if (!syncthread)
|
|
syncthread = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
|
|
else
|
|
ResetEvent (syncthread);
|
|
new cygthread (timer_thread, 0, this, "itimer", syncthread);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
timer_tracker::gettime (itimerspec *ovalue)
|
|
{
|
|
if (!hcancel)
|
|
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_gettime (timer_t timerid, struct itimerspec *ovalue)
|
|
{
|
|
myfault efault;
|
|
if (efault.faulted (EFAULT))
|
|
return -1;
|
|
|
|
timer_tracker *tt = (timer_tracker *) timerid;
|
|
if (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)
|
|
{
|
|
myfault efault;
|
|
if (efault.faulted (EFAULT))
|
|
return -1;
|
|
if (clock_id != CLOCK_REALTIME)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
*timerid = (timer_t) new timer_tracker (clock_id, evp);
|
|
return 0;
|
|
}
|
|
|
|
extern "C" int
|
|
timer_settime (timer_t timerid, int flags, const struct itimerspec *value,
|
|
struct itimerspec *ovalue)
|
|
{
|
|
timer_tracker *tt = (timer_tracker *) timerid;
|
|
myfault efault;
|
|
if (efault.faulted (EFAULT))
|
|
return -1;
|
|
if (tt->magic != TT_MAGIC)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
return tt->settime (flags, value, ovalue);
|
|
}
|
|
|
|
extern "C" int
|
|
timer_delete (timer_t timerid)
|
|
{
|
|
timer_tracker *in_tt = (timer_tracker *) timerid;
|
|
myfault efault;
|
|
if (efault.faulted (EFAULT))
|
|
return -1;
|
|
if (in_tt->magic != TT_MAGIC)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
lock_timer_tracker here;
|
|
for (timer_tracker *tt = &ttstart; tt->next != NULL; tt = tt->next)
|
|
if (tt->next == in_tt)
|
|
{
|
|
tt->next = in_tt->next;
|
|
delete in_tt;
|
|
return 0;
|
|
}
|
|
set_errno (EINVAL);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
fixup_timers_after_fork ()
|
|
{
|
|
ttstart.hcancel = ttstart.syncthread = NULL;
|
|
for (timer_tracker *tt = &ttstart; tt->next != NULL; /* nothing */)
|
|
{
|
|
timer_tracker *deleteme = tt->next;
|
|
tt->next = deleteme->next;
|
|
deleteme->hcancel = 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;
|
|
}
|
|
myfault efault;
|
|
if (efault.faulted (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;
|
|
}
|