use pthread_condattr_setclock to avoid race conditions with the clock
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user