newlib/winsup/cygwin/fhandler_timerfd.cc
Corinna Vinschen 98afd02be3 Cygwin: timerfd: rework implementation
timerfd_tracker and timerfd_shared classes:

- Just because handles are shared, we don't have to store them in
  shared memory.  Move share handles into timerfd_tracker class.

- Drop shared instance counter since it's not required anymore.
  timerfd_shared only stores the actual timer data.

- Drop timerfd_shared::create, just set clock id.

- Drop timerfd_shared::dtor, it's not required anymore.

- Drop timerfd_tracker::close, just call dtor where required.

- Rename timerfd_tracker::increment_instances to timerfd_tracker::dup.
  It's the only reason it exists...

- timerfd_tracker::dtor now checks the non-shared pointers for NULL
  before attempting to close them.

- timerfd_tracker::dtor handles decrementing the local instance count
  by itself.

- Add a method timerfd_tracker::init_fixup_after_fork_exec to set
  non-shared pointers to NULL.  Together with the dtor patches it
  fixes a problem with close_on_exec timerfd descriptors.

- Fix a bug in handling the thread synchronization event.  It's
  actually nice to create it before using it...

- Drop using sec_none{_nih} in InitializeObjectAttributes.  It's
  an unnecessary roundabout route just to get a NULL pointer.

- Slightly rework timechange window handling.

- Add more comments to explain what happens.

fhandler_timerfd:

- Drop cnew macro, it just hides what happens.

- fhandler_timerfd::fixup_after_exec now calls
  timerfd_tracker::init_fixup_after_fork_exec first, so a subsequent
  call to timerfd_tracker::dtor only works on valid handles.

- fhandler_timerfd::close directly calls timerfd_tracker::dtor now.

- Drop dtor call in fhandler_timerfd destructor.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
2019-02-25 21:01:32 +01:00

339 lines
6.2 KiB
C++

/* 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 __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
{
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 __stdcall
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);
}