[newlib-cygwin] Cygwin: signal: implement signalfd

Corinna Vinschen corinna@sourceware.org
Sun Jan 13 22:16:00 GMT 2019


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=9d13a2995cb4b6fd26cd7b7a2c478ad85115e055

commit 9d13a2995cb4b6fd26cd7b7a2c478ad85115e055
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Sun Jan 13 23:13:33 2019 +0100

    Cygwin: signal: implement signalfd
    
    First cut of a signalfd implementation.
    
    Still TODO: Non-polling select.
    
    This should mostly work as on Linux except for missing support
    for some members of struct signalfd_siginfo, namely ssi_fd,
    ssi_band (both SIGIO/SIGPOLL, not fully implemented) and ssi_trapno
    (HW exception, required HW support).
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/Makefile.in              |   1 +
 winsup/cygwin/common.din               |   1 +
 winsup/cygwin/devices.cc               |   3 +
 winsup/cygwin/devices.h                |   4 +
 winsup/cygwin/devices.in               |   3 +
 winsup/cygwin/dtable.cc                |   3 +
 winsup/cygwin/fhandler.cc              |   3 +
 winsup/cygwin/fhandler.h               |  40 +++++++++
 winsup/cygwin/fhandler_signalfd.cc     | 153 +++++++++++++++++++++++++++++++++
 winsup/cygwin/include/cygwin/version.h |   3 +-
 winsup/cygwin/include/sys/signalfd.h   |  54 ++++++++++++
 winsup/cygwin/release/2.12.0           |   2 +-
 winsup/cygwin/select.cc                |  65 ++++++++++++++
 winsup/cygwin/signal.cc                |  62 ++++++++++++-
 winsup/cygwin/sigproc.h                |   1 +
 winsup/doc/new-features.xml            |   2 +-
 winsup/doc/posix.xml                   |   1 +
 17 files changed, 397 insertions(+), 4 deletions(-)

diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
index e4ce31f..6147e7c 100644
--- a/winsup/cygwin/Makefile.in
+++ b/winsup/cygwin/Makefile.in
@@ -298,6 +298,7 @@ DLL_OFILES:= \
 	fhandler_raw.o \
 	fhandler_registry.o \
 	fhandler_serial.o \
+	fhandler_signalfd.o \
 	fhandler_socket.o \
 	fhandler_socket_inet.o \
 	fhandler_socket_local.o \
diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din
index a6363e1..b7f39f9 100644
--- a/winsup/cygwin/common.din
+++ b/winsup/cygwin/common.din
@@ -1333,6 +1333,7 @@ siginterrupt SIGFE
 sigismember SIGFE
 siglongjmp NOSIGFE
 signal SIGFE
+signalfd SIGFE
 significand NOSIGFE
 significandf NOSIGFE
 sigpause SIGFE
diff --git a/winsup/cygwin/devices.cc b/winsup/cygwin/devices.cc
index a830b32..31fd64f 100644
--- a/winsup/cygwin/devices.cc
+++ b/winsup/cygwin/devices.cc
@@ -120,6 +120,9 @@ const _device dev_piper_storage =
 const _device dev_pipew_storage =
   {"", {FH_PIPEW}, "", exists_internal};
 
+const _device dev_signalfd_storage =
+  {"", {FH_SIGNALFD}, "", exists_internal};
+
 const _device dev_socket_storage =
   {"", {FH_SOCKET}, "", exists_internal};
 
diff --git a/winsup/cygwin/devices.h b/winsup/cygwin/devices.h
index 0c1bf26..065f77e 100644
--- a/winsup/cygwin/devices.h
+++ b/winsup/cygwin/devices.h
@@ -72,6 +72,8 @@ enum fh_devices
   FH_DEV     = FHDEV (DEV_VIRTFS_MAJOR, 193),
   FH_CYGDRIVE= FHDEV (DEV_VIRTFS_MAJOR, 192),
 
+  FH_SIGNALFD= FHDEV (DEV_VIRTFS_MAJOR, 13),
+
   DEV_FLOPPY_MAJOR = 2,
   FH_FLOPPY  = FHDEV (DEV_FLOPPY_MAJOR, 0),
 
