[newlib-cygwin] Cygwin: signalfd: implement non-polling select

Corinna Vinschen corinna@sourceware.org
Mon Jan 14 16:19:00 GMT 2019


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

commit f42776fa781de858a927bc03aa966a0f3096b581
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Mon Jan 14 17:19:37 2019 +0100

    Cygwin: signalfd: implement non-polling select
    
    Allow the signal thread to recognize we're called in consequence of
    select on a signalfd.  If the signal is part of the wait mask, don't
    call any signal handler and don't remove the signal from the queue,
    so a subsequent read (or sigwaitinfo/sigtimedwait) still gets the
    signal.  Instead, just signal the event object at
    _cygtls::signalfd_select_wait for the thread running select.
    
    The addition of signalfd_select_wait to _cygtls unearthed the alignment
    problem of the context member again.  To make sure this doesn't get lost,
    improve the related comment in the header file so that this (hopefully)
    doesn't get lost (again).
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/cygtls.h             |  5 +-
 winsup/cygwin/exceptions.cc        |  8 ++++
 winsup/cygwin/fhandler.h           |  1 +
 winsup/cygwin/fhandler_signalfd.cc |  2 +-
 winsup/cygwin/select.cc            | 98 +++++++++++++++++++++++++++++++++-----
 winsup/cygwin/select.h             | 12 ++++-
 winsup/cygwin/signal.cc            |  4 +-
 winsup/cygwin/tlsoffsets.h         | 16 +++----
 winsup/cygwin/tlsoffsets64.h       | 16 +++----
 9 files changed, 129 insertions(+), 33 deletions(-)

diff --git a/winsup/cygwin/cygtls.h b/winsup/cygwin/cygtls.h
index 39dba13..65a905c 100644
--- a/winsup/cygwin/cygtls.h
+++ b/winsup/cygwin/cygtls.h
@@ -189,8 +189,11 @@ public:
   stack_t altstack;
   siginfo_t *sigwait_info;
   HANDLE signal_arrived;
+  HANDLE signalfd_select_wait;
   bool will_wait_for_signal;
-  long __align;			/* Needed to align context to 16 byte. */
+  /* context MUST be aligned to 16 byte, otherwise RtlCaptureContext fails.
+     If you prepend cygtls members here, make sure context stays 16 byte
+     aligned. */
   ucontext_t context;
   DWORD thread_id;
   siginfo_t infodata;
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
index 8c1b3b4..205ad85 100644
--- a/winsup/cygwin/exceptions.cc
+++ b/winsup/cygwin/exceptions.cc
@@ -1469,6 +1469,14 @@ sigpacket::process ()
   if (issig_wait)
     {
       tls->sigwait_mask = 0;
+      /* If the catching thread is running select on a signalfd, don't call
+	 the signal handler and don't remove the signal from the queue. */
+      if (tls->signalfd_select_wait)
+	{
+	  SetEvent (tls->signalfd_select_wait);
+	  rc = 0;
+	  goto done;
+	}
       goto dosig;
     }
 
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index a908964..f4a8b54 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -2651,6 +2651,7 @@ class fhandler_signalfd : public fhandler_base
   void __reg3 read (void *ptr, size_t& len);
 
   int poll ();
+  inline sigset_t get_sigset () const { return sigset; }
 
   select_record *select_read (select_stuff *);
   select_record *select_write (select_stuff *);
diff --git a/winsup/cygwin/fhandler_signalfd.cc b/winsup/cygwin/fhandler_signalfd.cc
index ec80948..1bb0d14 100644
--- a/winsup/cygwin/fhandler_signalfd.cc
+++ b/winsup/cygwin/fhandler_signalfd.cc
@@ -140,10 +140,10 @@ fhandler_signalfd::read (void *ptr, size_t& len)
   return;
 }
 
+/* Called from select */
 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;
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index 3927b98..d1ae3c6 100644
--- a/winsup/cygwin/select.cc
+++ b/winsup/cygwin/select.cc
@@ -1732,45 +1732,119 @@ fhandler_windows::select_except (select_stuff *ss)
   return s;
 }
 
