sigtimedwait also needs to clean up preemptors and the blocked mask before
returning EAGAIN.

Also add some sigtimedwait testing.
---
 signal/Makefile                   |  1 +
 signal/tst-sigtimedwait-timeout.c | 99 +++++++++++++++++++++++++++++++
 sysdeps/mach/hurd/sigtimedwait.c  | 16 ++---
 3 files changed, 108 insertions(+), 8 deletions(-)
 create mode 100644 signal/tst-sigtimedwait-timeout.c

diff --git a/signal/Makefile b/signal/Makefile
index 7049c4b377..16050e82f6 100644
--- a/signal/Makefile
+++ b/signal/Makefile
@@ -61,6 +61,7 @@ tests := \
   tst-sigset2 \
   tst-sigsimple \
   tst-sigwait-eintr \
+  tst-sigtimedwait-timeout \
 # tests
 
 include ../Rules
diff --git a/signal/tst-sigtimedwait-timeout.c 
b/signal/tst-sigtimedwait-timeout.c
new file mode 100644
index 0000000000..5acb079538
--- /dev/null
+++ b/signal/tst-sigtimedwait-timeout.c
@@ -0,0 +1,99 @@
+/* Test that sigtimedwait time out cleans up correctly for further signaling
+   Copyright (C) 2017-2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <time.h>
+#include <unistd.h>
+
+static int signaled;
+
+/* Handler for SIGUSR1.  */
+static void
+sigusr1_handler (int signo)
+{
+  TEST_VERIFY (signo == SIGUSR1);
+  signaled++;
+}
+
+/* Spawn a subprocess to send two SIGUSR1 signals.
+   Return the PID of the process.  */
+static pid_t
+signal_sender (void)
+{
+  pid_t pid = xfork ();
+  if (pid == 0)
+    {
+      static const struct timespec delay = { .tv_sec = 1 };
+      if (nanosleep (&delay, NULL) != 0)
+        FAIL_EXIT1 ("nanosleep: %m");
+      if (kill (getppid (), SIGUSR1) != 0)
+        FAIL_EXIT1 ("kill (SIGUSR1): %m");
+      if (nanosleep (&delay, NULL) != 0)
+        FAIL_EXIT1 ("nanosleep: %m");
+      if (kill (getppid (), SIGUSR1) != 0)
+        FAIL_EXIT1 ("kill (SIGUSR1): %m");
+      _exit (0);
+    }
+  return pid;
+}
+
+static int
+do_test (void)
+{
+  if (signal (SIGUSR1, sigusr1_handler) == SIG_ERR)
+    FAIL_EXIT1 ("signal (SIGUSR1): %m\n");
+
+  sigset_t sigs;
+  sigemptyset (&sigs);
+  sigaddset (&sigs, SIGUSR1);
+  if (sigprocmask (SIG_BLOCK, &sigs, NULL) != 0)
+    FAIL_EXIT1 ("sigprocmask (SIGBLOCK, SIGUSR1): %m");
+  pid_t pid = signal_sender ();
+
+  siginfo_t info;
+  struct timespec ts = { .tv_nsec = 500000000 };
+  int ret = sigtimedwait (&sigs, &info, &ts);
+  TEST_VERIFY (ret == -1);
+  TEST_VERIFY (errno == EAGAIN);
+
+  static const struct timespec delay = { .tv_sec = 1 };
+  if (nanosleep (&delay, NULL) != 0)
+    FAIL_EXIT1 ("nanosleep: %m");
+  TEST_VERIFY (signaled == 0);
+
+  if (sigprocmask (SIG_UNBLOCK, &sigs, NULL) != 0)
+    FAIL_EXIT1 ("sigprocmask (SIGBLOCK, SIGUSR1): %m");
+  TEST_VERIFY (signaled == 1);
+
+  if (nanosleep (&delay, NULL) != -1)
+    FAIL_EXIT1 ("nanosleep: %m");
+  TEST_VERIFY (errno == EINTR);
+  TEST_VERIFY (signaled == 2);
+
+  int status;
+  xwaitpid (pid, &status, 0);
+  TEST_VERIFY (status == 0);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/mach/hurd/sigtimedwait.c b/sysdeps/mach/hurd/sigtimedwait.c
index 918af14a47..4ffdc1b2e6 100644
--- a/sysdeps/mach/hurd/sigtimedwait.c
+++ b/sysdeps/mach/hurd/sigtimedwait.c
@@ -147,17 +147,17 @@ __sigtimedwait (const sigset_t *set, siginfo_t *info,
       /* Timed out.  */
       signo = __hurd_fail (EAGAIN);
     }
-  else
-    {
-      assert (signo);
 
-      _hurd_sigstate_lock (ss);
+  assert (signo);
 
-      /* Delete our preemptor. */
-      assert (ss->preemptors == &preemptor);
-      ss->preemptors = preemptor.next;
-    }
+  _hurd_sigstate_lock (ss);
+
+  /* Delete our preemptor. */
+  assert (ss->preemptors == &preemptor);
+  ss->preemptors = preemptor.next;
 
+  /* Restore the blocking mask. */
+  ss->blocked = blocked;
 
 all_done:
   _hurd_sigstate_unlock (ss);
-- 
2.51.0


Reply via email to