From 81c4bcb9b5b6d40a0d7aebf6c82e7497057fdfba Mon Sep 17 00:00:00 2001 From: patacongo Date: Wed, 30 May 2012 15:36:46 +0000 Subject: Fix return values from sleep(), usleep(), and sigtimedwait(). Fix STM32 F2 I2C bug-for-bug compatibility git-svn-id: https://nuttx.svn.sourceforge.net/svnroot/nuttx/trunk@4786 7fd9a85b-ad96-42d3-883c-3090e2eb8679 --- nuttx/sched/sig_timedwait.c | 64 ++++++++++++++++++++------ nuttx/sched/sleep.c | 110 ++++++++++++++++++++++++++++++++++++++++---- nuttx/sched/usleep.c | 86 ++++++++++++++++++++++++++++++---- 3 files changed, 231 insertions(+), 29 deletions(-) (limited to 'nuttx/sched') diff --git a/nuttx/sched/sig_timedwait.c b/nuttx/sched/sig_timedwait.c index 2d9afaab1..c0b4d802a 100644 --- a/nuttx/sched/sig_timedwait.c +++ b/nuttx/sched/sig_timedwait.c @@ -1,8 +1,8 @@ /**************************************************************************** * sched/sig_timedwait.c * - * Copyright (C) 2007-2009 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Copyright (C) 2007-2009, 2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -47,15 +47,24 @@ #include #include #include +#include + #include + #include "os_internal.h" #include "sig_internal.h" #include "clock_internal.h" /**************************************************************************** - * Definitions + * Pre-processor Definitions ****************************************************************************/ +/* This is a special value of si_signo that means that it was the timeout + * that awakened the wait... not the receipt of a signal. + */ + +#define SIG_WAIT_TIMEOUT 0xff + /**************************************************************************** * Private Type Declarations ****************************************************************************/ @@ -107,7 +116,7 @@ static void sig_timeout(int argc, uint32_t itcb) if (u.wtcb->task_state == TSTATE_WAIT_SIG) { - u.wtcb->sigunbinfo.si_signo = ERROR; + u.wtcb->sigunbinfo.si_signo = SIG_WAIT_TIMEOUT; u.wtcb->sigunbinfo.si_code = SI_TIMER; u.wtcb->sigunbinfo.si_value.sival_int = 0; up_unblock_task(u.wtcb); @@ -151,7 +160,11 @@ static void sig_timeout(int argc, uint32_t itcb) * * Return Value: * Signal number that cause the wait to be terminated, otherwise -1 (ERROR) - * is returned. + * 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. * * Assumptions: * @@ -270,23 +283,48 @@ int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info, rtcb->sigwaitmask = NULL_SIGNAL_SET; - /* When we awaken, the cause will be in the TCB. Return the signal - * info to the caller if so requested + /* When we awaken, the cause will be in the TCB. Get the signal number + * or timeout) that awakened us. */ - if (info) + if (GOOD_SIGNO(rtcb->sigunbinfo.si_signo)) { - memcpy(info, &rtcb->sigunbinfo, sizeof(struct siginfo)); + /* We were awakened by a signal... but is it one of the signals that + * we were waiting for? + */ + + if (sigismember(set, rtcb->sigunbinfo.si_signo)) + { + /* Yes.. the return value is the number of the signal that + * awakened us. + */ - /* The return value is the number of the signal that awakened us */ + ret = rtcb->sigunbinfo.si_signo; + } + else + { + /* No... then set EINTR and report an error */ - ret = info->si_signo; + set_errno(EINTR); + ret = ERROR; + } } else { - /* We don't know which signal awakened us. This is probably a bug. */ + /* Otherwise, we must have been awakened by the timeout. Set EGAIN + * and return an error. + */ - ret = 0; + DEBUGASSERT(rtcb->sigunbinfo.si_signo == SIG_WAIT_TIMEOUT); + set_errno(EAGAIN); + ret = ERROR; + } + + /* Return the signal info to the caller if so requested */ + + if (info) + { + memcpy(info, &rtcb->sigunbinfo, sizeof(struct siginfo)); } irqrestore(saved_state); } diff --git a/nuttx/sched/sleep.c b/nuttx/sched/sleep.c index bf586acd9..7df922c61 100644 --- a/nuttx/sched/sleep.c +++ b/nuttx/sched/sleep.c @@ -1,8 +1,8 @@ /**************************************************************************** * sched/sleep.c * - * Copyright (C) 2007, 2009 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Copyright (C) 2007, 2009, 2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -42,6 +42,9 @@ #include #include +#include +#include + /**************************************************************************** * Preprocessor Definitions ****************************************************************************/ @@ -70,15 +73,54 @@ * Function: sleep * * Description: - * As typically declared in unistd.h. sleep() is a simple application of - * sigtimedwait. + * The sleep() function will cause the calling thread to be suspended from + * execution until either the number of real-time seconds specified by the + * argument 'seconds' 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 + * due to the scheduling of other activity by the system. + * + * If a SIGALRM signal is generated for the calling process during + * execution of sleep() and if the SIGALRM signal is being ignored or + * blocked from delivery, it is unspecified whether sleep() returns + * when the SIGALRM signal is scheduled. If the signal is being blocked, it + * is also unspecified whether it remains pending after sleep() returns or + * it is discarded. + * + * If a SIGALRM signal is generated for the calling process during + * execution of sleep(), except as a result of a prior call to alarm(), + * and if the SIGALRM signal is not being ignored or blocked from delivery, + * it is unspecified whether that signal has any effect other than causing + * sleep() to return. + * + * If a signal-catching function interrupts sleep() and examines or changes + * either the time a SIGALRM is scheduled to be generated, the action + * associated with the SIGALRM signal, or whether the SIGALRM signal is + * blocked from delivery, the results are unspecified. + * + * If a signal-catching function interrupts sleep() and calls siglongjmp() + * or longjmp() to restore an environment saved prior to the sleep() call, + * the action associated with the SIGALRM signal and the time at which a + * SIGALRM signal is scheduled to be generated are unspecified. It is also + * unspecified whether the SIGALRM signal is blocked, unless the process' + * signal mask is restored as part of the environment. + * + * Implementations may place limitations on the granularity of timer values. + * For each interval timer, if the requested timer value requires a finer + * granularity than the implementation supports, the actual timer value will + * be rounded up to the next supported value. + * + * Interactions between sleep() and any of setitimer(), ualarm() or sleep() + * are unspecified. * * Parameters: * seconds * * Returned Value: - * Zero if the requested time has elapsed, or the number of seconds left - * to sleep. + * If sleep() returns because the requested time has elapsed, the value + * returned will be 0. If sleep() returns because of premature arousal due + * to delivery of a signal, the return value will be the "unslept" amount + * (the requested time minus the time actually slept) in seconds. * * Assumptions: * @@ -89,14 +131,66 @@ unsigned int sleep(unsigned int seconds) sigset_t set; struct timespec ts; struct siginfo value; + irqstate_t flags; + uint32_t start; + int32_t elapsed; + int32_t remaining = 0; + + /* Don't sleep if seconds == 0 */ if (seconds) { + /* Set up for the sleep. Using the empty set means that we are not + * waiting for any particualar signal. However, any unmasked signal + * can still awaken sigtimedwait(). + */ + (void)sigemptyset(&set); - ts.tv_sec = seconds; + ts.tv_sec = seconds; ts.tv_nsec = 0; + + /* Interrupts are disabled around the following so that it is atomic */ + + flags = irqsave(); + + /* Get the current time then sleep for the requested time. + * 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. + */ + + start = clock_systimer(); (void)sigtimedwait(&set, &value, &ts); + + /* Calculate the elapsed time (in clock ticks) when we wake up from the sleep. + * This is really only necessary if we were awakened from the sleep early + * due to the receipt of a signal. + */ + + elapsed = clock_systimer() - start; + irqrestore(flags); + + /* Get the remaining, un-waited seconds. Note that this calculation + * truncates the elapsed seconds in the division. We may have slept some + * fraction of a second longer than this! But if the calculation is less + * than the 'seconds', we certainly did not sleep for the complete + * requested interval. + */ + + remaining = (int32_t)seconds - elapsed / TICK_PER_SEC; + + /* Make sure that the elapsed time is non-negative (this should always + * be the case unless something exceptional happened while were we + * sleeping -- like the clock was reset or we went into a low power mode, + * OR if we had to wait a long time to run again after calling + * sigtimedwait() making 'elapsed' bigger than it should have been). + */ + + if (remaining < 0) + { + remaining = 0; + } } - return 0; + return (unsigned int)remaining; } diff --git a/nuttx/sched/usleep.c b/nuttx/sched/usleep.c index 2751628eb..ba2c072c4 100644 --- a/nuttx/sched/usleep.c +++ b/nuttx/sched/usleep.c @@ -1,8 +1,8 @@ /**************************************************************************** * sched/usleep.c * - * Copyright (C) 2007, 2009 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Copyright (C) 2007, 2009, 2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -40,6 +40,8 @@ #include #include #include +#include +#include /**************************************************************************** * Definitions @@ -69,30 +71,98 @@ * Function: usleep * * Description: - * BSD version as typically declared in unistd.h. usleep() is a simple - * application of sigtimedwait. + * The usleep() function will cause the calling thread to be suspended + * from execution until either the number of real-time microseconds + * specified by the argument 'usec' has elapsed or a signal is delivered + * to the calling thread. The suspension time may be longer than requested + * due to the scheduling of other activity by the system. * + * The 'usec' argument must be less than 1,000,000. If the value of + * 'usec' is 0, then the call has no effect. + * + * If a SIGALRM signal is generated for the calling process during + * execution of usleep() and if the SIGALRM signal is being ignored or + * blocked from delivery, it is unspecified whether usleep() returns + * when the SIGALRM signal is scheduled. If the signal is being blocked, it + * is also unspecified whether it remains pending after usleep() returns or + * it is discarded. + * + * If a SIGALRM signal is generated for the calling process during + * execution of usleep(), except as a result of a prior call to alarm(), + * and if the SIGALRM signal is not being ignored or blocked from delivery, + * it is unspecified whether that signal has any effect other than causing + * usleep() to return. + * + * If a signal-catching function interrupts usleep() and examines or changes + * either the time a SIGALRM is scheduled to be generated, the action + * associated with the SIGALRM signal, or whether the SIGALRM signal is + * blocked from delivery, the results are unspecified. + * + * If a signal-catching function interrupts usleep() and calls siglongjmp() + * or longjmp() to restore an environment saved prior to the usleep() call, + * the action associated with the SIGALRM signal and the time at which a + * SIGALRM signal is scheduled to be generated are unspecified. It is also + * unspecified whether the SIGALRM signal is blocked, unless the process' + * signal mask is restored as part of the environment. + * + * Implementations may place limitations on the granularity of timer values. + * For each interval timer, if the requested timer value requires a finer + * granularity than the implementation supports, the actual timer value will + * be rounded up to the next supported value. + * + * Interactions between usleep() and any of the following are unspecified: + * + * nanosleep(), setitimer(), timer_create(), timer_delete(), timer_getoverrun(), + * timer_gettime(), timer_settime(), ualarm(), sleep() + * Parameters: - * seconds + * usec - the number of microseconds to wait. * * Returned Value: - * None + * On successful completion, usleep() returns 0. Otherwise, it returns -1 + * and sets errno to indicate the error. * * Assumptions: * ****************************************************************************/ -void usleep(useconds_t usec) +int usleep(useconds_t usec) { sigset_t set; struct timespec ts; struct siginfo value; + int errval; + int ret = 0; if (usec) { + /* Set up for the sleep. Using the empty set means that we are not + * waiting for any particualar signal. However, any unmasked signal + * can still awaken sigtimedwait(). + */ + (void)sigemptyset(&set); ts.tv_sec = usec / 1000000; ts.tv_nsec = (usec % 1000000) * 1000; - (void)sigtimedwait(&set, &value, &ts); + + /* usleep is a simple application of sigtimedwait. */ + + ret = sigtimedwait(&set, &value, &ts); + + /* 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 = errno; + DEBUGASSERT(ret < 0 && (errval == EAGAIN || errval == EINTR)); + if (errval == EAGAIN) + { + /* The timeout "error" is the normal, successful result */ + + ret = 0; + } } + + return ret; } -- cgit v1.2.3