|
@@ -1,760 +0,0 @@ |
|
|
/* |
|
|
|
|
|
* 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*) (long) 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*) (long) 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 |
|
|
|