@@ -400,6 +402,8 @@ extern const _device dev_af_local_storage;
 extern const _device dev_af_unix_storage;
 #define af_unix_dev ((device *) &dev_af_unix_storage)
 
+extern const _device dev_signalfd_storage;
+#define signalfd_dev ((device *) &dev_signalfd_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 9ad8738..79a7fe7 100644
--- a/winsup/cygwin/devices.in
+++ b/winsup/cygwin/devices.in
@@ -116,6 +116,9 @@ const _device dev_piper_storage =
 const _device dev_pipew_storage =
   {"", {FH_PIPEW}, "", exists_internal};
 
+const _device dev_signalfd_storage =
+  {"", {FH_SIGNALFD}, "", exists_internal};
+
 const _device dev_socket_storage =
   {"", {FH_SOCKET}, "", exists_internal};
 
diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index 780deb5..c8aecfa 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -575,6 +575,9 @@ fh_alloc (path_conv& pc)
 	case FH_CYGDRIVE:
 	  fh = cnew (fhandler_cygdrive);
 	  break;
+	case FH_SIGNALFD:
+	  fh = cnew (fhandler_signalfd);
+	  break;
 	case FH_TTY:
 	  if (!pc.isopen ())
 	    {
diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc
index 9643373..2c1fcb7 100644
--- a/winsup/cygwin/fhandler.cc
+++ b/winsup/cygwin/fhandler.cc
@@ -1345,6 +1345,9 @@ fhandler_base::fstat (struct stat *buf)
     case FH_PIPER:
       buf->st_mode = S_IFIFO | S_IRUSR;
       break;
+    case FH_SIGNALFD:
+      buf->st_mode = S_IRUSR | S_IWUSR;
+      break;
     default:
       buf->st_mode = S_IFCHR | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH;
       break;
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index ec111de..a908964 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -420,6 +420,7 @@ public:
   virtual class fhandler_socket *is_socket () { return NULL; }
   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 int is_windows () {return 0; }
 
   virtual void __reg3 raw_read (void *ptr, size_t& ulen);
@@ -2633,6 +2634,44 @@ class fhandler_procnet: public fhandler_proc
   }
 };
 