+static int start_thread_signalfd (select_record *, select_stuff *);
+
+static DWORD WINAPI
+thread_signalfd (void *arg)
+{
+  select_signalfd_info *si = (select_signalfd_info *) arg;
+  bool event = false;
+
+  while (!event)
+    {
+      sigset_t set = 0;
+      _cygtls *tls = si->start->tls;
+
+      for (select_record *s = si->start; (s = s->next); )
+	if (s->startup == start_thread_signalfd)
+	  set |= ((fhandler_signalfd *) s->fh)->get_sigset ();
+      set_signal_mask (tls->sigwait_mask, set);
+      tls->signalfd_select_wait = si->evt;
+      sig_dispatch_pending (true);
+      switch (WaitForSingleObject (si->evt, INFINITE))
+	{
+	case WAIT_OBJECT_0:
+	  tls->signalfd_select_wait = NULL;
+	  event = true;
+	  break;
+	default:
+	  break;
+	}
+      if (si->stop_thread)
+	break;
+      if (!event)
+	Sleep (1L);
+    }
+  CloseHandle (si->evt);
+  return 0;
+}
+
+static int
+start_thread_signalfd (select_record *me, select_stuff *stuff)
+{
+  select_signalfd_info *si;
+
+  if ((si = stuff->device_specific_signalfd))
+    {
+      me->h = *si->thread;
+      return 1;
+    }
+  si = new select_signalfd_info;
+  si->start = &stuff->start;
+  si->stop_thread = false;
+  si->evt = CreateEventW (&sec_none_nih, TRUE, FALSE, NULL);
+  si->thread = new cygthread (thread_signalfd, si, "signalfdsel");
+  me->h = *si->thread;
+  stuff->device_specific_signalfd = si;
+  return 1;
+}
+
+static void
+signalfd_cleanup (select_record *, select_stuff *stuff)
+{
+  select_signalfd_info *si;
+
+  if (!(si = stuff->device_specific_signalfd))
+    return;
+  if (si->thread)
+    {
+      si->stop_thread = true;
+      SetEvent (si->evt);
+      si->thread->detach ();
+    }
+  delete si;
+  stuff->device_specific_signalfd = NULL;
+}
+
 static int
 peek_signalfd (select_record *me, bool)
 {
   if (((fhandler_signalfd *) me->fh)->poll () == 0)
     {
       select_printf ("signalfd %d ready", me->fd);
+      me->read_ready = true;
       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)
+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)
+fhandler_signalfd::select_read (select_stuff *stuff)
 {
-  select_record *s = ss->start.next;
+  select_record *s = stuff->start.next;
   if (!s->startup)
     {
-      s->startup = no_startup;
+      s->startup = start_thread_signalfd;
+      s->verify = verify_signalfd;
+      s->cleanup = signalfd_cleanup;
     }
-  s->verify = verify_signalfd;
   s->peek = peek_signalfd;
   s->read_selected = true;
-  s->read_ready = true;
+  s->read_ready = false;
   return s;
 }
 
 select_record *
