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

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

commit 17c5412bdf87497cf05688587782052974d40de0
Author: Marco Casaroli <[email protected]>
AuthorDate: Sun Oct 18 22:49:43 2026 +0200

    sched/semaphore: Do not spin in an uninterruptible wait for a signal.
    
    nxsem_wait_slow() returns -EINTR at once when an unmasked signal is
    pending, before it looks at the semaphore count.  During a system call,
    an unmasked signal stays pending until the call returns.  The kernel
    uses nxsem_wait_uninterruptible(), nxsem_clockwait_uninterruptible() and
    nxsem_tickwait_uninterruptible(), which retry on -EINTR, so each retry
    failed at once and the task looped without blocking.  A task with a
    lower priority on the same CPU did not run, and if that task had to
    post the semaphore, the system stopped.
    
    For example, pthread_join() waits for the thread exit with
    nxsem_wait_uninterruptible().  A signal with a handler to the joining
    thread made it loop until the joined thread exited.  If the joined
    thread had a lower priority, it never ran.
    
    Add the semaphore flag SEM_TYPE_UNINTERRUPTIBLE.  The uninterruptible
    waits set it in the kernel and in a flat build, and nxsem_wait_slow()
    does not do the pending signal check for such a semaphore, as for a
    mutex.  The flag stays set, because several tasks can wait on one
    semaphore at the same time.  In user space of a kernel or
    protected build the flag is not set: there, a pending signal is
    delivered when the system call returns, so a retry does not loop.
    
    A signal still wakes the task, so that a signal handler runs at once in
    a flat build.  The wait then blocks again.  The interruptible waits on
    other semaphores do not change: a pending signal still makes them return
    -EINTR before they block.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 include/nuttx/semaphore.h      | 13 +++++++++++++
 include/semaphore.h            |  1 +
 libs/libc/semaphore/sem_wait.c |  4 ++++
 sched/semaphore/sem_tickwait.c |  2 ++
 sched/semaphore/sem_wait.c     | 10 +++++-----
 5 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/include/nuttx/semaphore.h b/include/nuttx/semaphore.h
index 753186712d2..f417e1ea136 100644
--- a/include/nuttx/semaphore.h
+++ b/include/nuttx/semaphore.h
@@ -64,6 +64,19 @@
 #define NXSEM_COUNT(s)        ((FAR atomic_t *)&((s)->val.semcount))
 #define NXSEM_IS_MUTEX(s)     (((s)->flags & SEM_TYPE_MUTEX) != 0)
 
+/* The uninterruptible waits set SEM_TYPE_UNINTERRUPTIBLE: then a pending
+ * signal does not make nxsem_wait_slow() fail at once.  Not in user space:
+ * there, the flag would also stop a signal from ending sem_wait(), and the
+ * retry gets the signal when the system call returns.
+ */
+
+#if defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__)
+#  define nxsem_set_uninterruptible(s) \
+     ((s)->flags |= SEM_TYPE_UNINTERRUPTIBLE)
+#else
+#  define nxsem_set_uninterruptible(s)
+#endif
+
 /* Mutex related helper macros */
 
 #define NXSEM_MBLOCKING_BIT   ((uint32_t)0x80000000)
diff --git a/include/semaphore.h b/include/semaphore.h
index 0af204d54cb..4776bf0e9b1 100644
--- a/include/semaphore.h
+++ b/include/semaphore.h
@@ -46,6 +46,7 @@
 #define SEM_PRIO_MASK             3
 
 #define SEM_TYPE_MUTEX            4
+#define SEM_TYPE_UNINTERRUPTIBLE  8 /* The kernel waits without EINTR */
 
 /* Value returned by sem_open() in the event of a failure. */
 
diff --git a/libs/libc/semaphore/sem_wait.c b/libs/libc/semaphore/sem_wait.c
index 20b73a02f95..7300587cc04 100644
--- a/libs/libc/semaphore/sem_wait.c
+++ b/libs/libc/semaphore/sem_wait.c
@@ -222,6 +222,8 @@ int nxsem_wait_uninterruptible(FAR sem_t *sem)
 {
   int ret;
 
+  nxsem_set_uninterruptible(sem);
+
   do
     {
       /* Take the semaphore (perhaps waiting) */
@@ -262,6 +264,8 @@ int nxsem_clockwait_uninterruptible(FAR sem_t *sem, 
clockid_t clockid,
 {
   int ret;
 
+  nxsem_set_uninterruptible(sem);
+
   do
     {
       /* Take the semaphore (perhaps waiting) */
diff --git a/sched/semaphore/sem_tickwait.c b/sched/semaphore/sem_tickwait.c
index e436e23d389..ff27811864c 100644
--- a/sched/semaphore/sem_tickwait.c
+++ b/sched/semaphore/sem_tickwait.c
@@ -153,6 +153,8 @@ int nxsem_tickwait_uninterruptible(FAR sem_t *sem, uint32_t 
delay)
   clock_t end = clock_delay2abstick(delay);
   int ret;
 
+  nxsem_set_uninterruptible(sem);
+
   for (; ; )
     {
       /* Take the semaphore (perhaps waiting) */
diff --git a/sched/semaphore/sem_wait.c b/sched/semaphore/sem_wait.c
index 3493048af98..b1bb8c887ca 100644
--- a/sched/semaphore/sem_wait.c
+++ b/sched/semaphore/sem_wait.c
@@ -97,13 +97,13 @@ int nxsem_wait_slow(FAR sem_t *sem)
    * sem_wait_irq(). If sem_wait then blocks without re-checking unmasked
    * pending signals, it can sleep indefinitely and miss the interrupt.
    *
-   * Not for a mutex: a mutex wait is not interrupted by a signal, and the
-   * kernel retries it until it gets the mutex (mm_lock(), for example).
-   * During a system call an unmasked signal stays pending until the call
-   * returns, so the check would make every retry fail at once.
+   * Not for a mutex or an uninterruptible semaphore: the kernel retries
+   * them until it gets the semaphore (mm_lock(), for example).  During a
+   * system call an unmasked signal stays pending until the call returns,
+   * so the check would make every retry fail at once.
    */
 
-  if (!mutex)
+  if (!mutex && (sem->flags & SEM_TYPE_UNINTERRUPTIBLE) == 0)
     {
       pendingset = nxsig_pendingset(rtcb);
       nxsig_nandset(&pendingset, &pendingset, &rtcb->sigprocmask);

Reply via email to