+class fhandler_signalfd : public fhandler_base
+{
+  sigset_t sigset;
+
+ public:
+  fhandler_signalfd ();
+  fhandler_signalfd (void *) {}
+
+  fhandler_signalfd *is_signalfd () { return this; }
+
+  char *get_proc_fd_name (char *buf);
+
+  int signalfd (const sigset_t *mask, int flags);
+  int __reg2 fstat (struct stat *buf);
+  void __reg3 read (void *ptr, size_t& len);
+
+  int poll ();
+
+  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_signalfd *> (x) = *this;
+    x->reset (this);
+  }
+
+  fhandler_signalfd *clone (cygheap_types malloc_type = HEAP_FHANDLER)
+  {
+    void *ptr = (void *) ccalloc (malloc_type, 1, sizeof (fhandler_signalfd));
+    fhandler_signalfd *fh = new (ptr) fhandler_signalfd (ptr);
+    copyto (fh);
+    return fh;
+  }
+};
+
 struct fhandler_nodevice: public fhandler_base
 {
   fhandler_nodevice ();
@@ -2672,6 +2711,7 @@ typedef union
   char __pty_master[sizeof (fhandler_pty_master)];
   char __registry[sizeof (fhandler_registry)];
   char __serial[sizeof (fhandler_serial)];
+  char __signalfd[sizeof (fhandler_signalfd)];
   char __socket_inet[sizeof (fhandler_socket_inet)];
   char __socket_local[sizeof (fhandler_socket_local)];
 #ifdef __WITH_AF_UNIX
diff --git a/winsup/cygwin/fhandler_signalfd.cc b/winsup/cygwin/fhandler_signalfd.cc
new file mode 100644
index 0000000..ec80948
--- /dev/null
+++ b/winsup/cygwin/fhandler_signalfd.cc
@@ -0,0 +1,153 @@
+/* fhandler_signalfd.cc: fhandler for /proc/<pid>/fd/<desc> operations
+
+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 "sigproc.h"
+#include <cygwin/signal.h>
+#include <sys/signalfd.h>
+
+fhandler_signalfd::fhandler_signalfd () :
+  fhandler_base (),
+  sigset (0)
+{
+}
+
+char *
+fhandler_signalfd::get_proc_fd_name (char *buf)
+{
+  return strcpy (buf, "anon_inode:[signalfd]");
+}
+
+int
+fhandler_signalfd::signalfd (const sigset_t *mask, int flags)
+{
+  __try
+    {
+      sigset = *mask & ~(SIGKILL | SIGSTOP);
+    }
+  __except (EINVAL)
+    {
+      return -1;
+    }
+  __endtry
+  if (flags & SFD_NONBLOCK)
+    set_nonblocking (true);
+  if (flags & SFD_CLOEXEC)
+    set_close_on_exec (true);
+  if (get_unique_id () == 0)
+    {
+      nohandle (true);
+      set_unique_id ();
+      set_ino (get_unique_id ());
+    }
+  return 0;
+}
+
+int __reg2
+fhandler_signalfd::fstat (struct stat *buf)
+{
+  int ret = fhandler_base::fstat (buf);
+  if (!ret)
+    {
+      buf->st_dev = FH_SIGNALFD;
+      buf->st_ino = get_unique_id ();
+    }
+  return ret;
+}
+
+static inline void
+copy_siginfo_to_signalfd (struct signalfd_siginfo *sfd,
+			  const siginfo_t * const si)
+{
+  sfd->ssi_signo = si->si_signo;
+  sfd->ssi_errno = si->si_errno;
+  sfd->ssi_code = si->si_code;
+  sfd->ssi_pid = si->si_pid;
+  sfd->ssi_uid = si->si_uid;
+  sfd->ssi_fd = -1;
+  sfd->ssi_tid = si->si_tid;
+  sfd->ssi_band = 0;
+  sfd->ssi_overrun = si->si_overrun;
+  sfd->ssi_trapno = 0;
+  sfd->ssi_status = si->si_status;
+  sfd->ssi_int = si->si_value.sival_int;
+  sfd->ssi_ptr = (uint64_t) si->si_value.sival_ptr;
+  sfd->ssi_utime = si->si_utime;
+  sfd->ssi_stime = si->si_stime;
+  sfd->ssi_addr = (uint64_t) si->si_addr;
+}
+
+void __reg3
+fhandler_signalfd::read (void *ptr, size_t& len)
+{
+  const LARGE_INTEGER poll = { QuadPart : 0 };
+  siginfo_t si;
+  int ret, old_errno;
+  size_t curlen = 0;
+  signalfd_siginfo *sfd_ptr = (signalfd_siginfo *) ptr;
+
+  if (len < sizeof (struct signalfd_siginfo))
+    {
+      set_errno (EINVAL);
+      len = (size_t) -1;
+      return;
+    }
+  old_errno = get_errno ();
+  do
+    {
+      /* Even when read is blocking, only one pending signal is actually
+	 required to return.  Subsequently use sigtimedwait to just poll
+	 if some more signal is available. */
+      ret = sigwait_common (&sigset, &si, (is_nonblocking () || curlen)
+					  ? (PLARGE_INTEGER) &poll : NULL);
+      if (ret == -1)
+	{
+	  if (curlen == 0)
+	    {
+	      if (get_errno () == EINTR && curlen == 0)
+		continue;
+	      set_errno (old_errno);
+	    }
+	  len = curlen ?: (size_t) -1;
+	  return;
+	}
+      __try
+	{
+	  copy_siginfo_to_signalfd (sfd_ptr, &si);
+	}
+      __except (EFAULT)
+	{
+	  len = (size_t) -1;
+	  return;
+	}
+      __endtry
+      sfd_ptr++;
+      curlen += sizeof (*sfd_ptr);
+    }
+  while ((len - curlen >= sizeof (struct signalfd_siginfo)));
+  set_errno (old_errno);
+  len = curlen;
+  return;
+}
+
+int
+fhandler_signalfd::poll ()
+{
+  Sleep (1L);	/* BAD HACK, FIXME, need a non-polling technique. */
+  sigset_t outset = (sigset_t) sig_send (myself, __SIGPENDING, &_my_tls);
+  if (outset == SIG_BAD_MASK)
+    return -1;
+  if ((outset & sigset) != 0)
+    return 0;
+  return -1;
+}
diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h
index 1bcec33..ec5f55f 100644
--- a/winsup/cygwin/include/cygwin/version.h
+++ b/winsup/cygwin/include/cygwin/version.h
@@ -502,12 +502,13 @@ details. */
   330: Add CLOCK_REALTIME_COARSE, CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_COARSE,
        CLOCK_BOOTTIME.
   331: Add timer_getoverrun, DELAYTIMER_MAX.
