diff --git a/include/nuttx/signal.h b/include/nuttx/signal.h index ba38d960a45..57f8f2d6651 100644 --- a/include/nuttx/signal.h +++ b/include/nuttx/signal.h @@ -45,12 +45,132 @@ #include #include -#if defined(CONFIG_SIG_EVTHREAD) && defined(CONFIG_BUILD_FLAT) - /**************************************************************************** * Public Function Prototypes ****************************************************************************/ +struct timespec; /* Forward reference */ + +/**************************************************************************** + * Name: nxsig_waitinfo + * + * Description: + * This function is equivalent to nxsig_timedwait with a NULL timeout + * parameter. + * + * This is an internal OS interface. It is functionally equivalent to + * sigwaitinfo() except that: + * + * - It is not a cancellaction point, and + * - It does not modify the errno value. + * + * Parameters: + * set - The pending signal set + * info - The returned value + * + * Return Value: + * This is an internal OS interface and should not be used by applications. + * It follows the NuttX internal error return policy: Zero (OK) is + * returned on success. A negated errno value is returned on failure. + * + ****************************************************************************/ + +#define nxsig_waitinfo(s,i) nxsig_timedwait(s,i,NULL) + +/**************************************************************************** + * Name: nxsig_timedwait + * + * Description: + * This function selects the pending signal set specified by the argument + * set. If multiple signals are pending in set, it will remove and return + * the lowest numbered one. If no signals in set are pending at the time + * of the call, the calling process will be suspended until one of the + * signals in set becomes pending, OR until the process is interrupted by + * an unblocked signal, OR until the time interval specified by timeout + * (if any), has expired. If timeout is NULL, then the timeout interval + * is forever. + * + * If the info argument is non-NULL, the selected signal number is stored + * in the si_signo member and the cause of the signal is store din the + * si_code member. The content of si_value is only meaningful if the + * signal was generated by sigqueue(). + * + * This is an internal OS interface. It is functionally equivalent to + * sigtimedwait() except that: + * + * - It is not a cancellaction point, and + * - It does not modify the errno value. + * + * Parameters: + * set - The pending signal set. + * info - The returned value (may be NULL). + * timeout - The amount of time to wait (may be NULL) + * + * Return Value: + * This is an internal OS interface and should not be used by applications. + * It follows the NuttX internal error return policy: Zero (OK) is + * returned on success. A negated errno value is returned on failure. + * + * EAGAIN - No signal specified by set was generated within the specified + * timeout period. + * EINTR - The wait was interrupted by an unblocked, caught signal. + * + ****************************************************************************/ + +int nxsig_timedwait(FAR const sigset_t *set, FAR struct siginfo *info, + FAR const struct timespec *timeout); + +/**************************************************************************** + * Name: nxsig_nanosleep + * + * Description: + * The nxsig_nanosleep() function causes the current thread to be + * suspended from execution until either the time interval specified by + * the rqtp argument has elapsed or a signal is delivered to the calling + * thread and its action is to invoke a signal-catching function or to + * terminate the process. The suspension time may be longer than requested + * because the argument value is rounded up to an integer multiple of the + * sleep resolution or because of the scheduling of other activity by the + * system. But, except for the case of being interrupted by a signal, the + * suspension time will not be less than the time specified by rqtp, as + * measured by the system clock, CLOCK_REALTIME. + * + * The use of the nxsig_nanosleep() function has no effect on the action + * or blockage of any signal. + * + * Parameters: + * rqtp - The amount of time to be suspended from execution. + * rmtp - If the rmtp argument is non-NULL, the timespec structure + * referenced by it is updated to contain the amount of time + * remaining in the interval (the requested time minus the time + * actually slept) + * + * Returned Value: + * If the nxsig_nanosleep() function returns because the requested time + * has elapsed, its return value is zero. + * + * If the nxsig_nanosleep() function returns because it has been + * interrupted by a signal, the function returns a negated errno value + * indicate the interruption. If the rmtp argument is non-NULL, the + * timespec structure referenced by it is updated to contain the amount + * of time remaining in the interval (the requested time minus the time + * actually slept). If the rmtp argument is NULL, the remaining time is + * not returned. + * + * If nxsig_nanosleep() fails, it returns a negated errno indicating the + * cause of the failure. The nxsig_nanosleep() function will fail if: + * + * EINTR - The nxsig_nanosleep() function was interrupted by a signal. + * EINVAL - The rqtp argument specified a nanosecond value less than + * zero or greater than or equal to 1000 million. + * ENOSYS - The nxsig_nanosleep() function is not supported by this + * implementation. + * + ****************************************************************************/ + +int nxsig_nanosleep(FAR const struct timespec *rqtp, + FAR struct timespec *rmtp); + /**************************************************************************** * Name: nxsig_notification * @@ -71,7 +191,8 @@ * ****************************************************************************/ +#if defined(CONFIG_SIG_EVTHREAD) && defined(CONFIG_BUILD_FLAT) int nxsig_notification(pid_t pid, FAR struct sigevent *event); +#endif -#endif /* CONFIG_SIG_EVTHREAD && CONFIG_BUILD_FLAT */ #endif /* __INCLUDE_NUTTX_SIGNAL_H */ diff --git a/sched/sched/sched_waitid.c b/sched/sched/sched_waitid.c index f59e4e8374a..697178ec1e7 100644 --- a/sched/sched/sched_waitid.c +++ b/sched/sched/sched_waitid.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/sched/sched_waitid.c * - * Copyright (C) 2013, 2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2013, 2015, 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -44,6 +44,7 @@ #include #include +#include #include #include "sched/sched.h" @@ -366,10 +367,11 @@ int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options) /* Wait for any death-of-child signal */ - ret = sigwaitinfo(&set, info); + ret = nxsig_waitinfo(&set, info); if (ret < 0) { - goto errout; + errcode = -ret; + goto errout_with_errno; } /* Make there this was SIGCHLD */ @@ -403,8 +405,8 @@ int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options) else /* if (idtype == P_PGID) */ { - set_errno(ENOSYS); - goto errout; + errcode = ENOSYS; + goto errout_with_errno; } } } @@ -415,7 +417,7 @@ int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options) errout_with_errno: set_errno(errcode); -errout: + leave_cancellation_point(); sched_unlock(); return ERROR; diff --git a/sched/sched/sched_waitpid.c b/sched/sched/sched_waitpid.c index d3166b074e9..b2b341eea98 100644 --- a/sched/sched/sched_waitpid.c +++ b/sched/sched/sched_waitpid.c @@ -45,6 +45,7 @@ #include #include +#include #include #include @@ -528,10 +529,11 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) /* Wait for any death-of-child signal */ - ret = sigwaitinfo(&set, &info); + ret = nxsig_waitinfo(&set, &info); if (ret < 0) { - goto errout_with_lock; + errcode = -ret; + goto errout_with_errno; } /* Was this the death of the thread we were waiting for? In the of @@ -569,7 +571,6 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) errout_with_errno: set_errno(errcode); -errout_with_lock: leave_cancellation_point(); sched_unlock(); return ERROR; diff --git a/sched/signal/sig_nanosleep.c b/sched/signal/sig_nanosleep.c index 1e8163e8a3a..6fdb51ae453 100644 --- a/sched/signal/sig_nanosleep.c +++ b/sched/signal/sig_nanosleep.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/signal/sig/nanosleep.c * - * Copyright (C) 2013, 2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2013, 2016-2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -46,6 +46,7 @@ #include #include +#include #include #include "clock/clock.h" @@ -54,6 +55,149 @@ * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: nxsig_nanosleep + * + * Description: + * The nxsig_nanosleep() function causes the current thread to be + * suspended from execution until either the time interval specified by + * the rqtp argument has elapsed or a signal is delivered to the calling + * thread and its action is to invoke a signal-catching function or to + * terminate the process. The suspension time may be longer than requested + * because the argument value is rounded up to an integer multiple of the + * sleep resolution or because of the scheduling of other activity by the + * system. But, except for the case of being interrupted by a signal, the + * suspension time will not be less than the time specified by rqtp, as + * measured by the system clock, CLOCK_REALTIME. + * + * The use of the nxsig_nanosleep() function has no effect on the action + * or blockage of any signal. + * + * Parameters: + * rqtp - The amount of time to be suspended from execution. + * rmtp - If the rmtp argument is non-NULL, the timespec structure + * referenced by it is updated to contain the amount of time + * remaining in the interval (the requested time minus the time + * actually slept) + * + * Returned Value: + * If the nxsig_nanosleep() function returns because the requested time + * has elapsed, its return value is zero. + * + * If the nxsig_nanosleep() function returns because it has been + * interrupted by a signal, the function returns a negated errno value + * indicate the interruption. If the rmtp argument is non-NULL, the + * timespec structure referenced by it is updated to contain the amount + * of time remaining in the interval (the requested time minus the time + * actually slept). If the rmtp argument is NULL, the remaining time is + * not returned. + * + * If nxsig_nanosleep() fails, it returns a negated errno indicating the + * cause of the failure. The nxsig_nanosleep() function will fail if: + * + * EINTR - The nxsig_nanosleep() function was interrupted by a signal. + * EINVAL - The rqtp argument specified a nanosecond value less than + * zero or greater than or equal to 1000 million. + * ENOSYS - The nxsig_nanosleep() function is not supported by this + * implementation. + * + ****************************************************************************/ + +int nxsig_nanosleep(FAR const struct timespec *rqtp, + FAR struct timespec *rmtp) +{ + irqstate_t flags; + systime_t starttick; + sigset_t set; + int ret; + + /* Sanity check */ + + if (!rqtp || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1000000000) + { + return -EINVAL; + } + + /* Get the start time of the wait. Interrupts are disabled to prevent + * timer interrupts while we do tick-related calculations before and + * after the wait. + */ + + flags = enter_critical_section(); + starttick = clock_systimer(); + + /* Set up for the sleep. Using the empty set means that we are not + * waiting for any particular signal. However, any unmasked signal can + * still awaken nxsig_timedwait(). + */ + + (void)sigemptyset(&set); + + /* nxsig_nanosleep is a simple application of nxsig_timedwait. */ + + ret = nxsig_timedwait(&set, NULL, rqtp); + + /* nxsig_timedwait() cannot succeed. It should always return error with + * either (1) EAGAIN meaning that the timeout occurred, or (2) EINTR + * meaning that some other unblocked signal was caught. + */ + + DEBUGASSERT(ret < 0 && (ret == -EAGAIN || ret == -EINTR)); + + if (ret == -EAGAIN) + { + /* The timeout "error" is the normal, successful result */ + + leave_critical_section(flags); + return OK; + } + + /* If we get there, the wait has failed because we were awakened by a + * signal. Return the amount of "unwaited" time if rmtp is non-NULL. + */ + + if (rmtp) + { + systime_t elapsed; + systime_t remaining; + ssystime_t ticks; + + /* REVISIT: The conversion from time to ticks and back could + * be avoided. clock_timespec_subtract() would be used instead + * to get the time difference. + */ + + /* First get the number of clock ticks that we were requested to + * wait. + */ + + (void)clock_time2ticks(rqtp, &ticks); + + /* Get the number of ticks that we actually waited */ + + elapsed = clock_systimer() - starttick; + + /* The difference between the number of ticks that we were requested + * to wait and the number of ticks that we actualy waited is that + * amount of time that we failed to wait. + */ + + if (elapsed >= (systime_t)ticks) + { + remaining = 0; + } + else + { + remaining = (systime_t)ticks - elapsed; + } + + (void)clock_ticks2time((ssystime_t)remaining, rmtp); + } + + leave_critical_section(flags); + return ret; +} + /**************************************************************************** * Name: nanosleep * @@ -104,110 +248,21 @@ int nanosleep(FAR const struct timespec *rqtp, FAR struct timespec *rmtp) { - irqstate_t flags; - systime_t starttick; - sigset_t set; - int errval; -#ifdef CONFIG_DEBUG_ASSERTIONS /* Warning avoidance */ int ret; -#endif /* nanosleep() is a cancellation point */ (void)enter_cancellation_point(); - if (!rqtp || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1000000000) + /* Just a wrapper around nxsig_nanosleep() */ + + ret = nxsig_nanosleep(rqtp, rmtp); + if (ret < 0) { - errval = EINVAL; - goto errout; + set_errno(-ret); + ret = ERROR; } - /* Get the start time of the wait. Interrupts are disabled to prevent - * timer interrupts while we do tick-related calculations before and - * after the wait. - */ - - flags = enter_critical_section(); - starttick = clock_systimer(); - - /* Set up for the sleep. Using the empty set means that we are not - * waiting for any particular signal. However, any unmasked signal can - * still awaken sigtimedwait(). - */ - - (void)sigemptyset(&set); - - /* nanosleep is a simple application of sigtimedwait. */ - -#ifdef CONFIG_DEBUG_ASSERTIONS /* Warning avoidance */ - ret = sigtimedwait(&set, NULL, rqtp); -#else - (void)sigtimedwait(&set, NULL, rqtp); -#endif - - /* sigtimedwait() cannot succeed. It should always return error with - * either (1) EAGAIN meaning that the timeout occurred, or (2) EINTR - * meaning that some other unblocked signal was caught. - */ - - errval = get_errno(); - DEBUGASSERT(ret < 0 && (errval == EAGAIN || errval == EINTR)); - - if (errval == EAGAIN) - { - /* The timeout "error" is the normal, successful result */ - - leave_critical_section(flags); - leave_cancellation_point(); - return OK; - } - - /* If we get there, the wait has failed because we were awakened by a - * signal. Return the amount of "unwaited" time if rmtp is non-NULL. - */ - - if (rmtp) - { - systime_t elapsed; - systime_t remaining; - ssystime_t ticks; - - /* REVISIT: The conversion from time to ticks and back could - * be avoided. clock_timespec_subtract() would be used instead - * to get the time difference. - */ - - /* First get the number of clock ticks that we were requested to - * wait. - */ - - (void)clock_time2ticks(rqtp, &ticks); - - /* Get the number of ticks that we actually waited */ - - elapsed = clock_systimer() - starttick; - - /* The difference between the number of ticks that we were requested - * to wait and the number of ticks that we actualy waited is that - * amount of time that we failed to wait. - */ - - if (elapsed >= (systime_t)ticks) - { - remaining = 0; - } - else - { - remaining = (systime_t)ticks - elapsed; - } - - (void)clock_ticks2time((ssystime_t)remaining, rmtp); - } - - leave_critical_section(flags); - -errout: - set_errno(errval); leave_cancellation_point(); - return ERROR; + return ret; } diff --git a/sched/signal/sig_pause.c b/sched/signal/sig_pause.c index 62b90c2935c..305dc40be9d 100644 --- a/sched/signal/sig_pause.c +++ b/sched/signal/sig_pause.c @@ -41,7 +41,9 @@ #include #include +#include +#include #include /**************************************************************************** @@ -92,7 +94,13 @@ int pause(void) * meaning that some unblocked signal was caught. */ - ret = sigwaitinfo(&set, NULL); + ret = nxsig_waitinfo(&set, NULL); + if (ret < 0) + { + set_errno(-ret); + ret = ERROR; + } + leave_cancellation_point(); return ret; } diff --git a/sched/signal/sig_timedwait.c b/sched/signal/sig_timedwait.c index faf56b35a16..7ffd8b6f326 100644 --- a/sched/signal/sig_timedwait.c +++ b/sched/signal/sig_timedwait.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include "sched/sched.h" @@ -145,7 +146,7 @@ static void nxsig_timeout(int argc, wdparm_t itcb) ****************************************************************************/ /**************************************************************************** - * Name: sigtimedwait + * Name: nxsig_timedwait * * Description: * This function selects the pending signal set specified by the argument @@ -162,13 +163,11 @@ static void nxsig_timeout(int argc, wdparm_t itcb) * si_code member. The content of si_value is only meaningful if the * signal was generated by sigqueue(). * - * The following values for si_code are defined in signal.h: - * SI_USER - Signal sent from kill, raise, or abort - * SI_QUEUE - Signal sent from sigqueue - * SI_TIMER - Signal is result of timer expiration - * SI_ASYNCIO - Signal is the result of asynch IO completion - * SI_MESGQ - Signal generated by arrival of a message on an - * empty message queue. + * This is an internal OS interface. It is functionally equivalent to + * sigtimedwait() except that: + * + * - It is not a cancellaction point, and + * - It does not modify the errno value. * * Parameters: * set - The pending signal set. @@ -176,33 +175,28 @@ static void nxsig_timeout(int argc, wdparm_t itcb) * timeout - The amount of time to wait (may be NULL) * * Return Value: - * Signal number that cause the wait to be terminated, otherwise -1 (ERROR) - * is returned with errno set to either: + * This is an internal OS interface and should not be used by applications. + * It follows the NuttX internal error return policy: Zero (OK) is + * returned on success. A negated errno value is returned on failure. * * EAGAIN - No signal specified by set was generated within the specified * timeout period. * EINTR - The wait was interrupted by an unblocked, caught signal. * - * Assumptions: - * ****************************************************************************/ -int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info, - FAR const struct timespec *timeout) +int nxsig_timedwait(FAR const sigset_t *set, FAR struct siginfo *info, + FAR const struct timespec *timeout) { FAR struct tcb_s *rtcb = this_task(); sigset_t intersection; FAR sigpendq_t *sigpend; irqstate_t flags; int32_t waitticks; - int ret = ERROR; + int ret; DEBUGASSERT(rtcb->waitdog == NULL); - - /* sigtimedwait() is a cancellation point */ - - (void)enter_cancellation_point(); - sched_lock(); /* Not necessary */ + sched_lock(); /* Should not be necessary */ /* Several operations must be performed below: We must determine if any * signal is pending and, if not, wait for the signal. Since signals can @@ -225,7 +219,7 @@ int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info, */ sigpend = nxsig_remove_pendingsignal(rtcb, nxsig_lowest(&intersection)); - ASSERT(sigpend); + DEBUGASSERT(sigpend); /* Return the signal info to the caller if so requested */ @@ -344,10 +338,9 @@ int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info, } else { - /* No... then set EINTR and report an error */ + /* No... then report the EINTR error */ - set_errno(EINTR); - ret = ERROR; + ret = -EINTR; } } else @@ -357,8 +350,7 @@ int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info, */ DEBUGASSERT(rtcb->sigunbinfo.si_signo == SIG_WAIT_TIMEOUT); - set_errno(EAGAIN); - ret = ERROR; + ret = -EAGAIN; } /* Return the signal info to the caller if so requested */ @@ -372,6 +364,68 @@ int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info, } sched_unlock(); + return ret; +} + +/**************************************************************************** + * Name: sigtimedwait + * + * Description: + * This function selects the pending signal set specified by the argument + * set. If multiple signals are pending in set, it will remove and return + * the lowest numbered one. If no signals in set are pending at the time + * of the call, the calling process will be suspended until one of the + * signals in set becomes pending, OR until the process is interrupted by + * an unblocked signal, OR until the time interval specified by timeout + * (if any), has expired. If timeout is NULL, then the timeout interval + * is forever. + * + * If the info argument is non-NULL, the selected signal number is stored + * in the si_signo member and the cause of the signal is store din the + * si_code member. The content of si_value is only meaningful if the + * signal was generated by sigqueue(). + * + * The following values for si_code are defined in signal.h: + * SI_USER - Signal sent from kill, raise, or abort + * SI_QUEUE - Signal sent from sigqueue + * SI_TIMER - Signal is result of timer expiration + * SI_ASYNCIO - Signal is the result of asynch IO completion + * SI_MESGQ - Signal generated by arrival of a message on an + * empty message queue. + * + * Parameters: + * set - The pending signal set. + * info - The returned value (may be NULL). + * timeout - The amount of time to wait (may be NULL) + * + * Return Value: + * Signal number that cause the wait to be terminated, otherwise -1 (ERROR) + * is returned with errno set to either: + * + * EAGAIN - No signal specified by set was generated within the specified + * timeout period. + * EINTR - The wait was interrupted by an unblocked, caught signal. + * + ****************************************************************************/ + +int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info, + FAR const struct timespec *timeout) +{ + int ret; + + /* sigtimedwait() is a cancellation point */ + + (void)enter_cancellation_point(); + + /* Let nxsig_timedwait() do the work. */ + + ret = nxsig_timedwait(set, info, timeout); + if (ret < 0) + { + set_errno(-ret); + ret = ERROR; + } + leave_cancellation_point(); return ret; } diff --git a/sched/signal/sig_waitinfo.c b/sched/signal/sig_waitinfo.c index 73afb186de3..92d11acf286 100644 --- a/sched/signal/sig_waitinfo.c +++ b/sched/signal/sig_waitinfo.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/signal/sig_waitinfo.c * - * Copyright (C) 2007-2009 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -39,9 +39,10 @@ #include -#include +#include #include +#include /**************************************************************************** * Public Functions @@ -60,9 +61,7 @@ * * Return Value: * Signal number that cause the wait to be terminated, otherwise -1 (ERROR) - * is returned. - * - * Assumptions: + * is returned and the errno variable is set appropriately. * ****************************************************************************/ @@ -74,9 +73,15 @@ int sigwaitinfo(FAR const sigset_t *set, FAR struct siginfo *info) (void)enter_cancellation_point(); - /* Just a wrapper around sigtimedwait() */ + /* Just a wrapper around nxsig_timedwait() */ + + ret = nxsig_timedwait(set, info, NULL); + if (ret < 0) + { + set_errno(-ret); + ret = ERROR; + } - ret = sigtimedwait(set, info, NULL); leave_cancellation_point(); return ret; } diff --git a/sched/wqueue/kwork_process.c b/sched/wqueue/kwork_process.c index 458c3994bc6..58ecd47eef2 100644 --- a/sched/wqueue/kwork_process.c +++ b/sched/wqueue/kwork_process.c @@ -232,7 +232,7 @@ void work_process(FAR struct kwork_wqueue_s *wqueue, systime_t period, int wndx) sigaddset(&set, SIGWORK); wqueue->worker[wndx].busy = false; - DEBUGVERIFY(sigwaitinfo(&set, NULL)); + DEBUGVERIFY(nxsig_waitinfo(&set, NULL)); wqueue->worker[wndx].busy = true; } else