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:
Corinna Vinschen 2019-01-15 22:02:33 +01:00
parent b6f53617a7
commit 068182e26c
16 changed files with 592 additions and 52 deletions

View File

@ -305,6 +305,7 @@ DLL_OFILES:= \
fhandler_socket_unix.o \ fhandler_socket_unix.o \
fhandler_tape.o \ fhandler_tape.o \
fhandler_termios.o \ fhandler_termios.o \
fhandler_timerfd.o \
fhandler_tty.o \ fhandler_tty.o \
fhandler_virtual.o \ fhandler_virtual.o \
fhandler_windows.o \ fhandler_windows.o \

View File

@ -1488,6 +1488,9 @@ timer_delete SIGFE
timer_getoverrun SIGFE timer_getoverrun SIGFE
timer_gettime SIGFE timer_gettime SIGFE
timer_settime SIGFE timer_settime SIGFE
timerfd_create SIGFE
timerfd_gettime SIGFE
timerfd_settime SIGFE
times SIGFE times SIGFE
timezone SIGFE timezone SIGFE
timingsafe_bcmp NOSIGFE timingsafe_bcmp NOSIGFE

View File

@ -123,6 +123,9 @@ const _device dev_pipew_storage =
const _device dev_signalfd_storage = const _device dev_signalfd_storage =
{"", {FH_SIGNALFD}, "", exists_internal}; {"", {FH_SIGNALFD}, "", exists_internal};
const _device dev_timerfd_storage =
{"", {FH_TIMERFD}, "", exists_internal};
const _device dev_socket_storage = const _device dev_socket_storage =
{"", {FH_SOCKET}, "", exists_internal}; {"", {FH_SOCKET}, "", exists_internal};

View File

@ -73,6 +73,7 @@ enum fh_devices
FH_CYGDRIVE= FHDEV (DEV_VIRTFS_MAJOR, 192), FH_CYGDRIVE= FHDEV (DEV_VIRTFS_MAJOR, 192),
FH_SIGNALFD= FHDEV (DEV_VIRTFS_MAJOR, 13), FH_SIGNALFD= FHDEV (DEV_VIRTFS_MAJOR, 13),
FH_TIMERFD = FHDEV (DEV_VIRTFS_MAJOR, 14),
DEV_FLOPPY_MAJOR = 2, DEV_FLOPPY_MAJOR = 2,
FH_FLOPPY = FHDEV (DEV_FLOPPY_MAJOR, 0), FH_FLOPPY = FHDEV (DEV_FLOPPY_MAJOR, 0),
@ -404,6 +405,8 @@ extern const _device dev_af_unix_storage;
extern const _device dev_signalfd_storage; extern const _device dev_signalfd_storage;
#define signalfd_dev ((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; extern const _device dev_piper_storage;
#define piper_dev ((device *) &dev_piper_storage) #define piper_dev ((device *) &dev_piper_storage)
extern const _device dev_pipew_storage; extern const _device dev_pipew_storage;

View File

@ -119,6 +119,9 @@ const _device dev_pipew_storage =
const _device dev_signalfd_storage = const _device dev_signalfd_storage =
{"", {FH_SIGNALFD}, "", exists_internal}; {"", {FH_SIGNALFD}, "", exists_internal};
const _device dev_timerfd_storage =
{"", {FH_TIMERFD}, "", exists_internal};
const _device dev_socket_storage = const _device dev_socket_storage =
{"", {FH_SOCKET}, "", exists_internal}; {"", {FH_SOCKET}, "", exists_internal};

View File