+  332: Add signalfd.
 
   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 331
+#define CYGWIN_VERSION_API_MINOR 332
 
 /* 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/signalfd.h b/winsup/cygwin/include/sys/signalfd.h
new file mode 100644
index 0000000..f4e5bd5
--- /dev/null
+++ b/winsup/cygwin/include/sys/signalfd.h
@@ -0,0 +1,54 @@
+/* sys/signalfd.h: define signalfd(2) and struct signalfd_siginfo
+
+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_SIGNALFD_H
+#define	_SYS_SIGNALFD_H
+
+#include <stdint.h>
+#include <sys/_default_fcntl.h>
+
+enum
+{
+  SFD_CLOEXEC = O_CLOEXEC,
+  SFD_NONBLOCK = O_NONBLOCK
+};
+#define SFD_CLOEXEC SFD_CLOEXEC
+#define SFD_NONBLOCK SFD_NONBLOCK
+
+struct signalfd_siginfo
+{
+  uint32_t	ssi_signo;
+  int32_t	ssi_errno;
+  int32_t	ssi_code;
+  uint32_t	ssi_pid;
+  uint32_t	ssi_uid;
+  int32_t	ssi_fd;
+  uint32_t	ssi_tid;
+  uint32_t	ssi_band;
+  uint32_t	ssi_overrun;
+  uint32_t	ssi_trapno;
+  int32_t	ssi_status;
+  int32_t	ssi_int;
+  uint64_t	ssi_ptr;
+  uint64_t	ssi_utime;
+  uint64_t	ssi_stime;
+  uint64_t	ssi_addr;
+  uint8_t	pad[48];
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int signalfd (int, const sigset_t *, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_SIGNALFD_H */
diff --git a/winsup/cygwin/release/2.12.0 b/winsup/cygwin/release/2.12.0
index dbda788..299ea6d 100644
--- a/winsup/cygwin/release/2.12.0
+++ b/winsup/cygwin/release/2.12.0
@@ -27,7 +27,7 @@ What's new:
 - Support overrun counter for posix timers (via timer_getoverrun() or
   siginfo_t::si_overrun).
 
-- New API: timer_getoverrun.
+- New API: signalfd, timer_getoverrun.
 
 
 What changed:
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index 6ce679a..3927b98 100644
--- a/winsup/cygwin/select.cc
+++ b/winsup/cygwin/select.cc
@@ -1731,3 +1731,68 @@ fhandler_windows::select_except (select_stuff *ss)
   s->windows_handle = true;
   return s;
 }
