[PATCH v5 2/3] Cygwin: cygwait: Make cygwait() reentrant

Takashi Yano takashi.yano@nifty.ne.jp
Mon Jan 20 17:47:56 GMT 2025


To allow cygwait() to be called in the signal handler, a locally
created timer is used instead of _cygtls::locals.cw_timer if it is
in use.

Co-Authored-By: Corinna Vinschen <corinna@vinschen.de>
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
---
 winsup/cygwin/cygtls.cc               |  2 ++
 winsup/cygwin/cygwait.cc              | 23 ++++++++++++++++-------
 winsup/cygwin/local_includes/cygtls.h |  3 ++-
 winsup/cygwin/select.cc               | 21 +++++++++++++++------
 4 files changed, 35 insertions(+), 14 deletions(-)

diff --git a/winsup/cygwin/cygtls.cc b/winsup/cygwin/cygtls.cc
index bfaa19867..1134adc3e 100644
--- a/winsup/cygwin/cygtls.cc
+++ b/winsup/cygwin/cygtls.cc
@@ -64,6 +64,7 @@ _cygtls::init_thread (void *x, DWORD (*func) (void *, void *))
   initialized = CYGTLS_INITIALIZED;
   errno_addr = &(local_clib._errno);
   locals.cw_timer = NULL;
+  locals.cw_timer_inuse = false;
   locals.pathbufs.clear ();
 
   if ((void *) func == (void *) cygthread::stub
@@ -85,6 +86,7 @@ _cygtls::fixup_after_fork ()
   signal_arrived = NULL;
   locals.select.sockevt = NULL;
   locals.cw_timer = NULL;
+  locals.cw_timer_inuse = false;
   locals.pathbufs.clear ();
   wq.thread_ev = NULL;
 }
diff --git a/winsup/cygwin/cygwait.cc b/winsup/cygwin/cygwait.cc
index 80c0e971c..e9ac420e4 100644
--- a/winsup/cygwin/cygwait.cc
+++ b/winsup/cygwin/cygwait.cc
@@ -58,16 +58,21 @@ cygwait (HANDLE object, PLARGE_INTEGER timeout, unsigned mask)
     }
 
   DWORD timeout_n;
+  HANDLE *wait_timer = &_my_tls.locals.cw_timer;
+  HANDLE local_wait_timer = NULL;
   if (!timeout)
     timeout_n = WAIT_TIMEOUT + 1;
   else
     {
+      if (_my_tls.locals.cw_timer_inuse)
+	wait_timer = &local_wait_timer;
+      else
+	_my_tls.locals.cw_timer_inuse = true;
       timeout_n = WAIT_OBJECT_0 + num++;
-      if (!_my_tls.locals.cw_timer)
-	NtCreateTimer (&_my_tls.locals.cw_timer, TIMER_ALL_ACCESS, NULL,
-		       NotificationTimer);
-      NtSetTimer (_my_tls.locals.cw_timer, timeout, NULL, NULL, FALSE, 0, NULL);
-      wait_objects[timeout_n] = _my_tls.locals.cw_timer;
+      if (!*wait_timer)
+	NtCreateTimer (wait_timer, TIMER_ALL_ACCESS, NULL, NotificationTimer);
+      NtSetTimer (*wait_timer, timeout, NULL, NULL, FALSE, 0, NULL);
+      wait_objects[timeout_n] = *wait_timer;
     }
 
   while (1)
