Added: Implementation of timer tests for linux

This commit is contained in:
kaetemi 2012-07-28 01:32:48 +02:00
parent 26112d1aa4
commit 36f5ec9ac2
2 changed files with 169 additions and 66 deletions

View file

@ -34,16 +34,16 @@ struct CPMainThread : public CPThread
{ {
CPMainThread() : CPThread(NULL, 0) CPMainThread() : CPThread(NULL, 0)
{ {
if(pthread_key_create(&threadSpecificKey, NULL) != 0) if(pthread_key_create(&threadSpecificKey, NULL) != 0)
throw EThread("cannot create thread specific storage key."); throw EThread("cannot create thread specific storage key.");
if(pthread_setspecific(threadSpecificKey, this) != 0) if(pthread_setspecific(threadSpecificKey, this) != 0)
throw EThread("cannot set main thread ptr in thread specific storage."); throw EThread("cannot set main thread ptr in thread specific storage.");
} }
~CPMainThread() ~CPMainThread()
{ {
if(pthread_key_delete(threadSpecificKey) != 0) if(pthread_key_delete(threadSpecificKey) != 0)
throw EThread("cannot delete thread specific storage key."); throw EThread("cannot delete thread specific storage key.");
} }
}; };
@ -139,7 +139,7 @@ void CPThread::start()
/* setting the size of the stack also */ /* setting the size of the stack also */
ret = pthread_attr_setstacksize(&tattr, _StackSize); ret = pthread_attr_setstacksize(&tattr, _StackSize);
} }
bool detach_old_thread = false; bool detach_old_thread = false;
pthread_t old_thread_handle; pthread_t old_thread_handle;
if (_State != ThreadStateNone) if (_State != ThreadStateNone)
@ -221,6 +221,9 @@ void CPThread::wait ()
bool CPThread::setCPUMask(uint64 cpuMask) bool CPThread::setCPUMask(uint64 cpuMask)
{ {
#ifdef __USE_GNU #ifdef __USE_GNU
nlwarning("This code does not work. May cause a segmentation fault...");
sint res = pthread_setaffinity_np(_ThreadHandle, sizeof(uint64), (const cpu_set_t*)&cpuMask); sint res = pthread_setaffinity_np(_ThreadHandle, sizeof(uint64), (const cpu_set_t*)&cpuMask);
if (res) if (res)
@ -228,9 +231,14 @@ bool CPThread::setCPUMask(uint64 cpuMask)
nlwarning("pthread_setaffinity_np() returned %d", res); nlwarning("pthread_setaffinity_np() returned %d", res);
return false; return false;
} }
#endif // __USE_GNU
return true; return true;
#else // __USE_GNU
return false;
#endif // __USE_GNU
} }
/* /*
@ -238,9 +246,12 @@ bool CPThread::setCPUMask(uint64 cpuMask)
*/ */
uint64 CPThread::getCPUMask() uint64 CPThread::getCPUMask()
{ {
uint64 cpuMask = 1;
#ifdef __USE_GNU #ifdef __USE_GNU
nlwarning("This code does not work. May cause a segmentation fault...");
uint64 cpuMask = 0;
sint res = pthread_getaffinity_np(_ThreadHandle, sizeof(uint64), (cpu_set_t*)&cpuMask); sint res = pthread_getaffinity_np(_ThreadHandle, sizeof(uint64), (cpu_set_t*)&cpuMask);
if (res) if (res)
@ -248,9 +259,14 @@ uint64 CPThread::getCPUMask()
nlwarning("pthread_getaffinity_np() returned %d", res); nlwarning("pthread_getaffinity_np() returned %d", res);
return 0; return 0;
} }
#endif // __USE_GNU
return cpuMask; return cpuMask;
#else // __USE_GNU
return 0;
#endif // __USE_GNU
} }
void CPThread::setPriority(TThreadPriority priority) void CPThread::setPriority(TThreadPriority priority)
@ -311,9 +327,9 @@ IProcess *IProcess::getCurrentProcess ()
*/ */
uint64 CPProcess::getCPUMask() uint64 CPProcess::getCPUMask()
{ {
uint64 cpuMask = 1;
#ifdef __USE_GNU #ifdef __USE_GNU
uint64 cpuMask = 0;
sint res = sched_getaffinity(getpid(), sizeof(uint64), (cpu_set_t*)&cpuMask); sint res = sched_getaffinity(getpid(), sizeof(uint64), (cpu_set_t*)&cpuMask);
if (res) if (res)
@ -321,15 +337,21 @@ uint64 CPProcess::getCPUMask()
nlwarning("sched_getaffinity() returned %d, errno = %d: %s", res, errno, strerror(errno)); nlwarning("sched_getaffinity() returned %d, errno = %d: %s", res, errno, strerror(errno));
return 0; return 0;
} }
#endif // __USE_GNU
return cpuMask; return cpuMask;
#else // __USE_GNU
return 0;
#endif // __USE_GNU
} }
/// set the CPU mask /// set the CPU mask
bool CPProcess::setCPUMask(uint64 cpuMask) bool CPProcess::setCPUMask(uint64 cpuMask)
{ {
#ifdef __USE_GNU #ifdef __USE_GNU
sint res = sched_setaffinity(getpid(), sizeof(uint64), (const cpu_set_t*)&cpuMask); sint res = sched_setaffinity(getpid(), sizeof(uint64), (const cpu_set_t*)&cpuMask);
if (res) if (res)
@ -337,9 +359,14 @@ bool CPProcess::setCPUMask(uint64 cpuMask)
nlwarning("sched_setaffinity() returned %d, errno = %d: %s", res, errno, strerror(errno)); nlwarning("sched_setaffinity() returned %d, errno = %d: %s", res, errno, strerror(errno));
return false; return false;
} }
#endif // __USE_GNU
return true; return true;
#else // __USE_GNU
return false;
#endif // __USE_GNU
} }