+
+static int
+peek_signalfd (select_record *me, bool)
+{
+  if (((fhandler_signalfd *) me->fh)->poll () == 0)
+    {
+      select_printf ("signalfd %d ready", me->fd);
+      return 1;
+    }
+
+  select_printf ("signalfd %d not ready", me->fd);
+  return 0;
+}
+
+static int
+verify_signalfd (select_record *me, fd_set *rfds, fd_set *wfds,
+		fd_set *efds)
+{
+  return peek_signalfd (me, true);
+}
+
+select_record *
+fhandler_signalfd::select_read (select_stuff *ss)
+{
+  select_record *s = ss->start.next;
+  if (!s->startup)
+    {
+      s->startup = no_startup;
+    }
+  s->verify = verify_signalfd;
+  s->peek = peek_signalfd;
+  s->read_selected = true;
+  s->read_ready = true;
+  return s;
+}
+
+select_record *
+fhandler_signalfd::select_write (select_stuff *ss)
+{
+  select_record *s = ss->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_signalfd::select_except (select_stuff *ss)
+{
+  select_record *s = ss->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/signal.cc b/winsup/cygwin/signal.cc
index c5e9d01..5dee402 100644
--- a/winsup/cygwin/signal.cc
+++ b/winsup/cygwin/signal.cc
@@ -12,6 +12,7 @@ details. */
 #include "winsup.h"
 #include <stdlib.h>
 #include <sys/cygwin.h>
+#include <sys/signalfd.h>
 #include "pinfo.h"
 #include "sigproc.h"
 #include "cygtls.h"
@@ -592,7 +593,7 @@ siginterrupt (int sig, int flag)
   return res;
 }
 
-static inline int
+int
 sigwait_common (const sigset_t *set, siginfo_t *info, PLARGE_INTEGER waittime)
 {
   int res = -1;
@@ -781,3 +782,62 @@ sigaltstack (const stack_t *ss, stack_t *oss)
   __endtry
   return 0;
 }
+
+extern "C" int
+signalfd (int fd_in, const sigset_t *mask, int flags)
+{
+  int ret = -1;
+  fhandler_signalfd *fh;
+
+  debug_printf ("signalfd (%d, %p, %y)", fd_in, mask, flags);
+
+  if ((flags & ~(SFD_NONBLOCK | SFD_CLOEXEC)) != 0)
+    {
+      set_errno (EINVAL);
+      goto done;
+    }
+
+  if (fd_in != -1)
+    {
+      /* Change signal mask. */
+      cygheap_fdget fd (fd_in);
+
+      if (fd < 0)
+	goto done;
+      fh = fd->is_signalfd ();
+      if (!fh)
+	{
+	  set_errno (EINVAL);
+	  goto done;
+	}
+      __try
+        {
+	  if (fh->signalfd (mask, flags) == 0)
+	    ret = fd_in;
+	}
+      __except (EINVAL) {}
+      __endtry
+    }
+  else
+    {
+      /* Create new signalfd descriptor. */
+      cygheap_fdnew fd;
+
+      if (fd < 0)
+	goto done;
+      fh = (fhandler_signalfd *) build_fh_dev (*signalfd_dev);
+      if (fh && fh->signalfd (mask, flags) == 0)
+	{
+	  fd = fh;
+	  if (fd <= 2)
+	    set_std_handle (fd);
+	  ret = fd;
+	}
+      else
+	delete fh;
+    }
+
+done:
+  syscall_printf ("%R = signalfd (%d, %p, %y)", ret, fd_in, mask, flags);
+  return ret;
+}
diff --git a/winsup/cygwin/sigproc.h b/winsup/cygwin/sigproc.h
index f2cd113..9beb31d 100644
--- a/winsup/cygwin/sigproc.h
+++ b/winsup/cygwin/sigproc.h
@@ -79,6 +79,7 @@ void __stdcall sigalloc ();
 int kill_pgrp (pid_t, siginfo_t&);
 void __reg1 exit_thread (DWORD) __attribute__ ((noreturn));
 void __reg1 setup_signal_exit (int);
+int sigwait_common (const sigset_t *, siginfo_t *, PLARGE_INTEGER);
 
 class no_thread_exit_protect
 {
diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml
index 2e1645f..1707130 100644
--- a/winsup/doc/new-features.xml
+++ b/winsup/doc/new-features.xml
@@ -51,7 +51,7 @@ siginfo_t::si_overrun).
 </para></listitem>
 
 <listitem><para>
-New API: timer_getoverrun.
+New API: signalfd, timer_getoverrun.
 </para></listitem>
 
 <listitem><para>
diff --git a/winsup/doc/posix.xml b/winsup/doc/posix.xml
index 021b596..365a8f0 100644
--- a/winsup/doc/posix.xml
+++ b/winsup/doc/posix.xml
@@ -1378,6 +1378,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
     scandirat
     sched_getcpu
     setxattr
+    signalfd
     sincos
     sincosf
     sincosl



More information about the Cygwin-cvs mailing list