|
- /*
- * s e m _ t i m e d w a i t
- *
- * Function:
- * Implements a version of sem_timedwait().
- *
- * Description:
- * Not all systems implement sem_timedwait(), which is a version of
- * sem_wait() with a timeout. Mac OS X is one example, at least up to
- * and including version 10.6 (Leopard). If such a function is needed,
- * this code provides a reasonable implementation, which I think is
- * compatible with the standard version, although possibly less
- * efficient. It works by creating a thread that interrupts a normal
- * sem_wait() call after the specified timeout.
- *
- * Call:
- *
- * The Linux man pages say:
- *
- * #include <semaphore.h>
- *
- * int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
- *
- * sem_timedwait() is the same as sem_wait(), except that abs_timeout
- * specifies a limit on the amount of time that the call should block if
- * the decrement cannot be immediately performed. The abs_timeout argument
- * points to a structure that specifies an absolute timeout in seconds and
- * nanoseconds since the Epoch (00:00:00, 1 January 1970). This structure
- * is defined as follows:
- *
- * struct timespec {
- * time_t tv_sec; Seconds
- * long tv_nsec; Nanoseconds [0 .. 999999999]
- * };
- *
- * If the timeout has already expired by the time of the call, and the
- * semaphore could not be locked immediately, then sem_timedwait() fails
- * with a timeout error (errno set to ETIMEDOUT).
- * If the operation can be performed immediately, then sem_timedwait()
- * never fails with a timeout error, regardless of the value of abs_timeout.
- * Furthermore, the validity of abs_timeout is not checked in this case.
- *
- * Limitations:
- *
- * The mechanism used involves sending a SIGUSR2 signal to the thread
- * calling sem_timedwait(). The handler for this signal is set to a null
- * routine which does nothing, and with any flags for the signal
- * (eg SA_RESTART) cleared. Note that this effective disabling of the
- * SIGUSR2 signal is a side-effect of using this routine, and means it
- * may not be a completely transparent plug-in replacement for a
- * 'normal' sig_timedwait() call. Since OS X does not declare the
- * sem_timedwait() call in its standard include files, the relevant
- * declaration (shown above in the man pages extract) will probably have
- * to be added to any code that uses this.
- *
- * Compiling:
- * This compiles and runs cleanly on OS X (10.6) with gcc with the
- * -Wall -ansi -pedantic flags. On Linux, using -ansi causes a sweep of
- * compiler complaints about the timespec structure, but it compiles
- * and works fine with just -Wall -pedantic. (Since Linux provides
- * sem_timedwait() anyway, this really isn't needed on Linux.) However,
- * since Linux provides sem_timedwait anyway, the sem_timedwait()
- * code in this file is only compiled on OS X, and is a null on other
- * systems.
- *
- * Testing:
- * This file contains a test program that exercises the sem_timedwait
- * code. It is compiled if the pre-processor variable TEST is defined.
- * For more details, see the comments for the test routine at the end
- * of the file.
- *
- * Author: Keith Shortridge, AAO.
- *
- * History:
- * 8th Sep 2009. Original version. KS.
- * 24th Sep 2009. Added test that the calling thread still exists before
- * trying to set the timed-out flag. KS.
- * 2nd Oct 2009. No longer restores the original SIGUSR2 signal handler.
- * See comments in the body of the code for more details.
- * Prototypes for now discontinued internal routines removed.
- * 12th Aug 2010. Added the cleanup handler, so that this code no longer
- * leaks resources if the calling thread is cancelled. KS.
- * 21st Sep 2011. Added copyright notice below. Modified header comments
- * to describe the use of SIGUSR2 more accurately in the
- * light of the 2/10/09 change above. Now undefs DEBUG
- * before defining it, to avoid any possible clash. KS.
- * 14th Feb 2012. Tidied out a number of TABs that had got into the
- * code. KS.
- * 6th May 2013. Copyright notice modified to one based on the MIT licence,
- * which is more permissive than the previous notice. KS.
- *
- * Copyright (c) Australian Astronomical Observatory (AAO), (2013).
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
- #ifdef __APPLE__
-
- #include <semaphore.h>
- #include <time.h>
- #include <sys/time.h>
- #include <pthread.h>
- #include <errno.h>
- #include <signal.h>
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/fcntl.h>
- #include <setjmp.h>
-
- /* Some useful definitions - TRUE and FALSE */
-
- #undef TRUE
- #define TRUE 1
- #undef FALSE
- #define FALSE 0
-
- /* A structure of type timeoutDetails is passed to the thread used to
- * implement the timeout.
- */
-
- typedef struct {
- struct timespec delay; /* Specifies the delay, relative to now */
- pthread_t callingThread; /* The thread doing the sem_wait call */
- volatile short *timedOutShort; /* Address of a flag set to indicate that
- * the timeout was triggered. */
- } timeoutDetails;
-
- /* A structure of type cleanupDetails is passed to the thread cleanup
- * routine which is called at the end of the routine or if the thread calling
- * it is cancelled.
- */
-
- typedef struct {
- pthread_t *threadIdAddr; /* Address of the variable that holds
- * the Id of the timeout thread. */
- struct sigaction *sigHandlerAddr; /* Address of the old signal action
- * handler. */
- volatile short *timedOutShort; /* Address of a flag set to indicate that
- * the timeout was triggered. */
- } cleanupDetails;
-
- /* Forward declarations of internal routines */
-
- static void* timeoutThreadMain (void* passedPtr);
- static int triggerSignal (int Signal, pthread_t Thread);
- static void ignoreSignal (int Signal);
- static void timeoutThreadCleanup (void* passedPtr);
-
- /* -------------------------------------------------------------------------- */
- /*
- * s e m _ t i m e d w a i t
- *
- * This is the main code for the sem_timedwait() implementation.
- */
-
- static int sem_timedwait (
- sem_t *sem,
- const struct timespec *abs_timeout)
- {
- int result = 0; /* Code returned by this routine 0 or -1 */
-
- /* "Under no circumstances shall the function fail if the semaphore
- * can be locked immediately". So we try to get it quickly to see if we
- * can avoid all the timeout overheads.
- */
-
- if (sem_trywait(sem) == 0) {
-
- /* Yes, got it immediately. */
-
- result = 0;
-
- } else {
-
- /* No, we've got to do it with a sem_wait() call and a thread to run
- * the timeout. First, work out the time from now to the specified
- * timeout, which we will pass to the timeout thread in a way that can
- * be used to pass to nanosleep(). So we need this in seconds and
- * nanoseconds. Along the way, we check for an invalid passed time,
- * and for one that's already expired.
- */
-
- if ((abs_timeout->tv_nsec < 0) || (abs_timeout->tv_nsec > 1000000000)) {
-
- /* Passed time is invalid */
-
- result = -1;
- errno = EINVAL;
-
- } else {
-
- struct timeval currentTime; /* Time now */
- long secsToWait,nsecsToWait; /* Seconds and nsec to delay */
- gettimeofday (¤tTime,NULL);
- secsToWait = abs_timeout->tv_sec - currentTime.tv_sec;
- nsecsToWait = (abs_timeout->tv_nsec - (currentTime.tv_usec * 1000));
- while (nsecsToWait < 0) {
- nsecsToWait += 1000000000;
- secsToWait--;
- }
- if ((secsToWait < 0) || ((secsToWait == 0) && (nsecsToWait < 0))) {
-
- /* Time has passed. Report an immediate timeout. */
-
- result = -1;
- errno = ETIMEDOUT;
-
- } else {
-
- /* We're going to have to do a sem_wait() with a timeout thread.
- * The thread will wait the specified time, then will issue a
- * SIGUSR2 signal that will interrupt the sem_wait() call.
- * We pass the thread the id of the current thread, the delay,
- * and the address of a flag to set on a timeout, so we can
- * distinguish an interrupt caused by the timeout thread from
- * one caused by some other signal.
- */
-
- volatile short timedOut; /* Flag to set on timeout */
- timeoutDetails details; /* All the stuff the thread must know */
- struct sigaction oldSignalAction; /* Current signal setting */
- pthread_t timeoutThread; /* Id of timeout thread */
- cleanupDetails cleaningDetails; /* What the cleanup routine needs */
- int oldCancelState; /* Previous cancellation state */
- int ignoreCancelState; /* Used in call, but ignored */
- int createStatus; /* Status of pthread_create() call */
-
- /* If the current thread is cancelled (and CML does do this)
- * we don't want to leave our timer thread running - if we've
- * started the thread we want to make sure we join it in order
- * to release its resources. So we set a cleanup handler to
- * do this. We pass it the address of the structure that will
- * hold all it needs to know. While we set all this up,
- * we prevent ourselves being cancelled, so all this data is
- * coherent.
- */
-
- pthread_setcancelstate (PTHREAD_CANCEL_DISABLE,&oldCancelState);
- timeoutThread = (pthread_t) 0;
- cleaningDetails.timedOutShort = &timedOut;
- cleaningDetails.threadIdAddr = &timeoutThread;
- cleaningDetails.sigHandlerAddr = &oldSignalAction;
- pthread_cleanup_push (timeoutThreadCleanup,&cleaningDetails);
-
- /* Set up the details for the thread. Clear the timeout flag,
- * record the current SIGUSR2 action settings so we can restore
- * them later.
- */
-
- details.delay.tv_sec = secsToWait;
- details.delay.tv_nsec = nsecsToWait;
- details.callingThread = pthread_self();
- details.timedOutShort = &timedOut;
- timedOut = FALSE;
- sigaction (SIGUSR2,NULL,&oldSignalAction);
-
- /* Start up the timeout thread. Once we've done that, we can
- * restore the previous cancellation state.
- */
-
- createStatus = pthread_create(&timeoutThread,NULL,
- timeoutThreadMain, (void*)&details);
- pthread_setcancelstate (oldCancelState,&ignoreCancelState);
-
- if (createStatus < 0) {
-
- /* Failed to create thread. errno will already be set properly */
-
- result = -1;
-
- } else {
-
- /* Thread created OK. This is where we wait for the semaphore.
- */
-
- if (sem_wait(sem) == 0) {
-
- /* Got the semaphore OK. We return zero, and all's well. */
-
- result = 0;
-
- } else {
-
- /* If we got a -1 error from sem_wait(), it may be because
- * it was interrupted by a timeout, or failed for some
- * other reason. We check for the expected timeout
- * condition, which is an 'interrupted' status and the
- * timeout flag set by the timeout thread. We report that as
- * a timeout error. Anything else is some other error and
- * errno is already set properly.
- */
-
- result = -1;
- if (errno == EINTR) {
- if (timedOut) errno = ETIMEDOUT;
- }
- }
-
- }
-
- /* The cleanup routine - timeoutThreadCleanup() - packages up
- * any tidying up that is needed, including joining with the
- * timer thread. This will be called if the current thread is
- * cancelled, but we need it to happen anyway, so we set the
- * execute flag true here as we remove it from the list of
- * cleanup routines to be called. So normally, this line amounts
- * to calling timeoutThreadCleanup().
- */
-
- pthread_cleanup_pop (TRUE);
- }
- }
- }
- return (result);
- }
-
- /* -------------------------------------------------------------------------- */
- /*
- * t i m e o u t T h r e a d C l e a n u p
- *
- * This internal routine tidies up at the end of a sem_timedwait() call.
- * It is set as a cleanup routine for the current thread (not the timer
- * thread) so it is executed even if the thread is cancelled. This is
- * important, as we need to tidy up the timeout thread. If we took the
- * semaphore (in other words, if we didn't timeout) then the timer thread
- * will still be running, sitting in its nanosleep() call, and we need
- * to cancel it. If the timer thread did signal a timeout then it will
- * now be closing down. In either case, we need to join it (using a call
- * to pthread_join()) or its resources will never be released.
- * The single argument is a pointer to a cleanupDetails structure that has
- * all the routine needs to know.
- */
-
- static void timeoutThreadCleanup (void* passedPtr)
- {
- /* Get what we need from the structure we've been passed. */
-
- cleanupDetails *detailsPtr = (cleanupDetails*) passedPtr;
- short timedOut = *(detailsPtr->timedOutShort);
- pthread_t timeoutThread = *(detailsPtr->threadIdAddr);
-
- /* If we created the thread, stop it - doesn't matter if it's no longer
- * running, pthread_cancel can handle that. We make sure we wait for it
- * to complete, because it is this pthread_join() call that releases any
- * memory the thread may have allocated. Note that cancelling a thread is
- * generally not a good idea, because of the difficulty of cleaning up
- * after it, but this is a very simple thread that does nothing but call
- * nanosleep(), and that we can cancel quite happily.
- */
-
- if (!timedOut) pthread_cancel(timeoutThread);
- pthread_join(timeoutThread,NULL);
-
- /* The code originally restored the old action handler, which generally
- * was the default handler that caused the task to exit. Just occasionally,
- * there seem to be cases where the signal is still queued and ready to
- * trigger even though the thread that presumably sent it off just before
- * it was cancelled has finished. I had thought that once we'd joined
- * that thread, we could be sure of not seeing the signal, but that seems
- * not to be the case, and so restoring a handler that will allow the task
- * to crash is not a good idea, and so the line below has been commented
- * out.
- *
- * sigaction (SIGUSR2,detailsPtr->sigHandlerAddr,NULL);
- */
- }
-
- /* -------------------------------------------------------------------------- */
- /*
- * t i m e o u t T h r e a d M a i n
- *
- * This internal routine is the main code for the timeout thread.
- * The single argument is a pointer to a timeoutDetails structure that has
- * all the thread needs to know - thread to signal, delay time, and the
- * address of a flag to set if it triggers a timeout.
- */
-
- static void* timeoutThreadMain (void* passedPtr)
- {
- void* Return = (void*) 0;
-
- /* We grab all the data held in the calling thread right now. In some
- * cases, we find that the calling thread has vanished and released
- * its memory, including the details structure, by the time the timeout
- * expires, and then we get an access violation when we try to set the
- * 'timed out' flag.
- */
-
- timeoutDetails details = *((timeoutDetails*) passedPtr);
- struct timespec requestedDelay = details.delay;
-
- /* We do a nanosleep() for the specified delay, and then trigger a
- * timeout. Note that we allow for the case where the nanosleep() is
- * interrupted, and restart it for the remaining time. If the
- * thread that is doing the sem_wait() call gets the semaphore, it
- * will cancel this thread, which is fine as we aren't doing anything
- * other than a sleep and a signal.
- */
-
- for (;;) {
- struct timespec remainingDelay;
- if (nanosleep (&requestedDelay,&remainingDelay) == 0) {
- break;
- } else if (errno == EINTR) {
- requestedDelay = remainingDelay;
- } else {
- Return = (void*) errno;
- break;
- }
- }
-
- /* We've completed the delay without being cancelled, so we now trigger
- * the timeout by sending a signal to the calling thread. And that's it,
- * although we set the timeout flag first to indicate that it was us
- * that interrupted the sem_wait() call. One precaution: before we
- * try to set the timed-out flag, make sure the calling thread still
- * exists - this may not be the case if things are closing down a bit
- * messily. We check this quickly using a zero test signal.
- */
-
- if (pthread_kill(details.callingThread,0) == 0) {
- *(details.timedOutShort) = TRUE;
- if (triggerSignal (SIGUSR2,details.callingThread) < 0) {
- Return = (void*) errno;
- }
- }
-
- return Return;
- }
-
- /* -------------------------------------------------------------------------- */
- /*
- * t r i g g e r S i g n a l
- *
- * This is a general purpose routine that sends a specified signal to
- * a specified thread, setting up a signal handler that does nothing,
- * and then giving the signal. The only effect will be to interrupt any
- * operation that is currently blocking - in this case, we expect this to
- * be a sem_wait() call.
- */
-
- static int triggerSignal (int Signal, pthread_t Thread)
- {
- int Result = 0;
- struct sigaction SignalDetails;
- SignalDetails.sa_handler = ignoreSignal;
- SignalDetails.sa_flags = 0;
- (void) sigemptyset(&SignalDetails.sa_mask);
- if ((Result = sigaction(Signal,&SignalDetails,NULL)) == 0) {
- Result = pthread_kill(Thread,Signal);
- }
- return Result;
- }
-
- /* -------------------------------------------------------------------------- */
- /*
- * i g n o r e S i g n a l
- *
- * And this is the signal handler that does nothing. (It clears its argument,
- * but this has no effect and prevents a compiler warning about an unused
- * argument.)
- */
-
- static void ignoreSignal (int Signal) {
- Signal = 0;
- }
-
- #endif
-
- /* -------------------------------------------------------------------------- */
- /*
- * T e s t c o d e
- *
- * The rest of the code here is used to test sem_timedwait(), and is
- * compiled only if the pre-processor variable TEST is set. The test
- * program sets up a random timeout and a random delay after which a
- * test semaphore will become available. It starts a thread to release the
- * semaphore after the specified delay, and issues a sem_timedwait() call
- * to take the semaphore, with the specified timeout. It repeats this
- * several times, and finally reports the number of times the semaphore
- * was taken, the number of times it timed out, and the number of these
- * occurrences that were unexpected - ie a semaphore being taken although
- * the timeout was less than the delay before it was set, or vice versa.
- * The main() routine of the test returns the number of unexpected
- * occurrences, which will be zero if the code is working properly.
- *
- * To run:
- *
- * gcc -o timed -Wall -ansi -pedantic -DTEST sem_timedwait.c -lpthread
- * ./timed [count] [timescale]
- *
- * On some Linux systems, you may need to drop the -ansi - see comments
- * at start of file. On OS X systems, most tests up to a time frame of
- * 0.001 secs show nothing happening in an unexpected sequence. On a
- * Linux 2.4 system, with its lower time resolution, tests will show
- * occasional cases where things don't happen in the expected order, but
- * these are not counted as unexpected if the two random times are less
- * than 10 msec apart.
- */
-
- #ifdef TEST
-
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
-
- /* A giverDetails structure is used to pass the necessary information
- * to the thread that is started to release the semaphore.
- */
-
- typedef struct {
- sem_t *semAddr; /* Address of semaphore to release */
- float delaySecs; /* Time to wait before release */
- } giverDetails;
-
- /* -------------------------------------------------------------------------- */
- /*
- * g i v e r T h r e a d M a i n
- *
- * This is the main code for the thread that releases the semaphore after
- * a specified delay. The single argument is the address of a giverDetails
- * structure, which specifies the delay time and the semaphore to release.
- * If the sem_timedwait() call in the main thread times-out, this thread
- * is cancelled and so will not release the semaphore.
- */
-
- static void* giverThreadMain (void* passedPtr)
- {
- /* All we do is sleep the specified time and then release the semaphore */
-
- giverDetails *details = (giverDetails*) passedPtr;
- long uSecs = (long)(details->delaySecs * 1000000.0);
- usleep (uSecs);
- if (sem_post(details->semAddr) < 0) {
- perror ("sem_post");
- }
- return NULL;
- }
-
- /* -------------------------------------------------------------------------- */
- /*
- * m a i n
- *
- * The main test routine. This creates a semaphore, then sets up a series
- * of sem_timedwait() calls on it. For each call it starts a thread that
- * will release the semaphore after a random time, and specifies another
- * similar random time as the timeout for the sem_timedwait() call. It
- * then checks that what happens is what it expects.
- *
- * The pogram takes two optional arguments. The first is an integer giving
- * the nuber of sem_timedwait() calls it is to attempt (default 10) and the
- * second is a floatig point value that gives the time scale - it is the
- * maximum time in seconds for the two random times that are generated for
- * each sem_timedwait() call. The times should be fairly evenly distributed
- * between this value and a hard-coded minimum of 0.001 seconds. The default
- * time scale is 1.0.
- *
- * Note that on all systems, if the difference between the two random times
- * (the timeout and the delay before the semaphore is given) is comparable
- * with the scheduling resolution of the system - which is only 10
- * milliseconds on a standard Linux 2.4 kernel - you can expect to get
- * some cases where the 'wrong' timer goes off first. So these cases are
- * logged, but anything with a difference of less than 10 msec isn't
- * included in the unexpected count.
- */
-
- int main (
- int argc,
- char* argv[])
- {
- char semName[1024]; /* Semaphore name if we need to use sem_open */
- sem_t theSem; /* The semaphore itself */
- sem_t *semAddr; /* The address of the semaphore */
- struct timespec absTime; /* Absolute time at which we timeout */
- struct timeval currentTime; /* The time right now */
- pthread_t giverThread; /* Id for the thread that releases the sem */
- giverDetails details; /* Details passed to the giver thread */
- int randomShort; /* Random number in range 0..65535 */
- float randomDelaySecs; /* Random delay before semaphore given */
- float randomTimeoutSecs; /* Random timeout value */
- int intSecs; /* Integer number of seconds */
- long intNsecs; /* Nanoseconds in delay time */
- int msecs; /* Milliseconds between the two times */
- short retry; /* Controls the EAGAIN loop */
- int count; /* Number of tries at the semaphore so far */
- int takenCount = 0; /* Number ot time the semaphore was taken */
- int unexpectedCount = 0; /* Number of unexpected occurrences */
- int timeoutCount = 0; /* Number of times we timed out */
- float timeScaleSecs = 1.0; /* Time scale - from command line */
- int maxCount = 10; /* Times through the test loop - from command line */
-
- /* Get the command line arguments, the number of tries, and the time
- * scale to use.
- */
-
- if (argc >= 3) {
- timeScaleSecs = atof(argv[2]);
- }
- if (argc >= 2) {
- maxCount = atoi(argv[1]);
- }
-
- /* Creating a semaphore is awkward - some systems support sem_init(),
- * which is nice, but OS X only supports sem_open() and returns ENOSYS
- * for sem_init(). This code handles both cases. The semaphore is
- * created taken.
- */
-
- semName[0] = '\0';
- semAddr = &theSem;
- if (sem_init(semAddr,0,0) < 0) {
- if (errno == ENOSYS) {
- sprintf (semName,"/tmp/test_%ld.sem",(long)getpid());
- if ((semAddr = sem_open(semName,
- O_CREAT|O_EXCL,S_IWUSR | S_IRUSR,0))
- == (sem_t*)SEM_FAILED) {
- perror ("creating semaphore");
- }
- }
- }
-
- /* Loop through the specified number of tests. */
-
- for (count = 0; count < maxCount; count++) {
-
- /* Generate random times for the timeout and for the delay before
- * the semaphore is given. I'm only using the last 16 bits from random()
- * becasuse that's good enough for this and saves me worrying about
- * handling really big integers near the 32 bit limit. First for the
- * delay used by the giver thread.
- */
-
- randomShort = random() & 0xffff;
- randomDelaySecs = ((float)randomShort)/65536.0 * timeScaleSecs;
- if (randomDelaySecs < 0.001) randomDelaySecs = 0.001;
- details.semAddr = semAddr;
- details.delaySecs = randomDelaySecs;
-
- /* And now for the timeout, which has to be converted into an absolute
- * time from now in the form required by sem_timedwait.
- */
-
- randomShort = random() & 0xffff;
- randomTimeoutSecs = ((float)randomShort)/65536.0 * timeScaleSecs;
- if (randomTimeoutSecs < 0.001) randomTimeoutSecs = 0.001;
- intSecs = (int)randomTimeoutSecs;
- intNsecs = (long)((randomTimeoutSecs - (float)intSecs) * 1000000000.0);
- gettimeofday (¤tTime,NULL);
- absTime.tv_sec = currentTime.tv_sec + intSecs;
- absTime.tv_nsec = (currentTime.tv_usec * 1000) + intNsecs;
- while (absTime.tv_nsec > 1000000000) {
- absTime.tv_sec++;
- absTime.tv_nsec -= 1000000000;
- }
-
- /* Create the 'giver' thread, which will release the semaphore after
- * the specified delay time.
- */
-
- pthread_create(&giverThread,NULL,giverThreadMain,(void*)&details);
-
- /* Now try to take the semaphore and see what happens - timeout or
- * a taken semaphore? The retry loop handles any cases where an
- * EAGAIN problem is signalled.
- */
-
- retry = TRUE;
- while (retry) {
- retry = FALSE;
- if (sem_timedwait (semAddr,&absTime) == 0) {
-
- /* We got the semaphore. See if we expected to. */
-
- takenCount++;
- if (randomDelaySecs > randomTimeoutSecs) {
- msecs = (int)((randomDelaySecs - randomTimeoutSecs) * 1000.0);
- printf (
- "Sem taken first, delay %f timeout %f, diff %d msec\n",
- randomDelaySecs,randomTimeoutSecs,msecs);
- if (msecs < 10) {
- printf ("Time difference too short to count as an error\n");
- } else {
- unexpectedCount++;
- }
- }
- } else {
-
- /* We failed. See if this was a timeout, in which case see if
- * it was expected. If not, check for EAGAIN and retry, or log
- * an error in all other cases.
- */
-
- if (errno != ETIMEDOUT) {
- if (errno == EAGAIN) {
- retry = TRUE;
- } else {
- perror ("Timed wait");
- }
- } else {
- timeoutCount++;
- if (randomDelaySecs < randomTimeoutSecs) {
- msecs = (int)((randomTimeoutSecs - randomDelaySecs) * 1000.0);
- printf (
- "Timedout first, delay %f timeout %f, diff %d msec\n",
- randomDelaySecs,randomTimeoutSecs,msecs);
- if (msecs < 10) {
- printf (
- "Time difference too short to count as an error\n");
- } else {
- unexpectedCount++;
- }
- }
- }
- }
- }
-
- /* Cancel the giver thread if it's still running (ie a timeout or other
- * error), and wait for it to complete - that's needed to release its
- * resources.
- */
-
- pthread_cancel(giverThread);
- pthread_join(giverThread,NULL);
-
- /* Something to show we're still running. */
-
- if (((count + 1) % 25) == 0) {
- printf ("Tries: %d, Taken %d, Timedout %d, unexpected %d\n",
- count + 1,takenCount, timeoutCount,unexpectedCount);
- }
-
- /* And then back to try again. Note that the semaphore shuld now be
- * taken. Either it was releaed by the giver and we took it in the
- * sem_timedwait() call, or we timed out, in which case it's still
- * taken. So the next sem_timedwait() call will wait, just like this
- * one.
- */
- }
- printf ("Final results: Taken %d, Timedout %d, unexpected %d\n",
- takenCount,timeoutCount,unexpectedCount);
- return unexpectedCount;
- }
-
- #endif
|