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