This is an automated email from the ASF dual-hosted git repository.
anchao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new fa4e298e0fb sched/signal: fix missing EINTR when nxsig_clockwait is
interrupted
fa4e298e0fb is described below
commit fa4e298e0fb2d1692c39207a3c851cc796fb6cad
Author: Junbo Zheng <[email protected]>
AuthorDate: Sun Oct 18 00:49:38 2026 +0800
sched/signal: fix missing EINTR when nxsig_clockwait is interrupted
nxsig_clockwait() always returned OK after being awakened, so
nanosleep(), clock_nanosleep() and sleep() reported success when
interrupted by a caught, unblocked signal instead of -1/EINTR, and
the remaining time in *rmtp was invisible to retry loops.
Install a sigunbinfo buffer in nxsig_clockwait() when the caller did
not provide one (only nxsig_timedwait() does), then report -EINTR if
the wake came from a real signal and -ECANCELED on cancellation.
The new sigunbinfo accesses are guarded by
CONFIG_DISABLE_ALL_SIGNALS, matching the guard around
struct tcb_s::sigunbinfo in include/nuttx/sched.h: signal-disabled
configurations keep the previous behavior unchanged.
Testing: built and verified on the NuttX simulator (host:
Ubuntu 22.04):
cmake -B build -DBOARD_CONFIG=sim:nsh
cmake --build build -j$(nproc)
./build/nuttx
With a temporary test in examples/hello that installs a SIGUSR1
handler, spawns a task sending SIGUSR1 after 0.5 s, then calls
nanosleep(3s, &rem):
Before:
nanosleep ret=0 errno=0 (handler fired, rem=2.500000000)
After:
nanosleep ret=-1 errno=EINTR (handler fired, rem=2.500000000)
Plain nanosleep(0.2s) still returns 0 and sigtimedwait(0.2s) still
times out with EAGAIN, so the timedwait path is unaffected.
Assisted-by: Claude Code (glm-5.3) <[email protected]>
Signed-off-by: Junbo Zheng <[email protected]>
---
sched/signal/sig_clockwait.c | 46 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/sched/signal/sig_clockwait.c b/sched/signal/sig_clockwait.c
index c5a1b418efa..ee6f6595e85 100644
--- a/sched/signal/sig_clockwait.c
+++ b/sched/signal/sig_clockwait.c
@@ -195,6 +195,10 @@ int nxsig_clockwait(int clockid, int flags,
irqstate_t iflags;
clock_t expect = 0u;
clock_t stop = 0u;
+#ifndef CONFIG_DISABLE_ALL_SIGNALS
+ siginfo_t unbinfo;
+ bool own_unbinfo = false;
+#endif
if (rqtp && (rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1000000000))
{
@@ -238,6 +242,19 @@ int nxsig_clockwait(int clockid, int flags,
iflags = enter_critical_section();
rtcb = this_task();
+#ifndef CONFIG_DISABLE_ALL_SIGNALS
+ /* If the caller did not install its own sigunbinfo (only nxsig_timedwait()
+ * does), install ours so that we can tell how we were awakened: a real
+ * signal writes its siginfo here and requires -EINTR.
+ */
+
+ if (rtcb->sigunbinfo == NULL)
+ {
+ rtcb->sigunbinfo = &unbinfo;
+ own_unbinfo = true;
+ }
+#endif
+
if (rqtp)
{
/* Start the watchdog timer */
@@ -286,6 +303,15 @@ int nxsig_clockwait(int clockid, int flags,
stop = clock_systime_ticks();
}
+ /* Retrieve the wake reason before dropping the critical section */
+
+#ifndef CONFIG_DISABLE_ALL_SIGNALS
+ if (own_unbinfo)
+ {
+ rtcb->sigunbinfo = NULL;
+ }
+#endif
+
leave_critical_section(iflags);
if (rqtp && rmtp && expect)
@@ -294,5 +320,25 @@ int nxsig_clockwait(int clockid, int flags,
clock_compare(stop, expect) ? expect - stop : 0);
}
+#ifndef CONFIG_DISABLE_ALL_SIGNALS
+ if (own_unbinfo)
+ {
+ /* Awakened by a real signal? Then the wait must fail with EINTR
+ * (POSIX: nanosleep/clock_nanosleep). A pending cancellation is
+ * reported through the siginfo errno instead.
+ */
+
+ if (GOOD_SIGNO(unbinfo.si_signo))
+ {
+ return -EINTR;
+ }
+
+ if (unbinfo.si_signo == SIG_CANCEL_TIMEOUT)
+ {
+ return -unbinfo.si_errno;
+ }
+ }
+#endif
+
return 0;
}