-fhandler_signalfd::select_write (select_stuff *ss)
+fhandler_signalfd::select_write (select_stuff *stuff)
 {
-  select_record *s = ss->start.next;
+  select_record *s = stuff->start.next;
   if (!s->startup)
     {
       s->startup = no_startup;
@@ -1783,9 +1857,9 @@ fhandler_signalfd::select_write (select_stuff *ss)
 }
 
 select_record *
-fhandler_signalfd::select_except (select_stuff *ss)
+fhandler_signalfd::select_except (select_stuff *stuff)
 {
-  select_record *s = ss->start.next;
+  select_record *s = stuff->start.next;
   if (!s->startup)
     {
       s->startup = no_startup;
diff --git a/winsup/cygwin/select.h b/winsup/cygwin/select.h
index 9a737c3..71821f7 100644
--- a/winsup/cygwin/select.h
+++ b/winsup/cygwin/select.h
@@ -66,6 +66,12 @@ struct select_serial_info: public select_info
   select_serial_info (): select_info () {}
 };
 
+struct select_signalfd_info: public select_info
+{
+  HANDLE evt;
+  select_signalfd_info (): select_info () {}
+};
+
 class select_stuff
 {
 public:
@@ -85,6 +91,7 @@ public:
   select_pipe_info *device_specific_pipe;
   select_socket_info *device_specific_socket;
   select_serial_info *device_specific_serial;
+  select_signalfd_info *device_specific_signalfd;
 
   bool test_and_set (int, fd_set *, fd_set *, fd_set *);
   int poll (fd_set *, fd_set *, fd_set *);
@@ -93,10 +100,11 @@ public:
   void destroy ();
 
   select_stuff (): return_on_signal (false), always_ready (false),
-  		   windows_used (false), start (0),
+		   windows_used (false), start (),
 		   device_specific_pipe (NULL),
 		   device_specific_socket (NULL),
-		   device_specific_serial (NULL) {}
+		   device_specific_serial (NULL),
+		   device_specific_signalfd (NULL) {}
 };
 
 extern "C" int cygwin_select (int , fd_set *, fd_set *, fd_set *,
diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc
index 5dee402..5759cc4 100644
--- a/winsup/cygwin/signal.cc
+++ b/winsup/cygwin/signal.cc
@@ -603,9 +603,11 @@ sigwait_common (const sigset_t *set, siginfo_t *info, PLARGE_INTEGER waittime)
   __try
     {
       set_signal_mask (_my_tls.sigwait_mask, *set);
+      _my_tls.signalfd_select_wait = NULL;
       sig_dispatch_pending (true);
 
-      switch (cygwait (NULL, waittime, cw_sig_eintr | cw_cancel | cw_cancel_self))
+      switch (cygwait (NULL, waittime,
+		       cw_sig_eintr | cw_cancel | cw_cancel_self))
 	{
 	case WAIT_SIGNALED:
 	  if (!sigismember (set, _my_tls.infodata.si_signo))
diff --git a/winsup/cygwin/tlsoffsets.h b/winsup/cygwin/tlsoffsets.h
index 13d1003..8003a1f 100644
--- a/winsup/cygwin/tlsoffsets.h
+++ b/winsup/cygwin/tlsoffsets.h
@@ -29,10 +29,10 @@
 //; $tls::psigwait_info = 2852;
 //; $tls::signal_arrived = -9844;
 //; $tls::psignal_arrived = 2856;
-//; $tls::will_wait_for_signal = -9840;
-//; $tls::pwill_wait_for_signal = 2860;
-//; $tls::__align = -9836;
-//; $tls::p__align = 2864;
+//; $tls::signalfd_select_wait = -9840;
+//; $tls::psignalfd_select_wait = 2860;
+//; $tls::will_wait_for_signal = -9836;
+//; $tls::pwill_wait_for_signal = 2864;
 //; $tls::context = -9832;
 //; $tls::pcontext = 2868;
 //; $tls::thread_id = -9084;
@@ -91,10 +91,10 @@
 #define tls_psigwait_info (2852)
 #define tls_signal_arrived (-9844)
 #define tls_psignal_arrived (2856)
-#define tls_will_wait_for_signal (-9840)
-#define tls_pwill_wait_for_signal (2860)
-#define tls___align (-9836)
-#define tls_p__align (2864)
+#define tls_signalfd_select_wait (-9840)
+#define tls_psignalfd_select_wait (2860)
+#define tls_will_wait_for_signal (-9836)
+#define tls_pwill_wait_for_signal (2864)
 #define tls_context (-9832)
 #define tls_pcontext (2868)
 #define tls_thread_id (-9084)
diff --git a/winsup/cygwin/tlsoffsets64.h b/winsup/cygwin/tlsoffsets64.h
index d137408..7356221 100644
--- a/winsup/cygwin/tlsoffsets64.h
+++ b/winsup/cygwin/tlsoffsets64.h
@@ -29,10 +29,10 @@
 //; $tls::psigwait_info = 4144;
 //; $tls::signal_arrived = -8648;
 //; $tls::psignal_arrived = 4152;
-//; $tls::will_wait_for_signal = -8640;
-//; $tls::pwill_wait_for_signal = 4160;
-//; $tls::__align = -8632;
-//; $tls::p__align = 4168;
+//; $tls::signalfd_select_wait = -8640;
+//; $tls::psignalfd_select_wait = 4160;
+//; $tls::will_wait_for_signal = -8632;
+//; $tls::pwill_wait_for_signal = 4168;
 //; $tls::context = -8624;
 //; $tls::pcontext = 4176;
 //; $tls::thread_id = -7328;
@@ -91,10 +91,10 @@
 #define tls_psigwait_info (4144)
 #define tls_signal_arrived (-8648)
 #define tls_psignal_arrived (4152)
-#define tls_will_wait_for_signal (-8640)
-#define tls_pwill_wait_for_signal (4160)
-#define tls___align (-8632)
-#define tls_p__align (4168)
+#define tls_sigwait_for_select (-8640)
+#define tls_psigwait_for_select (4160)
+#define tls_will_wait_for_signal (-8632)
+#define tls_pwill_wait_for_signal (4168)
 #define tls_context (-8624)
 #define tls_pcontext (4176)
 #define tls_thread_id (-7328)



More information about the Cygwin-cvs mailing list