This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 278db9783a43c4231b10092ef6d7198e86d3a424
Author: Marco Casaroli <[email protected]>
AuthorDate: Tue Oct 13 13:12:33 2026 +0200

    sched/signal: Restore the mask of pselect() after its signals are 
dispatched.
    
    pselect(), ppoll() and sigsuspend() install a temporary signal mask and
    restore the old one before they return.  In a system call, a signal
    that the temporary mask unblocks is not delivered at once: it goes back
    on the list of pending signals, to be dispatched at the end of the
    system call.  By then the old mask was restored, so a signal that the
    old mask blocks stayed pending, and its handler never ran.  Every later
    pselect() found it and returned EINTR at once.
    
    For example, a program that blocks SIGCHLD and unblocks it only in
    pselect() never runs its SIGCHLD handler, and its pselect() loop spins.
    
    In a system call, keep the temporary mask and save the old one in the
    TCB.  nxsig_unmask_pendingsignal(), at the end of the system call,
    dispatches the pending signals with the temporary mask, which queues
    their actions, and then restores the old mask.  Outside a system call
    the mask is restored at once, as before.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 include/nuttx/sched.h                  |  2 +
 sched/signal/sig_ppoll.c               |  4 +-
 sched/signal/sig_pselect.c             |  4 +-
 sched/signal/sig_suspend.c             |  4 +-
 sched/signal/sig_unmaskpendingsignal.c | 76 ++++++++++++++++++++++++++++++++--
 sched/signal/signal.h                  |  2 +
 6 files changed, 82 insertions(+), 10 deletions(-)

diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h
index e77cd69ef84..053a2995f29 100644
--- a/include/nuttx/sched.h
+++ b/include/nuttx/sched.h
@@ -112,6 +112,7 @@
 #define TCB_FLAG_JOIN_COMPLETED    (1 << 14)                     /* Bit 14: 
Pthread join completed */
 #define TCB_FLAG_FREE_TCB          (1 << 15)                     /* Bit 15: 
Free tcb after exit */
 #define TCB_FLAG_PREEMPT_SCHED     (1 << 16)                     /* Bit 16: 
tcb is PREEMPT_SCHED */
+#define TCB_FLAG_SIGMASK_SAVED     (1 << 17)                     /* Bit 17: 
sigsavedmask to restore */
 
 /* Values for struct task_group tg_flags */
 
@@ -702,6 +703,7 @@ struct tcb_s
 #ifndef CONFIG_DISABLE_ALL_SIGNALS
   sigset_t   sigprocmask;                /* Signals that are blocked        */
   sigset_t   sigwaitmask;                /* Waiting for pending signals     */
+  sigset_t   sigsavedmask;               /* Mask to restore at syscall exit */
   siginfo_t  *sigunbinfo;                /* Signal info when task unblocked */
 #endif /* !CONFIG_DISABLE_ALL_SIGNALS */
 
diff --git a/sched/signal/sig_ppoll.c b/sched/signal/sig_ppoll.c
index c320bc1967d..567a4d9f3ef 100644
--- a/sched/signal/sig_ppoll.c
+++ b/sched/signal/sig_ppoll.c
@@ -105,7 +105,7 @@ int ppoll(FAR struct pollfd *fds, nfds_t nfds,
        * us to not wait. Restore the original sigprocmask.
        */
 
-      rtcb->sigprocmask = saved_sigprocmask;
+      nxsig_restoremask(rtcb, &saved_sigprocmask);
       leave_critical_section(flags);
       set_errno(EINTR);
     }
@@ -125,7 +125,7 @@ int ppoll(FAR struct pollfd *fds, nfds_t nfds,
 
       /* We are running again, restore the original sigprocmask */
 
-      rtcb->sigprocmask = saved_sigprocmask;
+      nxsig_restoremask(rtcb, &saved_sigprocmask);
       leave_critical_section(flags);
 
       /* Now, handle the (rare?) case where (a) a blocked signal was received
diff --git a/sched/signal/sig_pselect.c b/sched/signal/sig_pselect.c
index c660e379d11..b28096d12e6 100644
--- a/sched/signal/sig_pselect.c
+++ b/sched/signal/sig_pselect.c
@@ -102,7 +102,7 @@ int pselect(int nfds, FAR fd_set *readfds, FAR fd_set 
*writefds,
        * us to not wait. Restore the original sigprocmask.
        */
 
-      rtcb->sigprocmask = saved_sigprocmask;
+      nxsig_restoremask(rtcb, &saved_sigprocmask);
       leave_critical_section(flags);
       set_errno(EINTR);
     }
@@ -124,7 +124,7 @@ int pselect(int nfds, FAR fd_set *readfds, FAR fd_set 
*writefds,
 
       /* We are running again, restore the original sigprocmask */
 
-      rtcb->sigprocmask = saved_sigprocmask;
+      nxsig_restoremask(rtcb, &saved_sigprocmask);
       leave_critical_section(flags);
 
       /* Now, handle the (rare?) case where (a) a blocked signal was received
diff --git a/sched/signal/sig_suspend.c b/sched/signal/sig_suspend.c
index d7d14b52f00..4cd623b09ab 100644
--- a/sched/signal/sig_suspend.c
+++ b/sched/signal/sig_suspend.c
@@ -114,7 +114,7 @@ int sigsuspend(FAR const sigset_t *set)
        * us to not wait. Restore the original sigprocmask.
        */
 
-      rtcb->sigprocmask = saved_sigprocmask;
+      nxsig_restoremask(rtcb, &saved_sigprocmask);
       leave_critical_section(flags);
     }
   else