@ -578,6 +578,9 @@ fh_alloc (path_conv& pc)
case FH_SIGNALFD: case FH_SIGNALFD:
fh = cnew (fhandler_signalfd); fh = cnew (fhandler_signalfd);
break; break;
case FH_TIMERFD:
fh = cnew (fhandler_timerfd);
break;
case FH_TTY: case FH_TTY:
if (!pc.isopen ()) if (!pc.isopen ())
{ {

View File

@ -421,6 +421,7 @@ public:
virtual class fhandler_socket_wsock *is_wsock_socket () { return NULL; } virtual class fhandler_socket_wsock *is_wsock_socket () { return NULL; }
virtual class fhandler_console *is_console () { return 0; } virtual class fhandler_console *is_console () { return 0; }
virtual class fhandler_signalfd *is_signalfd () { return NULL; } virtual class fhandler_signalfd *is_signalfd () { return NULL; }
virtual class fhandler_timerfd *is_timerfd () { return NULL; }
virtual int is_windows () {return 0; } virtual int is_windows () {return 0; }
virtual void __reg3 raw_read (void *ptr, size_t& ulen); 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 struct fhandler_nodevice: public fhandler_base
{ {
fhandler_nodevice (); fhandler_nodevice ();
@ -2713,6 +2763,7 @@ typedef union
char __registry[sizeof (fhandler_registry)]; char __registry[sizeof (fhandler_registry)];
char __serial[sizeof (fhandler_serial)]; char __serial[sizeof (fhandler_serial)];
char __signalfd[sizeof (fhandler_signalfd)]; char __signalfd[sizeof (fhandler_signalfd)];
char __timerfd[sizeof (fhandler_timerfd)];
char __socket_inet[sizeof (fhandler_socket_inet)]; char __socket_inet[sizeof (fhandler_socket_inet)];
char __socket_local[sizeof (fhandler_socket_local)]; char __socket_local[sizeof (fhandler_socket_local)];
#ifdef __WITH_AF_UNIX #ifdef __WITH_AF_UNIX

View File

@ -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;
}

View File

@ -503,12 +503,13 @@ details. */
CLOCK_BOOTTIME. CLOCK_BOOTTIME.
331: Add timer_getoverrun, DELAYTIMER_MAX. 331: Add timer_getoverrun, DELAYTIMER_MAX.
332: Add signalfd. 332: Add signalfd.
333: Add timerfd_create, timerfd_gettime, timerfd_settime.
Note that we forgot to bump the api for ualarm, strtoll, strtoull, Note that we forgot to bump the api for ualarm, strtoll, strtoull,
sigaltstack, sethostname. */ sigaltstack, sethostname. */
#define CYGWIN_VERSION_API_MAJOR 0 #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 /* There is also a compatibity version number associated with the shared memory
regions. It is incremented when incompatible changes are made to the shared regions. It is incremented when incompatible changes are made to the shared

View File

@ -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 */

View File

@ -27,7 +27,8 @@ What's new:
- Support overrun counter for posix timers (via timer_getoverrun() or - Support overrun counter for posix timers (via timer_getoverrun() or
siginfo_t::si_overrun). siginfo_t::si_overrun).
- New API: signalfd, timer_getoverrun. - New APIs: signalfd, timerfd_create, timerfd_gettime, timerfd_settime,
timer_getoverrun.
What changed: What changed:

View File

@ -1870,3 +1870,48 @@ fhandler_signalfd::select_except (select_stuff *stuff)
s->except_ready = false; s->except_ready = false;
return s; 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;
}

View File

@ -15,13 +15,14 @@ details. */
#include "dtable.h" #include "dtable.h"
#include "cygheap.h" #include "cygheap.h"
#include "timer.h" #include "timer.h"
#include <sys/timerfd.h>
#include <sys/param.h> #include <sys/param.h>
#define EVENT_DISARMED 0 #define EVENT_DISARMED 0
#define EVENT_ARMED -1 #define EVENT_ARMED -1
#define EVENT_LOCK 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 class lock_timer_tracker
{ {
@ -58,34 +59,45 @@ timer_tracker::cancel ()
timer_tracker::~timer_tracker () timer_tracker::~timer_tracker ()
{ {
HANDLE hdl;
deleting = true;
if (cancel ()) if (cancel ())
{ {
CloseHandle (hcancel); HANDLE hdl = InterlockedExchangePointer (&hcancel, NULL);
#ifdef DEBUGGING CloseHandle (hdl);
hcancel = NULL; hdl = InterlockedExchangePointer (&timerfd_event, NULL);
#endif if (hdl)
CloseHandle (hdl);
} }
if (syncthread) hdl = InterlockedExchangePointer (&syncthread, NULL);
CloseHandle (syncthread); if (hdl)
CloseHandle (hdl);
magic = 0; 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) if (e != NULL)
evp = *e; evp = *e;
else if (fd)
{
evp.sigev_notify = SIGEV_NONE;
evp.sigev_signo = 0;
evp.sigev_value.sival_ptr = this;
}
else else
{ {
evp.sigev_notify = SIGEV_SIGNAL; evp.sigev_notify = SIGEV_SIGNAL;
evp.sigev_signo = SIGALRM; evp.sigev_signo = SIGALRM;
evp.sigev_value.sival_ptr = this; evp.sigev_value.sival_ptr = this;
} }
clock_id = c; if (fd)
magic = TT_MAGIC; timerfd_event = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
hcancel = NULL;
event_running = EVENT_DISARMED;
overrun_count_curr = 0;
overrun_count = 0;
if (this != &ttstart) if (this != &ttstart)
{ {
lock_timer_tracker here; 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 static inline int64_t
timespec_to_us (const timespec& ts) timespec_to_us (const timespec& ts)
{ {
@ -118,8 +140,8 @@ timer_tracker::arm_event ()
return ret; return ret;
} }
LONG LONG64
timer_tracker::disarm_event () timer_tracker::_disarm_event ()
{ {
LONG ret; LONG ret;
@ -128,13 +150,8 @@ timer_tracker::disarm_event ()
yield (); yield ();
if (ret == EVENT_ARMED) if (ret == EVENT_ARMED)
{ {
LONG64 ov_cnt;
InterlockedExchange64 (&ov_cnt, overrun_count); InterlockedExchange64 (&overrun_count_curr, overrun_count);
if (ov_cnt > DELAYTIMER_MAX || ov_cnt < 0)
overrun_count_curr = DELAYTIMER_MAX;
else
overrun_count_curr = ov_cnt;
ret = overrun_count_curr; ret = overrun_count_curr;
InterlockedExchange64 (&overrun_count, 0); InterlockedExchange64 (&overrun_count, 0);
InterlockedExchange (&event_running, EVENT_DISARMED); InterlockedExchange (&event_running, EVENT_DISARMED);
@ -142,6 +159,51 @@ timer_tracker::disarm_event ()
return ret; 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 * static void *
notify_thread_wrapper (void *arg) notify_thread_wrapper (void *arg)
{ {
@ -198,6 +260,14 @@ timer_tracker::thread_func ()
switch (evp.sigev_notify) switch (evp.sigev_notify)
{ {
case SIGEV_NONE:
{
if (!timerfd_event)
break;
arm_event ();
SetEvent (timerfd_event);
break;
}
case SIGEV_SIGNAL: case SIGEV_SIGNAL:
{ {
if (arm_event ()) 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 int
timer_tracker::clean_and_unhook () timer_tracker::clean_and_unhook ()
{ {
if (decrement_instances () > 0)
return 1;
for (timer_tracker *tt = &ttstart; tt->next != NULL; tt = tt->next) for (timer_tracker *tt = &ttstart; tt->next != NULL; tt = tt->next)
if (tt->next == this) if (tt->next == this)
{ {
@ -362,9 +440,26 @@ timer_tracker::clean_and_unhook ()
return -1; 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 void
timer_tracker::fixup_after_fork () timer_tracker::fixup_after_fork ()
{ {
/* TODO: Keep timerfd timers available and restart them */
ttstart.hcancel = ttstart.syncthread = NULL; ttstart.hcancel = ttstart.syncthread = NULL;
ttstart.event_running = EVENT_DISARMED; ttstart.event_running = EVENT_DISARMED;
ttstart.overrun_count_curr = 0; ttstart.overrun_count_curr = 0;
@ -412,21 +507,21 @@ timer_create (clockid_t clock_id, struct sigevent *__restrict evp,
{ {
int ret = -1; 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 __try
{ {
if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id)) *timerid = (timer_t) new timer_tracker (clock_id, evp, false);
{
set_errno (ENOTSUP);
return -1;
}
if (clock_id >= MAX_CLOCKS)
{
set_errno (EINVAL);
return -1;
}
*timerid = (timer_t) new timer_tracker (clock_id, evp);
ret = 0; ret = 0;
} }
__except (EFAULT) {} __except (EFAULT) {}
@ -489,15 +584,7 @@ timer_delete (timer_t timerid)
set_errno (EINVAL); set_errno (EINVAL);
__leave; __leave;
} }
ret = timer_tracker::close (in_tt);
lock_timer_tracker here;
if (in_tt->clean_and_unhook () == 0)
{
delete in_tt;
ret = 0;
}
else
set_errno (EINVAL);
} }
__except (EFAULT) {} __except (EFAULT) {}
__endtry __endtry
@ -604,3 +691,84 @@ ualarm (useconds_t value, useconds_t interval)
syscall_printf ("%d = ualarm(%ld , %ld)", ret, value, interval); syscall_printf ("%d = ualarm(%ld , %ld)", ret, value, interval);
return ret; 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);
}

View File

@ -14,35 +14,46 @@ class timer_tracker
{ {
unsigned magic; unsigned magic;
timer_tracker *next; timer_tracker *next;
LONG instance_count;
clockid_t clock_id; clockid_t clock_id;
sigevent evp; sigevent evp;
timespec it_interval; timespec it_interval;
bool deleting;
HANDLE hcancel; HANDLE hcancel;
HANDLE syncthread; HANDLE syncthread;
HANDLE timerfd_event;
int64_t interval_us; int64_t interval_us;
int64_t sleepto_us; int64_t sleepto_us;
LONG event_running; LONG event_running;
LONG overrun_count_curr; LONG64 overrun_count_curr;
LONG64 overrun_count; LONG64 overrun_count;
bool cancel (); bool cancel ();
LONG decrement_instances ();
int clean_and_unhook ();
LONG64 _disarm_event ();
public: public:
timer_tracker (clockid_t, const sigevent *); timer_tracker (clockid_t, const sigevent *, bool);
~timer_tracker (); ~timer_tracker ();
inline bool is_timer_tracker () const { return magic == TT_MAGIC; } 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 sigevent_t *sigevt () { return &evp; }
inline int getoverrun () const { return overrun_count_curr; } inline LONG64 getoverrun () const { return overrun_count_curr; }
void gettime (itimerspec *); void gettime (itimerspec *);
int settime (int, const itimerspec *, itimerspec *); int settime (int, const itimerspec *, itimerspec *);
int clean_and_unhook ();
LONG arm_event (); LONG arm_event ();
LONG disarm_event (); unsigned int disarm_event ();
DWORD thread_func (); DWORD thread_func ();
static void fixup_after_fork (); static void fixup_after_fork ();
static int close (timer_tracker *tt);
}; };
#endif /* __TIMER_H__ */ #endif /* __TIMER_H__ */

View File

@ -51,7 +51,8 @@ siginfo_t::si_overrun).
</para></listitem> </para></listitem>
<listitem><para> <listitem><para>
New API: signalfd, timer_getoverrun. New APIs: signalfd, timerfd_create, timerfd_gettime, timerfd_settime,
timer_getoverrun.
</para></listitem> </para></listitem>
<listitem><para> <listitem><para>

View File

@ -1394,6 +1394,9 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
strverscmp strverscmp
sysinfo sysinfo
tdestroy tdestroy
timerfd_create
timerfd_gettime
timerfd_settime
timegm timegm
timelocal timelocal
toascii_l toascii_l