View file

@ -42,13 +42,63 @@ namespace {
bool a_HaveQueryPerformance = false; bool a_HaveQueryPerformance = false;
LARGE_INTEGER a_QueryPerformanceFrequency; LARGE_INTEGER a_QueryPerformanceFrequency;
#endif #endif
#ifdef NL_OS_UNIX
# if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
# if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK >= 0)
# define NL_MONOTONIC_CLOCK
# endif
# endif
# ifdef NL_MONOTONIC_CLOCK
bool a_CheckedMonotonicClock = false;
bool a_HasMonotonicClock = false;
uint64 a_MonotonicClockFrequency = 0;
uint64 a_MonotonicClockResolutionNs = 0;
bool hasMonotonicClock()
{
if (!a_CheckedMonotonicClock)
{
/* Initialize the local time engine.
* On Unix, this method will find out if the Monotonic Clock is supported
* (seems supported by kernel 2.6, not by kernel 2.4). See getLocalTime().
*/
struct timespec tv;
if ((clock_gettime( CLOCK_MONOTONIC, &tv ) == 0) &&
(clock_getres( CLOCK_MONOTONIC, &tv ) == 0))
{
// nldebug( "Monotonic local time supported (resolution %.6f ms)", ((float)tv.tv_sec)*1000.0f + ((float)tv.tv_nsec)/1000000.0f );
if (tv.tv_sec > 0)
{
nlwarning("Monotonic clock not ok, resolution > 1s");
a_HasMonotonicClock = false;
}
else
{
uint64 nsPerTick = tv.tv_nsec;
uint64 nsPerSec = 1000000000L;
uint64 tickPerSec = nsPerSec / nsPerTick;
a_MonotonicClockFrequency = tickPerSec;
a_MonotonicClockResolutionNs = nsPerTick;
a_HasMonotonicClock = true;
}
}
else
{
a_HasMonotonicClock = false;
}
a_CheckedMonotonicClock = true;
}
return a_HasMonotonicClock;
}
# endif
#endif
} }
void CTime::probeTimerInfo(CTime::CTimerInfo &result) void CTime::probeTimerInfo(CTime::CTimerInfo &result)
{ {
breakable breakable
{ {
#ifdef NL_OS_WINDOWS #ifdef NL_OS_WINDOWS
LARGE_INTEGER winPerfFreq; LARGE_INTEGER winPerfFreq;
LARGE_INTEGER winPerfCount; LARGE_INTEGER winPerfCount;
DWORD lowResTime; DWORD lowResTime;
@ -78,21 +128,34 @@ void CTime::probeTimerInfo(CTime::CTimerInfo &result)
{ {
lowResTime = timeGetTime(); lowResTime = timeGetTime();
} }
#else #else
// nldebug("Probe of timer info not implemented");
result.IsHighPrecisionAvailable = false; // Other platforms are awesome. Generic implementation for now.
result.RequiresSingleCore = true; TTime localTime = getLocalTime();
break; result.IsHighPrecisionAvailable = true;
#endif result.HighPrecisionResolution = 0;
# ifdef NL_MONOTONIC_CLOCK
timespec monoClock;
if (hasMonotonicClock())
{
clock_gettime(CLOCK_MONOTONIC, &monoClock);
result.HighPrecisionResolution = a_MonotonicClockFrequency;
}
else
{
nldebug("Monotonic clock not available");
}
# endif
#endif
uint64 cpuMask = IProcess::getCurrentProcess()->getCPUMask(); uint64 cpuMask = IProcess::getCurrentProcess()->getCPUMask();
uint64 threadMask = IThread::getCurrentThread()->getCPUMask(); #ifdef NL_OS_WINDOWS
uint64 threadMask = IThread::getCurrentThread()->getCPUMask(); // broken on linux, don't expect it to work anywhere
#ifdef NL_OS_WINDOWS #else
uint64 threadMask = cpuMask;
#else #endif
TTicks timerFrequency = 0;
#endif
uint identical = 0; // Identical stamps may indicate the os handling backwards glitches. uint identical = 0; // Identical stamps may indicate the os handling backwards glitches.
uint backwards = 0; // Happens when the timers are not always in sync and the implementation is faulty. uint backwards = 0; // Happens when the timers are not always in sync and the implementation is faulty.
@ -109,7 +172,12 @@ void CTime::probeTimerInfo(CTime::CTimerInfo &result)
{ {
if (cpuMask & currentBit) if (cpuMask & currentBit)
{ {
IThread::getCurrentThread()->setCPUMask(currentBit); #ifdef NL_OS_WINDOWS
if (!IThread::getCurrentThread()->setCPUMask(currentBit))
#else
if (!IProcess::getCurrentProcess()->setCPUMask(currentBit))
#endif
break; // Thread was set to last cpu.
#ifdef NL_OS_WINDOWS #ifdef NL_OS_WINDOWS
// Make sure the thread is rescheduled. // Make sure the thread is rescheduled.
SwitchToThread(); SwitchToThread();
@ -152,13 +220,53 @@ void CTime::probeTimerInfo(CTime::CTimerInfo &result)
++regular; ++regular;
lowResTime = lowResTimeN; lowResTime = lowResTimeN;
} }
#else
#ifdef NL_OS_UNIX
sched_yield();
#else
nlSleep(0);
#endif
# ifdef NL_MONOTONIC_CLOCK
if (hasMonotonicClock())
{
timespec monoClockN;
clock_gettime(CLOCK_MONOTONIC, &monoClockN);
if (monoClock.tv_sec == monoClockN.tv_sec && monoClock.tv_nsec == monoClockN.tv_nsec)
++identical;
if (monoClockN.tv_sec < monoClock.tv_sec || (monoClock.tv_sec == monoClockN.tv_sec && monoClockN.tv_nsec < monoClock.tv_nsec))
++backwards;
if (monoClock.tv_sec == monoClockN.tv_sec && (monoClockN.tv_nsec - monoClock.tv_nsec > 50000000L))
++skipping;
else if ((monoClock.tv_sec == monoClockN.tv_sec && monoClock.tv_nsec < monoClockN.tv_nsec) || monoClock.tv_sec < monoClockN.tv_sec)
++regular;
monoClock.tv_sec = monoClockN.tv_sec;
monoClock.tv_nsec = monoClockN.tv_nsec;
}
else
# endif
{
TTime localTimeN = getLocalTime();
if (localTimeN == localTime)
++identical;
if (localTimeN < localTime || localTimeN - localTime < 0)
++backwards;
if (localTimeN - localTime > 50)
++skipping;
else if (localTimeN > localTime)
++regular;
localTime = localTimeN;
}
#endif #endif
} }
currentBit <<= 1; currentBit <<= 1;
} }
} }
#ifdef NL_OS_WINDOWS
IThread::getCurrentThread()->setCPUMask(threadMask); IThread::getCurrentThread()->setCPUMask(threadMask);
#else
IProcess::getCurrentProcess()->setCPUMask(threadMask);
#endif
nldebug("Timer resolution: %i Hz", (int)(result.HighPrecisionResolution)); nldebug("Timer resolution: %i Hz", (int)(result.HighPrecisionResolution));
nldebug("Time identical: %i, backwards: %i, regular: %i, skipping: %i, frequency bug: %i", identical, backwards, regular, skipping, frequencybug); nldebug("Time identical: %i, backwards: %i, regular: %i, skipping: %i, frequency bug: %i", identical, backwards, regular, skipping, frequencybug);
@ -245,10 +353,10 @@ TTime CTime::getLocalTime ()
// return timeGetTime(); // Only this was left active before it was commented. // return timeGetTime(); // Only this was left active before it was commented.
//} //}
/* /*
* The above is no longer relevant. * The above is no longer relevant.
*/ */
if (a_HaveQueryPerformance) if (a_HaveQueryPerformance)
{ {
// On a (fast) 15MHz timer this rolls over after 7000 days. // On a (fast) 15MHz timer this rolls over after 7000 days.
@ -266,49 +374,17 @@ TTime CTime::getLocalTime ()
#elif defined (NL_OS_UNIX) #elif defined (NL_OS_UNIX)
static bool initdone = false; #ifdef NL_MONOTONIC_CLOCK
static bool isMonotonicClockSupported = false;
if ( ! initdone ) if (hasMonotonicClock())
{ {
timespec tv;
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
#if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK >= 0)
/* Initialize the local time engine.
* On Unix, this method will find out if the Monotonic Clock is supported
* (seems supported by kernel 2.6, not by kernel 2.4). See getLocalTime().
*/
struct timespec tv;
if ( (clock_gettime( CLOCK_MONOTONIC, &tv ) == 0) &&
(clock_getres( CLOCK_MONOTONIC, &tv ) == 0) )
{
// nldebug( "Monotonic local time supported (resolution %.6f ms)", ((float)tv.tv_sec)*1000.0f + ((float)tv.tv_nsec)/1000000.0f );
isMonotonicClockSupported = true;
}
else
#endif
#endif
{
// nlwarning( "Monotonic local time not supported, caution with time sync" );
}
initdone = true;
}
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
#if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK >= 0)
if ( isMonotonicClockSupported )
{
struct timespec tv;
// This is not affected by system time changes. // This is not affected by system time changes.
if ( clock_gettime( CLOCK_MONOTONIC, &tv ) != 0 ) if ( clock_gettime( CLOCK_MONOTONIC, &tv ) != 0 )
nlerror ("Can't get clock time again"); nlerror ("Can't get clock time again");
return (TTime)tv.tv_sec * (TTime)1000 + (TTime)((tv.tv_nsec/*+500*/) / 1000000); return (TTime)tv.tv_sec * (TTime)1000 + (TTime)((tv.tv_nsec/*+500*/) / 1000000);
} }
#endif
#endif #endif
// This is affected by system time changes. // This is affected by system time changes.
@ -346,7 +422,7 @@ TTicks CTime::getPerformanceTime ()
return (hi << 32) | (lo & 0xffffffff); return (hi << 32) | (lo & 0xffffffff);
#elif defined(HAVE_X86) and !defined(NL_OS_MAC) #elif defined(HAVE_X86) and !defined(NL_OS_MAC)
uint64 x; uint64 x;
// RDTSC - Read time-stamp counter into EDX:EAX. // RDTSC - Read time-stamp counter into EDX:EAX.
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return x; return x;
#else // HAVE_X86 #else // HAVE_X86