diff options
Diffstat (limited to 'winsup/cygwin/fhandler/timerfd.cc')
-rw-r--r-- | winsup/cygwin/fhandler/timerfd.cc | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/winsup/cygwin/fhandler/timerfd.cc b/winsup/cygwin/fhandler/timerfd.cc new file mode 100644 index 000000000..9269494db --- /dev/null +++ b/winsup/cygwin/fhandler/timerfd.cc @@ -0,0 +1,338 @@ +/* fhandler_timerfd.cc: fhandler for timerfd, public timerfd API + +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 "timerfd.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]"); +} + +/* The timers connected to a descriptor are stored on the cygheap + together with their fhandler. */ + +int +fhandler_timerfd::timerfd (clockid_t clock_id, int flags) +{ + timerfd_tracker *tfd = (timerfd_tracker *) + ccalloc (HEAP_FHANDLER, 1, sizeof (timerfd_tracker)); + if (!tfd) + { + set_errno (ENOMEM); + return -1; + } + new (tfd) timerfd_tracker (); + int ret = tfd->create (clock_id); + if (ret < 0) + { + cfree (tfd); + set_errno (-ret); + return -1; + } + if (flags & TFD_NONBLOCK) + set_nonblocking (true); + if (flags & TFD_CLOEXEC) + set_close_on_exec (true); + nohandle (true); + set_unique_id (); + set_ino (get_unique_id ()); + set_flags (O_RDWR | O_BINARY); + timerid = (timer_t) tfd; + return 0; +} + +int +fhandler_timerfd::settime (int flags, const struct itimerspec *new_value, + struct itimerspec *old_value) +{ + int ret = -1; + + __try + { + timerfd_tracker *tfd = (timerfd_tracker *) timerid; + ret = tfd->settime (flags, new_value, old_value); + if (ret < 0) + { + set_errno (-ret); + ret = -1; + } + } + __except (EFAULT) {} + __endtry + return ret; +} + +int +fhandler_timerfd::gettime (struct itimerspec *ovalue) +{ + int ret = -1; + + __try + { + timerfd_tracker *tfd = (timerfd_tracker *) timerid; + ret = tfd->gettime (ovalue); + if (ret < 0) + { + set_errno (-ret); + ret = -1; + } + } + __except (EFAULT) {} + __endtry + return ret; +} + +int +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 +fhandler_timerfd::read (void *ptr, size_t& len) +{ + if (len < sizeof (LONG64)) + { + set_errno (EINVAL); + len = (size_t) -1; + return; + } + + __try + { + timerfd_tracker *tfd = (timerfd_tracker *) timerid; + LONG64 ret = tfd->wait (is_nonblocking ()); + if (ret < 0) + { + set_errno (-ret); + __leave; + } + *(PLONG64) ptr = ret; + len = sizeof (LONG64); + return; + } + __except (EFAULT) {} + __endtry + len = (size_t) -1; + return; +} + +ssize_t +fhandler_timerfd::write (const void *, size_t) +{ + set_errno (EINVAL); + return -1; +} + +HANDLE +fhandler_timerfd::get_timerfd_handle () +{ + __try + { + timerfd_tracker *tfd = (timerfd_tracker *) timerid; + return tfd->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 + { + timerfd_tracker *tfd = (timerfd_tracker *) fhc->timerid; + tfd->dup (); + ret = 0; + } + __except (EFAULT) {} + __endtry + } + return ret; +} + +int +fhandler_timerfd::ioctl (unsigned int cmd, void *p) +{ + int ret = -1; + uint64_t exp_cnt; + + switch (cmd) + { + case TFD_IOC_SET_TICKS: + __try + { + timerfd_tracker *tfd = (timerfd_tracker *) timerid; + + exp_cnt = *(uint64_t *) p; + ret = tfd->ioctl_set_ticks (exp_cnt); + if (ret < 0) + set_errno (-ret); + } + __except (EFAULT) {} + __endtry + break; + default: + ret = fhandler_base::ioctl (cmd, p); + break; + } + syscall_printf ("%d = ioctl_timerfd(%x, %p)", ret, cmd, p); + return ret; +} + +void +fhandler_timerfd::fixup_after_fork (HANDLE) +{ + __try + { + timerfd_tracker *tfd = (timerfd_tracker *) timerid; + tfd->fixup_after_fork (); + } + __except (EFAULT) {} + __endtry +} + +void +fhandler_timerfd::fixup_after_exec () +{ + __try + { + timerfd_tracker *tfd = (timerfd_tracker *) timerid; + tfd->init_fixup_after_fork_exec (); + if (close_on_exec ()) + timerfd_tracker::dtor (tfd); + else + tfd->fixup_after_exec (); + } + __except (EFAULT) {} + __endtry +} + +int +fhandler_timerfd::close () +{ + int ret = -1; + + __try + { + timerfd_tracker *tfd = (timerfd_tracker *) timerid; + timerfd_tracker::dtor (tfd); + ret = 0; + } + __except (EFAULT) {} + __endtry + return ret; +} + +extern "C" int +timerfd_create (clockid_t clock_id, int flags) +{ + int ret = -1; + fhandler_timerfd *fh; + + debug_printf ("timerfd_create (%lu, %y)", clock_id, flags); + + if (clock_id != CLOCK_REALTIME + && clock_id != CLOCK_MONOTONIC + && clock_id != CLOCK_BOOTTIME) + { + set_errno (EINVAL); + goto done; + } + 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_create (%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); +} |