/************************************************************ * pthread_condtimedwait.c * * Copyright (C) 2007 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 * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name Gregory Nutt nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ************************************************************/ /************************************************************ * Included Files ************************************************************/ #include #include #include #include #include #include #include #include #include #include "os_internal.h" #include "pthread_internal.h" #include "clock_internal.h" /************************************************************ * Definitions ************************************************************/ #define ECHO_COND_WAIT_SIGNO 3 /************************************************************ * Private Type Declarations ************************************************************/ /************************************************************ * Global Variables ************************************************************/ /************************************************************ * Private Variables ************************************************************/ /************************************************************ * Private Functions ************************************************************/ /************************************************************ * Function: pthread_condtimedout * * Description: * This function is called if the timeout elapses before * the condition is signaled. * * Parameters: * argc - the number of arguments (should be 2) * pid - the task ID of the task to wateup * signo - The signal to use to wake up the task * * Return Value: * None * * Assumptions: * ************************************************************/ static void pthread_condtimedout(int argc, uint32 pid, uint32 signo, ...) { #ifdef CONFIG_CAN_PASS_STRUCTS union sigval value; /* Send the specified signal to the specified task. */ value.sival_ptr = NULL; (void)sigqueue((int)pid, (int)signo, value); #else (void)sigqueue((int)pid, (int)signo, NULL); #endif } /************************************************************ * Function: pthread_timeoutticks * * Description: * Convert a timespec delay to system timer ticks. * * Parameters: * abstime - wait until this absolute time * * Return Value: * The relative number of ticks to wait (or ERROR on * failure; * * Assumptions: * Interrupts should be disabled so that the time is * not changing during the calculation * ************************************************************/ int pthread_timeouticks(const struct timespec *abstime, int *ticks) { struct timespec currtime; struct timespec reltime; sint32 relusec; int ret; /* Convert the timespec to clock ticks. NOTE: Here we use * internal knowledge that CLOCK_REALTIME is defined to be zero! */ ret = clock_gettime(0, &currtime); if (ret) { return EINVAL; } /* The relative time to wait is the absolute time minus the * current time. */ reltime.tv_nsec = (abstime->tv_nsec - currtime.tv_nsec); reltime.tv_sec = (abstime->tv_sec - currtime.tv_sec); /* Check if we were supposed to borrow from the seconds to * borrow from the seconds */ if (reltime.tv_nsec < 0) { reltime.tv_nsec += NSEC_PER_SEC; reltime.tv_sec -= 1; } /* Convert this relative time into microseconds.*/ relusec = reltime.tv_sec * USEC_PER_SEC + reltime.tv_nsec / NSEC_PER_USEC; /* Convert microseconds to clock ticks */ *ticks = relusec / USEC_PER_TICK; return OK; } /************************************************************ * Public Functions ************************************************************/ /************************************************************ * Function: pthread_cond_timedwait * * Description: * A thread can perform a timed wait on a condition variable. * * Parameters: * cond - the condition variable to wait on * mutex - the mutex that protects the condition variable * abstime - wait until this absolute time * * Return Value: * OK (0) on success; ERROR (-1) on failure with errno * set appropriately. * * Assumptions: * Timing is of resolution 1 msec, with +/-1 millisecond * accuracy. * ************************************************************/ int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) { WDOG_ID wdog; sint32 ticks; int mypid = (int)getpid(); irqstate_t int_state; int ret = OK; int status; dbg("cond=0x%p mutex=0x%p abstime=0x%p\n", cond, mutex, abstime); /* Make sure that non-NULL references were provided. */ if (!cond || !mutex) { ret = EINVAL; } /* Make sure that the caller holds the mutex */ else if (mutex->pid != mypid) { ret = EPERM; } /* If no wait time is provided, this function degenerates to * the same behavior as pthread_cond_wait(). */ else if (!abstime) { ret = pthread_cond_wait(cond, mutex); } else { /* Create a watchdog */ wdog = wd_create(); if (!wdog) { ret = EINVAL; } else { dbg("Give up mutex...\n"); /* We must disable pre-emption and interrupts here so that * the time stays valid until the wait begins. This adds * complexity because we assure that interrupts and * pre-emption are re-enabled correctly. */ sched_lock(); int_state = irqsave(); /* Convert the timespec to clock ticks. We must disable pre-emption * here so that this time stays valid until the wait begins. */ ret = pthread_timeouticks(abstime, &ticks); if (ret) { /* Restore interrupts (pre-emption will be enabled when * we fall through the if/then/else */ irqrestore(int_state); } else { /* Check the absolute time to wait. If it is now or in the past, then * just return with the timedout condition. */ if (ticks <= 0) { /* Restore interrupts and indicate that we have already timed out. * (pre-emption will be enabled when we fall through the * if/then/else */ irqrestore(int_state); ret = ETIMEDOUT; } else { /* Give up the mutex */ mutex->pid = 0; ret = pthread_givesemaphore((sem_t*)&mutex->sem); if (ret) { /* Restore interrupts (pre-emption will be enabled when * we fall through the if/then/else) */ irqrestore(int_state); } else { /* Start the watchdog */ wd_start(wdog, ticks, (wdentry_t)pthread_condtimedout, 2, (uint32)mypid, (uint32)ECHO_COND_WAIT_SIGNO); /* Take the condition semaphore. Do not restore interrupts * until we return from the wait. This is necessary to * make sure that the watchdog timer and the condition wait * are started atomically. */ status = sem_wait((sem_t*)&cond->sem); irqrestore(int_state); /* Did we get the condition semaphore. */ if (status != OK) { /* NO.. Handle the special case where the semaphore wait was * awakened by the receipt of a signal -- presumably the * signal posted by pthread_condtimedout(). */ if (*get_errno_ptr() == EINTR) { dbg("Timedout!\n"); ret = ETIMEDOUT; } else { ret = EINVAL; } } } /* Reacquire the mutex (retaining the ret). */ dbg("Re-locking...\n"); status = pthread_takesemaphore((sem_t*)&mutex->sem); if (!status) { mutex->pid = mypid; } else if (!ret) { ret = status; } } /* Re-enable pre-emption (It is expected that interrupts * have already been re-enabled in the above logic) */ sched_unlock(); } /* We no longer need the watchdog */ wd_delete(wdog); } } dbg("Returning %d\n", ret); return ret; }