use pthread_condattr_setclock to avoid race conditions with the clock

This commit is contained in:
Glenn Maynard
2006-07-12 01:47:10 +00:00
parent 22dc123b35
commit 6e245f46ae
+105 -18
View File
@@ -209,10 +209,92 @@ MutexImpl *MakeMutex( RageMutex *pParent )
return new MutexImpl_Pthreads( pParent );
}
/* Check if condattr_setclock is supported, and supports the clock that RageTimer
* selected. */
#if defined(UNIX)
#include <dlfcn.h>
#include "arch/ArchHooks/ArchHooks_Unix.h"
#endif
namespace
{
typedef int (* CONDATTR_SET_CLOCK)( pthread_condattr_t *attr, clockid_t clock_id );
CONDATTR_SET_CLOCK g_CondattrSetclock = NULL;
bool bInitialized = false;
#if defined(UNIX)
clockid_t GetClock()
{
return ArchHooks_Unix::GetClock();
}
void InitMonotonic()
{
if( bInitialized )
return;
bInitialized = true;
void *pLib = NULL;
do {
{
pLib = dlopen( NULL, RTLD_LAZY );
if( pLib == NULL )
break;
g_CondattrSetclock = (CONDATTR_SET_CLOCK) dlsym( pLib, "pthread_condattr_setclock" );
if( g_CondattrSetclock == NULL )
break;
}
/* Make sure that we can set up the clock attribute. */
pthread_condattr_t condattr;
pthread_condattr_init( &condattr );
if( g_CondattrSetclock(&condattr, GetClock()) != 0 )
{
printf( "pthread_condattr_setclock failed\n" );
pthread_condattr_destroy( &condattr );
break;
}
pthread_condattr_destroy( &condattr );
/* Everything seems to work. */
return;
} while(0);
g_CondattrSetclock = NULL;
if( pLib != NULL )
dlclose( pLib );
pLib = NULL;
}
#else
void InitMonotonic()
{
bInitialized = true;
}
clockid_t GetClock()
{
return CLOCK_REALTIME;
}
#endif
};
EventImpl_Pthreads::EventImpl_Pthreads( MutexImpl_Pthreads *pParent )
{
m_pParent = pParent;
pthread_cond_init( &m_Cond, NULL );
InitMonotonic();
pthread_condattr_t condattr;
pthread_condattr_init( &condattr );
if( g_CondattrSetclock != NULL )
g_CondattrSetclock( &condattr, GetClock() );
pthread_cond_init( &m_Cond, &condattr );
pthread_condattr_destroy( &condattr );
}
EventImpl_Pthreads::~EventImpl_Pthreads()
@@ -229,29 +311,34 @@ bool EventImpl_Pthreads::Wait( RageTimer *pTimeout )
return true;
}
#if 1
float fSecondsInFuture = -pTimeout->Ago();
RageTimer timeofday;
/* If the clock is not CLOCK_MONOTONIC, or we can't change the wait clock (no
* condattr_setclock), pthread_cond_timedwait has an inherent race condition:
* the system clock may change before we call it. */
timespec abstime;
if( g_CondattrSetclock != NULL || GetClock() == CLOCK_REALTIME )
{
/* If we support condattr_setclock, we'll set the condition to use the same
* clock as RageTimer and can use it directly. If the clock is CLOCK_REALTIME,
* that's the default anyway. */
abstime.tv_sec = pTimeout->m_secs;
abstime.tv_nsec = pTimeout->m_us * 1000;
}
else
{
/* The RageTimer clock is different than the wait clock; convert it. */
timeval tv;
gettimeofday( &tv, NULL );
RageTimer timeofday;
timeofday.m_secs = tv.tv_sec;
timeofday.m_us = tv.tv_usec;
}
timeofday += fSecondsInFuture;
timespec abstime;
abstime.tv_sec = timeofday.m_secs;
abstime.tv_nsec = timeofday.m_us * 1000;
#else
/* XXX: This is how it should look for CLOCK_MONOTONIC: simply use the
* time directly. This requires that the condition be set to that clock,
* which requires pthread_condattr_setclock(). */
abstime.tv_sec = pTimeout->m_secs;
abstime.tv_nsec = pTimeout->m_us * 1000;
#endif
float fSecondsInFuture = -pTimeout->Ago();
timeofday += fSecondsInFuture;
abstime.tv_sec = timeofday.m_secs;
abstime.tv_nsec = timeofday.m_us * 1000;
}
int iRet = pthread_cond_timedwait( &m_Cond, &m_pParent->mutex, &abstime );
return iRet != ETIMEDOUT;