@@ -141,7 +141,7 @@ int sigsuspend(FAR const sigset_t *set)
 
       /* We are running again, restore the original sigprocmask */
 
-      rtcb->sigprocmask = saved_sigprocmask;
+      nxsig_restoremask(rtcb, &saved_sigprocmask);
       leave_critical_section(flags);
 
       /* Now, handle the (rare?) case where (a) a blocked signal was received
diff --git a/sched/signal/sig_unmaskpendingsignal.c 
b/sched/signal/sig_unmaskpendingsignal.c
index 704dad82a1d..1f79ea8420b 100644
--- a/sched/signal/sig_unmaskpendingsignal.c
+++ b/sched/signal/sig_unmaskpendingsignal.c
@@ -34,11 +34,11 @@
 #include "signal/signal.h"
 
 /****************************************************************************
- * Public Functions
+ * Private Functions
  ****************************************************************************/
 
 /****************************************************************************
- * Name: nxsig_unmask_pendingsignal
+ * Name: nxsig_dispatch_unmasked
  *
  * Description:
  *   Based upon the current setting of the sigprocmask, this function
@@ -47,9 +47,8 @@
  *
  ****************************************************************************/
 
-bool nxsig_unmask_pendingsignal(void)
+static bool nxsig_dispatch_unmasked(FAR struct tcb_s *rtcb)
 {
-  FAR struct tcb_s *rtcb = this_task();
   sigset_t unmaskedset;
   FAR sigpendq_t *pendingsig;
   int signo;
@@ -118,3 +117,72 @@ bool nxsig_unmask_pendingsignal(void)
   sched_unlock();
   return true;
 }
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxsig_unmask_pendingsignal
+ *
+ * Description:
+ *   Dispatch the pending signals that the signal mask of the calling thread
+ *   does not block.  At the end of a system call, also restore the mask
+ *   that nxsig_restoremask() saved, after the signals that the temporary
+ *   mask of pselect(), ppoll() or sigsuspend() unblocks are dispatched.
+ *
+ * Returned Value:
+ *   true if one or more signals were dispatched.
+ *
+ ****************************************************************************/
+
+bool nxsig_unmask_pendingsignal(void)
+{
+  FAR struct tcb_s *rtcb = this_task();
+  bool dispatched;
+
+  dispatched = nxsig_dispatch_unmasked(rtcb);
+
+  if ((rtcb->flags & (TCB_FLAG_SYSCALL | TCB_FLAG_SIGMASK_SAVED)) ==
+      TCB_FLAG_SIGMASK_SAVED)
+    {
+      rtcb->sigprocmask = rtcb->sigsavedmask;
+      rtcb->flags &= ~TCB_FLAG_SIGMASK_SAVED;
+
+      if (nxsig_dispatch_unmasked(rtcb))
+        {
+          dispatched = true;
+        }
+    }
+
+  return dispatched;
+}
+
+/****************************************************************************
+ * Name: nxsig_restoremask
+ *
+ * Description:
+ *   Restore the signal mask after the temporary mask of pselect(), ppoll()
+ *   or sigsuspend().  In a system call, a signal that the temporary mask
+ *   unblocked is still on the pending list: it is dispatched only at the
+ *   end of the system call.  So keep the temporary mask until then, and
+ *   let nxsig_unmask_pendingsignal() restore this one.
+ *
+ * Input Parameters:
+ *   rtcb - The calling thread.
+ *   mask - The signal mask to restore.
+ *
+ ****************************************************************************/
+
+void nxsig_restoremask(FAR struct tcb_s *rtcb, FAR const sigset_t *mask)
+{
+  if ((rtcb->flags & TCB_FLAG_SYSCALL) != 0)
+    {
+      rtcb->sigsavedmask = *mask;
+      rtcb->flags |= TCB_FLAG_SIGMASK_SAVED;
+    }
+  else
+    {
+      rtcb->sigprocmask = *mask;
+    }
+}
diff --git a/sched/signal/signal.h b/sched/signal/signal.h
index 46f64d56a52..1de4b8b66a8 100644
--- a/sched/signal/signal.h
+++ b/sched/signal/signal.h
@@ -213,6 +213,8 @@ FAR sigpendq_t    *nxsig_remove_pendingsignal(FAR struct 
tcb_s *stcb,
                                               int signo);
 #ifndef CONFIG_DISABLE_ALL_SIGNALS
 bool               nxsig_unmask_pendingsignal(void);
+void               nxsig_restoremask(FAR struct tcb_s *rtcb,
+                                     FAR const sigset_t *mask);
 #else
 static inline bool nxsig_unmask_pendingsignal(void)
 {

Reply via email to