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) {