@@ -100,7 +105,7 @@ cygwait (HANDLE object, PLARGE_INTEGER timeout, unsigned mask)
     {
       TIMER_BASIC_INFORMATION tbi;
 
-      NtQueryTimer (_my_tls.locals.cw_timer, TimerBasicInformation, &tbi,
+      NtQueryTimer (*wait_timer, TimerBasicInformation, &tbi,
 		    sizeof tbi, NULL);
       /* if timer expired, TimeRemaining is negative and represents the
 	  system uptime when signalled */
@@ -108,7 +113,11 @@ cygwait (HANDLE object, PLARGE_INTEGER timeout, unsigned mask)
 	timeout->QuadPart = tbi.SignalState || tbi.TimeRemaining.QuadPart < 0LL
                             ? 0LL : tbi.TimeRemaining.QuadPart;
       }
-      NtCancelTimer (_my_tls.locals.cw_timer, NULL);
+      NtCancelTimer (*wait_timer, NULL);
+      if (local_wait_timer)
+	NtClose(local_wait_timer);
+      else
+	_my_tls.locals.cw_timer_inuse = false;
     }
 
   if (res == WAIT_CANCELED && is_cw_cancel_self)
diff --git a/winsup/cygwin/local_includes/cygtls.h b/winsup/cygwin/local_includes/cygtls.h
index e0de712f4..d814814b1 100644
--- a/winsup/cygwin/local_includes/cygtls.h
+++ b/winsup/cygwin/local_includes/cygtls.h
@@ -135,6 +135,7 @@ struct _local_storage
 
   /* thread.cc */
   HANDLE cw_timer;
+  bool cw_timer_inuse;
 
   tls_pathbuf pathbufs;
   char ttybuf[32];
@@ -180,7 +181,7 @@ public: /* Do NOT remove this public: line, it's a marker for gentls_offsets. */
   siginfo_t *sigwait_info;
   HANDLE signal_arrived;
   bool will_wait_for_signal;
-#if 0
+#if 1
   long __align;			/* Needed to align context to 16 byte. */
 #endif
   /* context MUST be aligned to 16 byte, otherwise RtlCaptureContext fails.
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index a440a98d4..e44d42a0d 100644
--- a/winsup/cygwin/select.cc
+++ b/winsup/cygwin/select.cc
@@ -385,13 +385,18 @@ next_while:;
      to create the timer once per thread.  Since WFMO checks the handles
      in order, we append the timer as last object, otherwise it's preferred
      over actual events on the descriptors. */
-  HANDLE &wait_timer = _my_tls.locals.cw_timer;
+  HANDLE *wait_timer = &_my_tls.locals.cw_timer;
+  HANDLE local_wait_timer = NULL;
   if (us > 0LL)
     {
       NTSTATUS status;
-      if (!wait_timer)
+      if (_my_tls.locals.cw_timer_inuse)
+	wait_timer = &local_wait_timer;
+      else
+	_my_tls.locals.cw_timer_inuse = true;
+      if (!*wait_timer)
 	{
-	  status = NtCreateTimer (&wait_timer, TIMER_ALL_ACCESS, NULL,
+	  status = NtCreateTimer (wait_timer, TIMER_ALL_ACCESS, NULL,
 				  NotificationTimer);
 	  if (!NT_SUCCESS (status))
 	    {
@@ -400,7 +405,7 @@ next_while:;
 	    }
 	}
       LARGE_INTEGER ms_clock_ticks = { .QuadPart = -us * 10 };
-      status = NtSetTimer (wait_timer, &ms_clock_ticks, NULL, NULL, FALSE,
+      status = NtSetTimer (*wait_timer, &ms_clock_ticks, NULL, NULL, FALSE,
 			   0, NULL);
       if (!NT_SUCCESS (status))
 	{
@@ -408,7 +413,7 @@ next_while:;
 			 status, ms_clock_ticks.QuadPart);
 	  return select_error;
 	}
-      w4[m] = wait_timer;
+      w4[m] = *wait_timer;
       timer_idx = m++;
     }
 
@@ -430,7 +435,11 @@ next_while:;
   if (timer_idx)
     {
       BOOLEAN current_state;
-      NtCancelTimer (wait_timer, &current_state);
+      NtCancelTimer (*wait_timer, &current_state);
+      if (local_wait_timer)
+	NtClose (local_wait_timer);
+      else
+	_my_tls.locals.cw_timer_inuse = false;
     }
 
   wait_states res;
-- 
2.45.1



More information about the Cygwin-patches mailing list