From 068182e26c7b397df579b69a18f745092844d1b4 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Tue, 15 Jan 2019 22:02:33 +0100 Subject: [PATCH] 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 --- winsup/cygwin/Makefile.in | 1 + winsup/cygwin/common.din | 3 + winsup/cygwin/devices.cc | 3 + winsup/cygwin/devices.h | 3 + winsup/cygwin/devices.in | 3 + winsup/cygwin/dtable.cc | 3 + winsup/cygwin/fhandler.h | 51 +++++ winsup/cygwin/fhandler_timerfd.cc | 198 +++++++++++++++++++ winsup/cygwin/include/cygwin/version.h | 3 +- winsup/cygwin/include/sys/timerfd.h | 45 +++++ winsup/cygwin/release/2.12.0 | 3 +- winsup/cygwin/select.cc | 45 +++++ winsup/cygwin/timer.cc | 254 ++++++++++++++++++++----- winsup/cygwin/timer.h | 21 +- winsup/doc/new-features.xml | 3 +- winsup/doc/posix.xml | 3 + 16 files changed, 591 insertions(+), 51 deletions(-) create mode 100644 winsup/cygwin/fhandler_timerfd.cc create mode 100644 winsup/cygwin/include/sys/timerfd.h diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index 6147e7c9f..8481851c0 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -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 \ diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din index b7f39f9c0..384cf0b15 100644 --- a/winsup/cygwin/common.din +++ b/winsup/cygwin/common.din @@ -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 diff --git a/winsup/cygwin/devices.cc b/winsup/cygwin/devices.cc index 31fd64fa2..2e31ca366 100644 --- a/winsup/cygwin/devices.cc +++ b/winsup/cygwin/devices.cc @@ -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}; diff --git a/winsup/cygwin/devices.h b/winsup/cygwin/devices.h index 065f77e0e..47156f275 100644 --- a/winsup/cygwin/devices.h +++ b/winsup/cygwin/devices.h @@ -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; diff --git a/winsup/cygwin/devices.in b/winsup/cygwin/devices.in index 79a7fe726..59f5f00d2 100644 --- a/winsup/cygwin/devices.in +++ b/winsup/cygwin/devices.in @@ -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}; diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc index c8aecfa1a..663f99b34 100644 --- a/winsup/cygwin/dtable.cc +++ b/winsup/cygwin/dtable.cc @@ -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 ()) { diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index f4a8b5463..898f85db2 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -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 (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 diff --git a/winsup/cygwin/fhandler_timerfd.cc b/winsup/cygwin/fhandler_timerfd.cc new file mode 100644 index 000000000..b88c797ef --- /dev/null +++ b/winsup/cygwin/fhandler_timerfd.cc @@ -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 +#include + +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; +} diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index ec5f55fa9..d6dcbea0e 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -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 diff --git a/winsup/cygwin/include/sys/timerfd.h b/winsup/cygwin/include/sys/timerfd.h new file mode 100644 index 000000000..bf3ce2ae0 --- /dev/null +++ b/winsup/cygwin/include/sys/timerfd.h @@ -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 +#include +#include + +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 */ diff --git a/winsup/cygwin/release/2.12.0 b/winsup/cygwin/release/2.12.0 index a0dce6ffd..80d0c6fba 100644 --- a/winsup/cygwin/release/2.12.0 +++ b/winsup/cygwin/release/2.12.0 @@ -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: diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index d1ae3c649..d6757e4ed 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -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; +} diff --git a/winsup/cygwin/timer.cc b/winsup/cygwin/timer.cc index e38decb9a..93e4714e2 100644 --- a/winsup/cygwin/timer.cc +++ b/winsup/cygwin/timer.cc @@ -15,13 +15,14 @@ details. */ #include "dtable.h" #include "cygheap.h" #include "timer.h" +#include #include #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; - __try + if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id)) { - if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id)) - { - set_errno (ENOTSUP); - return -1; - } + set_errno (ENOTSUP); + return -1; + } - if (clock_id >= MAX_CLOCKS) - { - set_errno (EINVAL); - return -1; - } + if (clock_id >= MAX_CLOCKS) + { + set_errno (EINVAL); + return -1; + } - *timerid = (timer_t) new timer_tracker (clock_id, evp); + __try + { + *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); +} diff --git a/winsup/cygwin/timer.h b/winsup/cygwin/timer.h index f75cd487c..2a31e3e77 100644 --- a/winsup/cygwin/timer.h +++ b/winsup/cygwin/timer.h @@ -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__ */ diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml index d4fc74510..2c4b3e43f 100644 --- a/winsup/doc/new-features.xml +++ b/winsup/doc/new-features.xml @@ -51,7 +51,8 @@ siginfo_t::si_overrun). -New API: signalfd, timer_getoverrun. +New APIs: signalfd, timerfd_create, timerfd_gettime, timerfd_settime, +timer_getoverrun. diff --git a/winsup/doc/posix.xml b/winsup/doc/posix.xml index 365a8f0b5..8e9b1a511 100644 --- a/winsup/doc/posix.xml +++ b/winsup/doc/posix.xml @@ -1394,6 +1394,9 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008). strverscmp sysinfo tdestroy + timerfd_create + timerfd_gettime + timerfd_settime timegm timelocal toascii_l -- 2.43.5