Cygwin: timers: implement timerfd
First cut of a timerfd implementation. Still TODO: - fork/exec semantics - timerfd_settime TFD_TIMER_CANCEL_ON_SET flag - ioctl(TFD_IOC_SET_TICKS) - bug fixes Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
parent
b6f53617a7
commit
068182e26c
|
@ -305,6 +305,7 @@ DLL_OFILES:= \
|
|||
fhandler_socket_unix.o \
|
||||
fhandler_tape.o \
|
||||
fhandler_termios.o \
|
||||
fhandler_timerfd.o \
|
||||
fhandler_tty.o \
|
||||
fhandler_virtual.o \
|
||||
fhandler_windows.o \
|
||||
|
|
|
@ -1488,6 +1488,9 @@ timer_delete SIGFE
|
|||
timer_getoverrun SIGFE
|
||||
timer_gettime SIGFE
|
||||
timer_settime SIGFE
|
||||
timerfd_create SIGFE
|
||||
timerfd_gettime SIGFE
|
||||
timerfd_settime SIGFE
|
||||
times SIGFE
|
||||
timezone SIGFE
|
||||
timingsafe_bcmp NOSIGFE
|
||||
|
|
|
@ -123,6 +123,9 @@ const _device dev_pipew_storage =
|
|||
const _device dev_signalfd_storage =
|
||||
{"", {FH_SIGNALFD}, "", exists_internal};
|
||||
|
||||
const _device dev_timerfd_storage =
|
||||
{"", {FH_TIMERFD}, "", exists_internal};
|
||||
|
||||
const _device dev_socket_storage =
|
||||
{"", {FH_SOCKET}, "", exists_internal};
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ enum fh_devices
|
|||
FH_CYGDRIVE= FHDEV (DEV_VIRTFS_MAJOR, 192),
|
||||
|
||||
FH_SIGNALFD= FHDEV (DEV_VIRTFS_MAJOR, 13),
|
||||
FH_TIMERFD = FHDEV (DEV_VIRTFS_MAJOR, 14),
|
||||
|
||||
DEV_FLOPPY_MAJOR = 2,
|
||||
FH_FLOPPY = FHDEV (DEV_FLOPPY_MAJOR, 0),
|
||||
|
@ -404,6 +405,8 @@ extern const _device dev_af_unix_storage;
|
|||
|
||||
extern const _device dev_signalfd_storage;
|
||||
#define signalfd_dev ((device *) &dev_signalfd_storage)
|
||||
extern const _device dev_timerfd_storage;
|
||||
#define timerfd_dev ((device *) &dev_timerfd_storage)
|
||||
extern const _device dev_piper_storage;
|
||||
#define piper_dev ((device *) &dev_piper_storage)
|
||||
extern const _device dev_pipew_storage;
|
||||
|
|
|
@ -119,6 +119,9 @@ const _device dev_pipew_storage =
|
|||
const _device dev_signalfd_storage =
|
||||
{"", {FH_SIGNALFD}, "", exists_internal};
|
||||
|
||||
const _device dev_timerfd_storage =
|
||||
{"", {FH_TIMERFD}, "", exists_internal};
|
||||
|
||||
const _device dev_socket_storage =
|
||||
{"", {FH_SOCKET}, "", exists_internal};
|
||||
|
||||
|
|
|
@ -578,6 +578,9 @@ fh_alloc (path_conv& pc)
|
|||
case FH_SIGNALFD:
|
||||
fh = cnew (fhandler_signalfd);
|
||||
break;
|
||||
case FH_TIMERFD:
|
||||
fh = cnew (fhandler_timerfd);
|
||||
break;
|
||||
case FH_TTY:
|
||||
if (!pc.isopen ())
|
||||
{
|
||||
|
|
|
@ -421,6 +421,7 @@ public:
|
|||
virtual class fhandler_socket_wsock *is_wsock_socket () { return NULL; }
|
||||
virtual class fhandler_console *is_console () { return 0; }
|
||||
virtual class fhandler_signalfd *is_signalfd () { return NULL; }
|
||||
virtual class fhandler_timerfd *is_timerfd () { return NULL; }
|
||||
virtual int is_windows () {return 0; }
|
||||
|
||||
virtual void __reg3 raw_read (void *ptr, size_t& ulen);
|
||||
|
@ -2673,6 +2674,55 @@ class fhandler_signalfd : public fhandler_base
|
|||
}
|
||||
};
|
||||
|
||||
class fhandler_timerfd : public fhandler_base
|
||||
{
|
||||
timer_t timerid;
|
||||
|
||||
public:
|
||||
fhandler_timerfd ();
|
||||
fhandler_timerfd (void *) {}
|
||||
|
||||
fhandler_timerfd *is_timerfd () { return this; }
|
||||
|
||||
char *get_proc_fd_name (char *buf);
|
||||
|
||||
int timerfd (clockid_t clock_id, int flags);
|
||||
int settime (int flags, const struct itimerspec *value,
|
||||
struct itimerspec *ovalue);
|
||||
int gettime (struct itimerspec *ovalue);
|
||||
|
||||
int __reg2 fstat (struct stat *buf);
|
||||
void __reg3 read (void *ptr, size_t& len);
|
||||
int dup (fhandler_base *child, int);
|
||||
int ioctl (unsigned int, void *);
|
||||
int close ();
|
||||
|
||||
HANDLE get_timerfd_handle ();
|
||||
|
||||
void fixup_after_fork_exec (bool);
|
||||
void fixup_after_exec () {fixup_after_fork_exec (true);}
|
||||
void fixup_after_fork (HANDLE) {fixup_after_fork_exec (false);}
|
||||
|
||||
select_record *select_read (select_stuff *);
|
||||
select_record *select_write (select_stuff *);
|
||||
select_record *select_except (select_stuff *);
|
||||
|
||||
void copyto (fhandler_base *x)
|
||||
{
|
||||
x->pc.free_strings ();
|
||||
*reinterpret_cast<fhandler_timerfd *> (x) = *this;
|
||||
x->reset (this);
|
||||
}
|
||||
|
||||
fhandler_timerfd *clone (cygheap_types malloc_type = HEAP_FHANDLER)
|
||||
{
|
||||
void *ptr = (void *) ccalloc (malloc_type, 1, sizeof (fhandler_timerfd));
|
||||
fhandler_timerfd *fh = new (ptr) fhandler_timerfd (ptr);
|
||||
copyto (fh);
|
||||
return fh;
|
||||
}
|
||||
};
|
||||
|
||||
struct fhandler_nodevice: public fhandler_base
|
||||
{
|
||||
fhandler_nodevice ();
|
||||
|
@ -2713,6 +2763,7 @@ typedef union
|
|||
char __registry[sizeof (fhandler_registry)];
|
||||
char __serial[sizeof (fhandler_serial)];
|
||||
char __signalfd[sizeof (fhandler_signalfd)];
|
||||
char __timerfd[sizeof (fhandler_timerfd)];
|
||||
char __socket_inet[sizeof (fhandler_socket_inet)];
|
||||
char __socket_local[sizeof (fhandler_socket_local)];
|
||||
#ifdef __WITH_AF_UNIX
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
/* fhandler_timerfd.cc: fhandler for timerfd
|
||||
|
||||
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 "path.h"
|
||||
#include "fhandler.h"
|
||||
#include "pinfo.h"
|
||||
#include "dtable.h"
|
||||
#include "cygheap.h"
|
||||
#include "timer.h"
|
||||
#include <sys/timerfd.h>
|
||||
#include <cygwin/signal.h>
|
||||
|
||||
fhandler_timerfd::fhandler_timerfd () :
|
||||
fhandler_base ()
|
||||
{
|
||||
}
|
||||
|
||||
char *
|
||||
fhandler_timerfd::get_proc_fd_name (char *buf)
|
||||
{
|
||||
return strcpy (buf, "anon_inode:[timerfd]");
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_timerfd::timerfd (clockid_t clock_id, int flags)
|
||||
{
|
||||
timerid = (timer_t) new timer_tracker (clock_id, NULL, true);
|
||||
if (flags & TFD_NONBLOCK)
|
||||
set_nonblocking (true);
|
||||
if (flags & TFD_CLOEXEC)
|
||||
set_close_on_exec (true);
|
||||
if (get_unique_id () == 0)
|
||||
{
|
||||
nohandle (true);
|
||||
set_unique_id ();
|
||||
set_ino (get_unique_id ());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_timerfd::settime (int flags, const itimerspec *value,
|
||||
itimerspec *ovalue)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
__try
|
||||
{
|
||||
timer_tracker *tt = (timer_tracker *) timerid;
|
||||
ret = tt->settime (flags, value, ovalue);
|
||||
}
|
||||
__except (EFAULT) {}
|
||||
__endtry
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_timerfd::gettime (itimerspec *ovalue)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
__try
|
||||
{
|
||||
timer_tracker *tt = (timer_tracker *) timerid;
|
||||
tt->gettime (ovalue);
|
||||
ret = 0;
|
||||
}
|
||||
__except (EFAULT) {}
|
||||
__endtry
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __reg2
|
||||
fhandler_timerfd::fstat (struct stat *buf)
|
||||
{
|
||||
int ret = fhandler_base::fstat (buf);
|
||||
if (!ret)
|
||||
{
|
||||
buf->st_mode = S_IRUSR | S_IWUSR;
|
||||
buf->st_dev = FH_TIMERFD;
|
||||
buf->st_ino = get_unique_id ();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __reg3
|
||||
fhandler_timerfd::read (void *ptr, size_t& len)
|
||||
{
|
||||
if (len < sizeof (LONG64))
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
len = (size_t) -1;
|
||||
return;
|
||||
}
|
||||
|
||||
__try
|
||||
{
|
||||
timer_tracker *tt = (timer_tracker *) timerid;
|
||||
LONG64 ret = tt->wait (is_nonblocking ());
|
||||
if (ret == -1)
|
||||
__leave;
|
||||
PLONG64 pl64 = (PLONG64) ptr;
|
||||
*pl64 = ret + 1;
|
||||
len = sizeof (LONG64);
|
||||
return;
|
||||
}
|
||||
__except (EFAULT) {}
|
||||
__endtry
|
||||
len = (size_t) -1;
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE
|
||||
fhandler_timerfd::get_timerfd_handle ()
|
||||
{
|
||||
__try
|
||||
{
|
||||
timer_tracker *tt = (timer_tracker *) timerid;
|
||||
return tt->get_timerfd_handle ();
|
||||
}
|
||||
__except (EFAULT) {}
|
||||
__endtry
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_timerfd::dup (fhandler_base *child, int flags)
|
||||
{
|
||||
int ret = fhandler_base::dup (child, flags);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
fhandler_timerfd *fhc = (fhandler_timerfd *) child;
|
||||
__try
|
||||
{
|
||||
timer_tracker *tt = (timer_tracker *) fhc->timerid;
|
||||
tt->increment_instances ();
|
||||
ret = 0;
|
||||
}
|
||||
__except (EFAULT) {}
|
||||
__endtry
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
fhandler_timerfd::fixup_after_fork_exec (bool execing)
|
||||
{
|
||||
if (!execing)
|
||||
{
|
||||
/* TODO after fork */
|
||||
}
|
||||
else if (!close_on_exec ())
|
||||
{
|
||||
/* TODO after exec */
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_timerfd::ioctl (unsigned int cmd, void *p)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case TFD_IOC_SET_TICKS:
|
||||
/* TODO */
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
set_errno (EINVAL);
|
||||
break;
|
||||
}
|
||||
syscall_printf ("%d = ioctl_timerfd(%x, %p)", ret, cmd, p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_timerfd::close ()
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
__try
|
||||
{
|
||||
timer_tracker *tt = (timer_tracker *) timerid;
|
||||
timer_tracker::close (tt);
|
||||
ret = 0;
|
||||
}
|
||||
__except (EFAULT) {}
|
||||
__endtry
|
||||
return ret;
|
||||
}
|
|
@ -503,12 +503,13 @@ details. */
|
|||
CLOCK_BOOTTIME.
|
||||
331: Add timer_getoverrun, DELAYTIMER_MAX.
|
||||
332: Add signalfd.
|
||||
333: Add timerfd_create, timerfd_gettime, timerfd_settime.
|
||||
|
||||
Note that we forgot to bump the api for ualarm, strtoll, strtoull,
|
||||
sigaltstack, sethostname. */
|
||||
|
||||
#define CYGWIN_VERSION_API_MAJOR 0
|
||||
#define CYGWIN_VERSION_API_MINOR 332
|
||||
#define CYGWIN_VERSION_API_MINOR 333
|
||||
|
||||
/* There is also a compatibity version number associated with the shared memory
|
||||
regions. It is incremented when incompatible changes are made to the shared
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/* sys/timerfd.h: define timerfd_create(2) and friends
|
||||
|
||||
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. */
|
||||
|
||||
#ifndef _SYS_TIMERFD_H
|
||||
#define _SYS_TIMERFD_H
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/_default_fcntl.h>
|
||||
#include <asm/socket.h>
|
||||
|
||||
enum
|
||||
{
|
||||
/* timerfd_create */
|
||||
TFD_CLOEXEC = O_CLOEXEC,
|
||||
TFD_NONBLOCK = O_NONBLOCK,
|
||||
/* timerfd_settime */
|
||||
TFD_TIMER_ABSTIME = TIMER_ABSTIME,
|
||||
TFD_TIMER_CANCEL_ON_SET = (TIMER_ABSTIME << 1)
|
||||
};
|
||||
#define TFD_CLOEXEC TFD_CLOEXEC
|
||||
#define TFD_NONBLOCK TFD_NONBLOCK
|
||||
#define TFD_TIMER_ABSTIME TFD_TIMER_ABSTIME
|
||||
#define TFD_TIMER_CANCEL_ON_SET TFD_TIMER_CANCEL_ON_SET
|
||||
|
||||
#define TFD_IOC_SET_TICKS _IOW('T', 0, __uint64_t)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int timerfd_create (clockid_t, int);
|
||||
extern int timerfd_settime (int, int, const struct itimerspec *,
|
||||
struct itimerspec *);
|
||||
extern int timerfd_gettime (int, struct itimerspec *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_TIMERFD_H */
|
|
@ -27,7 +27,8 @@ What's new:
|
|||
- Support overrun counter for posix timers (via timer_getoverrun() or
|
||||
siginfo_t::si_overrun).
|
||||
|
||||
- New API: signalfd, timer_getoverrun.
|
||||
- New APIs: signalfd, timerfd_create, timerfd_gettime, timerfd_settime,
|
||||
timer_getoverrun.
|
||||
|
||||
|
||||
What changed:
|
||||
|
|
|
@ -1870,3 +1870,48 @@ fhandler_signalfd::select_except (select_stuff *stuff)
|
|||
s->except_ready = false;
|
||||
return s;
|
||||
}
|
||||
|
||||
select_record *
|
||||
fhandler_timerfd::select_read (select_stuff *stuff)
|
||||
{
|
||||
select_record *s = stuff->start.next;
|
||||
if (!s->startup)
|
||||
{
|
||||
s->startup = no_startup;
|
||||
s->verify = verify_ok;
|
||||
}
|
||||
s->h = get_timerfd_handle ();
|
||||
s->read_selected = true;
|
||||
s->read_ready = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
select_record *
|
||||
fhandler_timerfd::select_write (select_stuff *stuff)
|
||||
{
|
||||
select_record *s = stuff->start.next;
|
||||
if (!s->startup)
|
||||
{
|
||||
s->startup = no_startup;
|
||||
s->verify = no_verify;
|
||||
}
|
||||
s->peek = NULL;
|
||||
s->write_selected = false;
|
||||
s->write_ready = false;
|
||||
return s;
|
||||
}
|
||||
|
||||
select_record *
|
||||
fhandler_timerfd::select_except (select_stuff *stuff)
|
||||
{
|
||||
select_record *s = stuff->start.next;
|
||||
if (!s->startup)
|
||||
{
|
||||
s->startup = no_startup;
|
||||
s->verify = no_verify;
|
||||
}
|
||||
s->peek = NULL;
|
||||
s->except_selected = false;
|
||||
s->except_ready = false;
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -15,13 +15,14 @@ details. */
|
|||
#include "dtable.h"
|
||||
#include "cygheap.h"
|
||||
#include "timer.h"
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#define EVENT_DISARMED 0
|
||||
#define EVENT_ARMED -1
|
||||
#define EVENT_LOCK 1
|
||||
|
||||
timer_tracker NO_COPY ttstart (CLOCK_REALTIME, NULL);
|
||||
timer_tracker NO_COPY ttstart (CLOCK_REALTIME, NULL, false);
|
||||
|
||||
class lock_timer_tracker
|
||||
{
|
||||
|
@ -58,34 +59,45 @@ timer_tracker::cancel ()
|
|||
|
||||
timer_tracker::~timer_tracker ()
|
||||
{
|
||||
HANDLE hdl;
|
||||
|
||||
deleting = true;
|
||||
if (cancel ())
|
||||
{
|
||||
CloseHandle (hcancel);
|
||||
#ifdef DEBUGGING
|
||||
hcancel = NULL;
|
||||
#endif
|
||||
HANDLE hdl = InterlockedExchangePointer (&hcancel, NULL);
|
||||
CloseHandle (hdl);
|
||||
hdl = InterlockedExchangePointer (&timerfd_event, NULL);
|
||||
if (hdl)
|
||||
CloseHandle (hdl);
|
||||
}
|
||||
if (syncthread)
|
||||
CloseHandle (syncthread);
|
||||
hdl = InterlockedExchangePointer (&syncthread, NULL);
|
||||
if (hdl)
|
||||
CloseHandle (hdl);
|
||||
magic = 0;
|
||||
}
|
||||
|
||||
timer_tracker::timer_tracker (clockid_t c, const sigevent *e)
|
||||
/* fd is true for timerfd timers. */
|
||||
timer_tracker::timer_tracker (clockid_t c, const sigevent *e, bool fd)
|
||||
: magic (TT_MAGIC), instance_count (1), clock_id (c), deleting (false),
|
||||
hcancel (NULL), syncthread (NULL), event_running (EVENT_DISARMED),
|
||||
overrun_count_curr (0), overrun_count (0)
|
||||
{
|
||||
if (e != NULL)
|
||||
evp = *e;
|
||||
else if (fd)
|
||||
{
|
||||
evp.sigev_notify = SIGEV_NONE;
|
||||
evp.sigev_signo = 0;
|
||||
evp.sigev_value.sival_ptr = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
evp.sigev_notify = SIGEV_SIGNAL;
|
||||
evp.sigev_signo = SIGALRM;
|
||||
evp.sigev_value.sival_ptr = this;
|
||||
}
|
||||
clock_id = c;
|
||||
magic = TT_MAGIC;
|
||||
hcancel = NULL;
|
||||
event_running = EVENT_DISARMED;
|
||||
overrun_count_curr = 0;
|
||||
overrun_count = 0;
|
||||
if (fd)
|
||||
timerfd_event = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
|
||||
if (this != &ttstart)
|
||||
{
|
||||
lock_timer_tracker here;
|
||||
|
@ -94,6 +106,16 @@ timer_tracker::timer_tracker (clockid_t c, const sigevent *e)
|
|||
}
|
||||
}
|
||||
|
||||
void timer_tracker::increment_instances ()
|
||||
{
|
||||
InterlockedIncrement (&instance_count);
|
||||
}
|
||||
|
||||
LONG timer_tracker::decrement_instances ()
|
||||
{
|
||||
return InterlockedDecrement (&instance_count);
|
||||
}
|
||||
|
||||
static inline int64_t
|
||||
timespec_to_us (const timespec& ts)
|
||||
{
|
||||
|
@ -118,8 +140,8 @@ timer_tracker::arm_event ()
|
|||
return ret;
|
||||
}
|
||||
|
||||
LONG
|
||||
timer_tracker::disarm_event ()
|
||||
LONG64
|
||||
timer_tracker::_disarm_event ()
|
||||
{
|
||||
LONG ret;
|
||||
|
||||
|
@ -128,13 +150,8 @@ timer_tracker::disarm_event ()
|
|||
yield ();
|
||||
if (ret == EVENT_ARMED)
|
||||
{
|
||||
LONG64 ov_cnt;
|
||||
|
||||
InterlockedExchange64 (&ov_cnt, overrun_count);
|
||||
if (ov_cnt > DELAYTIMER_MAX || ov_cnt < 0)
|
||||
overrun_count_curr = DELAYTIMER_MAX;
|
||||
else
|
||||
overrun_count_curr = ov_cnt;
|
||||
InterlockedExchange64 (&overrun_count_curr, overrun_count);
|
||||
ret = overrun_count_curr;
|
||||
InterlockedExchange64 (&overrun_count, 0);
|
||||
InterlockedExchange (&event_running, EVENT_DISARMED);
|
||||
|
@ -142,6 +159,51 @@ timer_tracker::disarm_event ()
|
|||
return ret;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
timer_tracker::disarm_event ()
|
||||
{
|
||||
LONG64 ov = _disarm_event ();
|
||||
if (ov > DELAYTIMER_MAX || ov < 0)
|
||||
return DELAYTIMER_MAX;
|
||||
return (unsigned int) ov;
|
||||
}
|
||||
|
||||
LONG64
|
||||
timer_tracker::wait (bool nonblocking)
|
||||
{
|
||||
HANDLE w4[3] = { NULL, hcancel, timerfd_event };
|
||||
LONG64 ret = -1;
|
||||
|
||||
wait_signal_arrived here (w4[0]);
|
||||
repeat:
|
||||
switch (WaitForMultipleObjects (3, w4, FALSE, nonblocking ? 0 : INFINITE))
|
||||
{
|
||||
case WAIT_OBJECT_0: /* signal */
|
||||
if (_my_tls.call_signal_handler ())
|
||||
goto repeat;
|
||||
set_errno (EINTR);
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 1: /* settime oder timer delete */
|
||||
if (deleting)
|
||||
{
|
||||
set_errno (EIO);
|
||||
break;
|
||||
}
|
||||
/*FALLTHRU*/
|
||||
case WAIT_OBJECT_0 + 2: /* timer event */
|
||||
ret = _disarm_event ();
|
||||
ResetEvent (timerfd_event);
|
||||
break;
|
||||
case WAIT_TIMEOUT:
|
||||
set_errno (EAGAIN);
|
||||
break;
|
||||
default:
|
||||
__seterrno ();
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *
|
||||
notify_thread_wrapper (void *arg)
|
||||
{
|
||||
|
@ -198,6 +260,14 @@ timer_tracker::thread_func ()
|
|||
|
||||
switch (evp.sigev_notify)
|
||||
{
|
||||
case SIGEV_NONE:
|
||||
{
|
||||
if (!timerfd_event)
|
||||
break;
|
||||
arm_event ();
|
||||
SetEvent (timerfd_event);
|
||||
break;
|
||||
}
|
||||
case SIGEV_SIGNAL:
|
||||
{
|
||||
if (arm_event ())
|
||||
|
@ -350,9 +420,17 @@ timer_tracker::gettime (itimerspec *ovalue)
|
|||
}
|
||||
}
|
||||
|
||||
/* Returns
|
||||
|
||||
1 if we still have to keep the timer around
|
||||
0 if we can delete the timer
|
||||
-1 if we can't find the timer in the list
|
||||
*/
|
||||
int
|
||||
timer_tracker::clean_and_unhook ()
|
||||
{
|
||||
if (decrement_instances () > 0)
|
||||
return 1;
|
||||
for (timer_tracker *tt = &ttstart; tt->next != NULL; tt = tt->next)
|
||||
if (tt->next == this)
|
||||
{
|
||||
|
@ -362,9 +440,26 @@ timer_tracker::clean_and_unhook ()
|
|||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
timer_tracker::close (timer_tracker *tt)
|
||||
{
|
||||
lock_timer_tracker here;
|
||||
int ret = tt->clean_and_unhook ();
|
||||
if (ret >= 0)
|
||||
{
|
||||
if (ret == 0)
|
||||
delete tt;
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
set_errno (EINVAL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
timer_tracker::fixup_after_fork ()
|
||||
{
|
||||
/* TODO: Keep timerfd timers available and restart them */
|
||||
ttstart.hcancel = ttstart.syncthread = NULL;
|
||||
ttstart.event_running = EVENT_DISARMED;
|
||||
ttstart.overrun_count_curr = 0;
|
||||
|
@ -412,21 +507,21 @@ timer_create (clockid_t clock_id, struct sigevent *__restrict evp,
|
|||
{
|
||||
int ret = -1;
|
||||
|
||||
if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id))
|
||||
{
|
||||
set_errno (ENOTSUP);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (clock_id >= MAX_CLOCKS)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
__try
|
||||
{
|
||||
if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id))
|
||||
{
|
||||
set_errno (ENOTSUP);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (clock_id >= MAX_CLOCKS)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*timerid = (timer_t) new timer_tracker (clock_id, evp);
|
||||
*timerid = (timer_t) new timer_tracker (clock_id, evp, false);
|
||||
ret = 0;
|
||||
}
|
||||
__except (EFAULT) {}
|
||||
|
@ -489,15 +584,7 @@ timer_delete (timer_t timerid)
|
|||
set_errno (EINVAL);
|
||||
__leave;
|
||||
}
|
||||
|
||||
lock_timer_tracker here;
|
||||
if (in_tt->clean_and_unhook () == 0)
|
||||
{
|
||||
delete in_tt;
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
set_errno (EINVAL);
|
||||
ret = timer_tracker::close (in_tt);
|
||||
}
|
||||
__except (EFAULT) {}
|
||||
__endtry
|
||||
|
@ -604,3 +691,84 @@ ualarm (useconds_t value, useconds_t interval)
|
|||
syscall_printf ("%d = ualarm(%ld , %ld)", ret, value, interval);
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
timerfd_create (clockid_t clock_id, int flags)
|
||||
{
|
||||
int ret = -1;
|
||||
fhandler_timerfd *fh;
|
||||
|
||||
debug_printf ("timerfd (%lu, %y)", clock_id, flags);
|
||||
|
||||
if (clock_id != CLOCK_REALTIME
|
||||
&& clock_id != CLOCK_MONOTONIC
|
||||
&& clock_id != CLOCK_BOOTTIME)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
if ((flags & ~(TFD_NONBLOCK | TFD_CLOEXEC)) != 0)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
goto done;
|
||||
}
|
||||
|
||||
{
|
||||
/* Create new timerfd descriptor. */
|
||||
cygheap_fdnew fd;
|
||||
|
||||
if (fd < 0)
|
||||
goto done;
|
||||
fh = (fhandler_timerfd *) build_fh_dev (*timerfd_dev);
|
||||
if (fh && fh->timerfd (clock_id, flags) == 0)
|
||||
{
|
||||
fd = fh;
|
||||
if (fd <= 2)
|
||||
set_std_handle (fd);
|
||||
ret = fd;
|
||||
}
|
||||
else
|
||||
delete fh;
|
||||
}
|
||||
|
||||
done:
|
||||
syscall_printf ("%R = timerfd (%lu, %y)", ret, clock_id, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
timerfd_settime (int fd_in, int flags, const struct itimerspec *value,
|
||||
struct itimerspec *ovalue)
|
||||
{
|
||||
if ((flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) != 0)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cygheap_fdget fd (fd_in);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
fhandler_timerfd *fh = fd->is_timerfd ();
|
||||
if (!fh)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
return fh->settime (flags, value, ovalue);
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
timerfd_gettime (int fd_in, struct itimerspec *ovalue)
|
||||
{
|
||||
cygheap_fdget fd (fd_in);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
fhandler_timerfd *fh = fd->is_timerfd ();
|
||||
if (!fh)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
return fh->gettime (ovalue);
|
||||
}
|
||||
|
|
|
@ -14,35 +14,46 @@ class timer_tracker
|
|||
{
|
||||
unsigned magic;
|
||||
timer_tracker *next;
|
||||
LONG instance_count;
|
||||
|
||||
clockid_t clock_id;
|
||||
sigevent evp;
|
||||
timespec it_interval;
|
||||
bool deleting;
|
||||
HANDLE hcancel;
|
||||
HANDLE syncthread;
|
||||
HANDLE timerfd_event;
|
||||
int64_t interval_us;
|
||||
int64_t sleepto_us;
|
||||
LONG event_running;
|
||||
LONG overrun_count_curr;
|
||||
LONG64 overrun_count_curr;
|
||||
LONG64 overrun_count;
|
||||
|
||||
bool cancel ();
|
||||
LONG decrement_instances ();
|
||||
int clean_and_unhook ();
|
||||
LONG64 _disarm_event ();
|
||||
|
||||
public:
|
||||
timer_tracker (clockid_t, const sigevent *);
|
||||
timer_tracker (clockid_t, const sigevent *, bool);
|
||||
~timer_tracker ();
|
||||
inline bool is_timer_tracker () const { return magic == TT_MAGIC; }
|
||||
|
||||
void increment_instances ();
|
||||
LONG64 wait (bool nonblocking);
|
||||
HANDLE get_timerfd_handle () const { return timerfd_event; }
|
||||
|
||||
inline sigevent_t *sigevt () { return &evp; }
|
||||
inline int getoverrun () const { return overrun_count_curr; }
|
||||
inline LONG64 getoverrun () const { return overrun_count_curr; }
|
||||
|
||||
void gettime (itimerspec *);
|
||||
int settime (int, const itimerspec *, itimerspec *);
|
||||
int clean_and_unhook ();
|
||||
LONG arm_event ();
|
||||
LONG disarm_event ();
|
||||
unsigned int disarm_event ();
|
||||
|
||||
DWORD thread_func ();
|
||||
static void fixup_after_fork ();
|
||||
static int close (timer_tracker *tt);
|
||||
};
|
||||
|
||||
#endif /* __TIMER_H__ */
|
||||
|
|
|
@ -51,7 +51,8 @@ siginfo_t::si_overrun).
|
|||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
New API: signalfd, timer_getoverrun.
|
||||
New APIs: signalfd, timerfd_create, timerfd_gettime, timerfd_settime,
|
||||
timer_getoverrun.
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
|
|
|
@ -1394,6 +1394,9 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
|
|||
strverscmp
|
||||
sysinfo
|
||||
tdestroy
|
||||
timerfd_create
|
||||
timerfd_gettime
|
||||
timerfd_settime
|
||||
timegm
|
||||
timelocal
|
||||
toascii_l
|
||||
|
|
Loading…
Reference in New Issue