/************************************************************ * 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 "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 ************************************************************/ static void pthread_condtimedout(int pid, int signo, int arg3, int arg4) { union sigval value; /* Send the specified signal to the specified task. */ value.sival_ptr = 0; (void)sigqueue(pid, signo, value); } /************************************************************ * Public Functions ************************************************************/ /************************************************************ * Function: pthread_cond_timedwait * * Description: * A thread can perform a timed wait on a condition variable. * * Parameters: * None * * Return Value: * None * * 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) { struct timespec currtime; struct timespec reltime; WDOG_ID wdog; sint32 relusec; sint32 ticks; int mypid = (int)getpid(); int ret = OK; int int_state; int status; dbg("%s: cond=0x%p mutex=0x%p abstime=0x%p\n", __FUNCTION__, 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("%s: Give up mutex...\n", __FUNCTION__); /* 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. * NOTE: Here we use internal knowledge that CLOCK_REALTIME is * defined to be zero! */ ret = clock_gettime(0, &currtime); if (ret) { /* Restore interrupts (pre-emption will be enabled when * we fall through the if/then/else */ irqrestore(int_state); } else { /* The relative time to wait is the absolute time minus the * 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; /* 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, mypid, ECHO_COND_WAIT_SIGNO, 0, 0); /* 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("%s: Timedout!\n", __FUNCTION__); ret = ETIMEDOUT; } else { ret = EINVAL; } } } /* Reacquire the mutex (retaining the ret). */ dbg("%s: Re-locking...\n", __FUNCTION__); 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("%s: Returning %d\n", __FUNCTION__, ret); return ret; }