From 96d8d45c1d18f1666cb34068de1a5eef79409e0b Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 27 Jul 2012 21:20:07 +0200 Subject: [PATCH 1/9] Added: Function and tool to check system timer sanity across cpu cores --- code/nel/include/nel/misc/time_nl.h | 13 ++ code/nel/src/misc/time_nl.cpp | 136 ++++++++++++++++++ code/nel/tools/misc/CMakeLists.txt | 2 + .../tools/misc/probe_timers/CMakeLists.txt | 9 ++ code/nel/tools/misc/probe_timers/main.cpp | 38 +++++ 5 files changed, 198 insertions(+) create mode 100644 code/nel/tools/misc/probe_timers/CMakeLists.txt create mode 100644 code/nel/tools/misc/probe_timers/main.cpp diff --git a/code/nel/include/nel/misc/time_nl.h b/code/nel/include/nel/misc/time_nl.h index 2bd3edb72..9e44b4b36 100644 --- a/code/nel/include/nel/misc/time_nl.h +++ b/code/nel/include/nel/misc/time_nl.h @@ -48,6 +48,19 @@ typedef sint64 TTicks; class CTime { public: + struct CTimerInfo + { + /// Returns if there is a high precision timer that can be used. + bool IsHighPrecisionAvailable; + /// If a CPU specific timer is used and the values are not consistent accross threads. + bool RequiresSingleCore; + /// The resolution of the high resolution timer. + TTicks HighPrecisionResolution; + }; + + /** Get advanced information on the used timers. + */ + static void probeTimerInfo(CTimerInfo &result); /** Return the number of second since midnight (00:00:00), January 1, 1970, * coordinated universal time, according to the system clock. diff --git a/code/nel/src/misc/time_nl.cpp b/code/nel/src/misc/time_nl.cpp index f018208f9..4a3586baa 100644 --- a/code/nel/src/misc/time_nl.cpp +++ b/code/nel/src/misc/time_nl.cpp @@ -32,9 +32,145 @@ #include "nel/misc/time_nl.h" #include "nel/misc/sstring.h" +#include + namespace NLMISC { +void CTime::probeTimerInfo(CTime::CTimerInfo &result) +{ + breakable + { + #ifdef NL_OS_WINDOWS + LARGE_INTEGER winPerfFreq; + LARGE_INTEGER winPerfCount; + DWORD lowResTime; + if (!QueryPerformanceFrequency(&winPerfFreq)) + { + nldebug("Cannot query performance frequency"); + result.IsHighPrecisionAvailable = false; + } + else + { + result.HighPrecisionResolution = winPerfFreq.QuadPart; + } + if (winPerfFreq.QuadPart == 1000) + { + nldebug("Higher precision timer not available, OS defaulted to GetTickCount"); + result.IsHighPrecisionAvailable = false; + } + if (!QueryPerformanceCounter(&winPerfCount)) + { + nldebug("Cannot query performance counter"); + result.IsHighPrecisionAvailable = false; + result.HighPrecisionResolution = 1000; + } + if (!result.IsHighPrecisionAvailable) + { + lowResTime = timeGetTime(); + } + #else + // nldebug("Probe of timer info not implemented"); + result.IsHighPrecisionAvailable = false; + result.RequiresSingleCore = true; + break; + #endif + + uint64 cpuMask = IProcess::getCurrentProcess()->getCPUMask(); + uint64 threadMask = IThread::getCurrentThread()->getCPUMask(); + + #ifdef NL_OS_WINDOWS + + #else + TTicks timerFrequency = 0; + #endif + + 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 regular = 0; // How many times the number advanced normally. + uint skipping = 0; // Does not really mean anything necessarily. + uint frequencybug = 0; // Should never happen. + // uint badcore = 0; // Affinity does not work. + + // Cycle 32 times trough all cores, and verify if the timing remains consistent. + for (uint i = 32; i; --i) + { + uint64 currentBit = 1; + for (uint j = 64; j; --j) + { + if (cpuMask & currentBit) + { + IThread::getCurrentThread()->setCPUMask(currentBit); +#ifdef NL_OS_WINDOWS + // Make sure the thread is rescheduled. + SwitchToThread(); + Sleep(0); + // Verify the core + /* Can only verify on 2003, Vista and higher. + if (1 << GetCurrentProcessorNumber() != currentBit) + ++badcore; + */ + // Check if the timer is still sane. + if (result.IsHighPrecisionAvailable) + { + LARGE_INTEGER winPerfFreqN; + LARGE_INTEGER winPerfCountN; + QueryPerformanceFrequency(&winPerfFreqN); + if (winPerfFreqN.QuadPart != winPerfFreq.QuadPart) + ++frequencybug; + QueryPerformanceCounter(&winPerfCountN); + if (winPerfCountN.QuadPart == winPerfCount.QuadPart) + ++identical; + if (winPerfCountN.QuadPart < winPerfCount.QuadPart || winPerfCountN.QuadPart - winPerfCount.QuadPart < 0) + ++backwards; + if (winPerfCountN.QuadPart - winPerfCount.QuadPart > winPerfFreq.QuadPart / 20) // 50ms skipping check + ++skipping; + else if (winPerfCountN.QuadPart > winPerfCount.QuadPart) + ++regular; + winPerfCount.QuadPart = winPerfCountN.QuadPart; + } + else + { + DWORD lowResTimeN; + lowResTimeN = timeGetTime(); + if (lowResTimeN == lowResTime) + ++identical; + if (lowResTimeN < lowResTime || lowResTimeN - lowResTime < 0) + ++backwards; + if (lowResTimeN - lowResTime > 50) + ++skipping; + else if (lowResTimeN > lowResTime) + ++regular; + lowResTime = lowResTimeN; + } +#endif + } + currentBit <<= 1; + } + } + + IThread::getCurrentThread()->setCPUMask(threadMask); + + 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); + if (identical > regular) + nlwarning("The system timer is of relatively low resolution, you may experience issues"); + if (backwards > 0 || frequencybug > 0) + { + nlwarning("The current system timer is not reliable across multiple cpu cores"); + result.RequiresSingleCore = true; + } + else result.RequiresSingleCore = false; + + if (result.HighPrecisionResolution == 14318180) + nldebug("Detected known HPET era timer frequency"); + if (result.HighPrecisionResolution == 3579545) + nldebug("Detected known AHCI era timer frequency"); + if (result.HighPrecisionResolution == 1193182) + nldebug("Detected known i8253/i8254 era timer frequency"); + } +} + /* Return the number of second since midnight (00:00:00), January 1, 1970, * coordinated universal time, according to the system clock. * This values is the same on all computer if computers are synchronized (with NTP for example). diff --git a/code/nel/tools/misc/CMakeLists.txt b/code/nel/tools/misc/CMakeLists.txt index fcb259a64..5386cbbc6 100644 --- a/code/nel/tools/misc/CMakeLists.txt +++ b/code/nel/tools/misc/CMakeLists.txt @@ -18,3 +18,5 @@ IF(WIN32) ADD_SUBDIRECTORY(words_dic) ENDIF(MFC_FOUND) ENDIF(WIN32) + +ADD_SUBDIRECTORY(probe_timers) diff --git a/code/nel/tools/misc/probe_timers/CMakeLists.txt b/code/nel/tools/misc/probe_timers/CMakeLists.txt new file mode 100644 index 000000000..df0f34926 --- /dev/null +++ b/code/nel/tools/misc/probe_timers/CMakeLists.txt @@ -0,0 +1,9 @@ +FILE(GLOB SRC *.cpp) + +ADD_EXECUTABLE(nl_probe_timers ${SRC}) + +TARGET_LINK_LIBRARIES(nl_probe_timers nelmisc) +NL_DEFAULT_PROPS(nl_probe_timers "NeL, Tools, Misc: Probe Timers") +NL_ADD_RUNTIME_FLAGS(nl_probe_timers) + +INSTALL(TARGETS nl_probe_timers RUNTIME DESTINATION bin COMPONENT toolsmisc) diff --git a/code/nel/tools/misc/probe_timers/main.cpp b/code/nel/tools/misc/probe_timers/main.cpp new file mode 100644 index 000000000..de4c1f785 --- /dev/null +++ b/code/nel/tools/misc/probe_timers/main.cpp @@ -0,0 +1,38 @@ +// NeL - MMORPG Framework +// Copyright (C) 2012 by authors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include +#include +#include + +#include "nel/misc/types_nl.h" +#include "nel/misc/time_nl.h" + +using namespace NLMISC; + +int main (int argc, char **argv) +{ + for (uint i = 0; i < 8; ++i) + { + CTime::CTimerInfo timerInfo; + CTime::probeTimerInfo(timerInfo); + } + + printf ("\nPress to exit\n"); + getchar (); + + return EXIT_SUCCESS; +} From bf70ca6bb2e3cd92e373205ed55f827e86a2f0cf Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 27 Jul 2012 22:26:49 +0200 Subject: [PATCH 2/9] Added: Function to prefer high resolution local time --- code/nel/include/nel/misc/time_nl.h | 8 +++++- code/nel/src/misc/time_nl.cpp | 44 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/code/nel/include/nel/misc/time_nl.h b/code/nel/include/nel/misc/time_nl.h index 9e44b4b36..62161e304 100644 --- a/code/nel/include/nel/misc/time_nl.h +++ b/code/nel/include/nel/misc/time_nl.h @@ -86,7 +86,13 @@ public: * time that is the same on all computers. * \warning On Win32, the value is on 32 bits only. It wraps around to 0 every about 49.71 days. */ - static TTime getLocalTime (); + static TTime getLocalTime(); + + /** Same as getLocalTime, but prefers high resolution timers. + * Must call probe once in the beginning of the application before using, + * to ensure the correct settings are applied. + */ + static TTime getLocalTimeHR(); /** Return the time in processor ticks. Use it for profile purpose. * If the performance time is not supported on this hardware, it returns 0. diff --git a/code/nel/src/misc/time_nl.cpp b/code/nel/src/misc/time_nl.cpp index 4a3586baa..11a920a79 100644 --- a/code/nel/src/misc/time_nl.cpp +++ b/code/nel/src/misc/time_nl.cpp @@ -37,6 +37,12 @@ namespace NLMISC { +namespace { +#ifdef NL_OS_WINDOWS +bool a_HaveQueryPerformance = false; +#endif +} + void CTime::probeTimerInfo(CTime::CTimerInfo &result) { breakable @@ -65,6 +71,7 @@ void CTime::probeTimerInfo(CTime::CTimerInfo &result) result.IsHighPrecisionAvailable = false; result.HighPrecisionResolution = 1000; } + a_HaveQueryPerformance = result.IsHighPrecisionAvailable; if (!result.IsHighPrecisionAvailable) { lowResTime = timeGetTime(); @@ -292,6 +299,43 @@ TTime CTime::getLocalTime () #endif } +#ifdef NL_OS_WINDOWS +namespace { +struct CQPFProvider +{ + CQPFProvider() + { + QueryPerformanceFrequency(&Frequency); + } + LARGE_INTEGER Frequency; +}; +CQPFProvider s_QPFProvider; +} +#endif + +/// Same as above but prefer high resolution timer +TTime CTime::getLocalTimeHR() +{ +#ifdef NL_OS_WINDOWS + if (a_HaveQueryPerformance) + { + // On a (fast) 15MHz timer this rolls over after 7000 days. + // If my calculations are right. + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + counter.QuadPart *= 1000; + counter.QuadPart /= s_QPFProvider.Frequency.QuadPart; + } + else + { + // Use default reliable low resolution timer. + return getLocalTime(); + } +#else + // Other OS always use the best available high resolution timer. + return getLocalTime(); +#endif +} /* Return the time in processor ticks. Use it for profile purpose. * If the performance time is not supported on this hardware, it returns 0. From 1c9c2eededb26c27544480202694e29cddb0a497 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 27 Jul 2012 22:33:07 +0200 Subject: [PATCH 3/9] Changed: Simplify some things --- code/nel/include/nel/misc/time_nl.h | 2 +- code/nel/src/misc/time_nl.cpp | 20 ++++---------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/code/nel/include/nel/misc/time_nl.h b/code/nel/include/nel/misc/time_nl.h index 62161e304..ac9ea619c 100644 --- a/code/nel/include/nel/misc/time_nl.h +++ b/code/nel/include/nel/misc/time_nl.h @@ -84,7 +84,7 @@ public: * the value can jump backwards if the system time is changed by a user or a NTP time sync process. * The value is different on 2 different computers; use the CUniTime class to get a universal * time that is the same on all computers. - * \warning On Win32, the value is on 32 bits only. It wraps around to 0 every about 49.71 days. + * \warning On Win32, the value is on 32 bits only, and uses the low-res timer. It wraps around to 0 every about 49.71 days. */ static TTime getLocalTime(); diff --git a/code/nel/src/misc/time_nl.cpp b/code/nel/src/misc/time_nl.cpp index 11a920a79..065db103e 100644 --- a/code/nel/src/misc/time_nl.cpp +++ b/code/nel/src/misc/time_nl.cpp @@ -40,6 +40,7 @@ namespace NLMISC namespace { #ifdef NL_OS_WINDOWS bool a_HaveQueryPerformance = false; +LARGE_INTEGER a_QueryPerformanceFrequency; #endif } @@ -72,6 +73,7 @@ void CTime::probeTimerInfo(CTime::CTimerInfo &result) result.HighPrecisionResolution = 1000; } a_HaveQueryPerformance = result.IsHighPrecisionAvailable; + a_QueryPerformanceFrequency.QuadPart = winPerfFreq.QuadPart; if (!result.IsHighPrecisionAvailable) { lowResTime = timeGetTime(); @@ -299,20 +301,6 @@ TTime CTime::getLocalTime () #endif } -#ifdef NL_OS_WINDOWS -namespace { -struct CQPFProvider -{ - CQPFProvider() - { - QueryPerformanceFrequency(&Frequency); - } - LARGE_INTEGER Frequency; -}; -CQPFProvider s_QPFProvider; -} -#endif - /// Same as above but prefer high resolution timer TTime CTime::getLocalTimeHR() { @@ -323,8 +311,8 @@ TTime CTime::getLocalTimeHR() // If my calculations are right. LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - counter.QuadPart *= 1000; - counter.QuadPart /= s_QPFProvider.Frequency.QuadPart; + counter.QuadPart *= (LONGLONG)1000L; + counter.QuadPart /= a_QueryPerformanceFrequency.QuadPart; } else { From 6404f8eafaed13ccaef0d937a1ec187d92dff5c3 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 27 Jul 2012 22:35:26 +0200 Subject: [PATCH 4/9] Changed: Use the high resolution local time function instead of the funky code when getting the local time in the client. Use the timer probe function to check if it is necessary to set the cpu mask in the client --- code/ryzom/client/src/init.cpp | 7 +++++-- code/ryzom/client/src/time_client.h | 19 ++----------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/code/ryzom/client/src/init.cpp b/code/ryzom/client/src/init.cpp index 11d01934f..bb2540824 100644 --- a/code/ryzom/client/src/init.cpp +++ b/code/ryzom/client/src/init.cpp @@ -686,8 +686,11 @@ void prelogInit() #ifdef NL_OS_WINDOWS _control87 (_EM_INVALID|_EM_DENORMAL/*|_EM_ZERODIVIDE|_EM_OVERFLOW*/|_EM_UNDERFLOW|_EM_INEXACT, _MCW_EM); #endif // NL_OS_WINDOWS - - setCPUMask(); + + CTime::CTimerInfo timerInfo; + NLMISC::CTime::probeTimerInfo(timerInfo); + if (timerInfo.RequiresSingleCore) // TODO: Also have a FV configuration value to force single core. + setCPUMask(); FPU_CHECKER_ONCE diff --git a/code/ryzom/client/src/time_client.h b/code/ryzom/client/src/time_client.h index 80b21195b..436cf1465 100644 --- a/code/ryzom/client/src/time_client.h +++ b/code/ryzom/client/src/time_client.h @@ -134,24 +134,9 @@ void updateClientTime(); // update smoothed time (useful for sky animation) void updateSmoothedTime(); -inline TTime ryzomGetLocalTime () +inline NLMISC::TTime ryzomGetLocalTime() { -#ifdef NL_OS_WINDOWS - if (ClientCfg.TimerMode == 0) - { - return (TTime)(NLMISC::CTime::ticksToSecond (NLMISC::CTime::getPerformanceTime()) * 1000.0); - } - else // if (ClientCfg.TimerMode == 1) - { - // Use 1 ms timer precision - timeBeginPeriod(1); - DWORD start = timeGetTime(); - timeEndPeriod(1); - return (TTime)start; - } -#else // NL_OS_WINDOWS - return (TTime)(NLMISC::CTime::ticksToSecond (NLMISC::CTime::getPerformanceTime()) * 1000.0); -#endif // NL_OS_WINDOWS + return NLMISC::CTime::getLocalTimeHR(); } inline NLMISC::TTicks ryzomGetPerformanceTime() From 555336bbea88f1c71325ad2104e5200e2a7e4ab7 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 27 Jul 2012 22:49:17 +0200 Subject: [PATCH 5/9] Changed: Simplify more --- code/nel/include/nel/misc/time_nl.h | 8 +---- code/nel/src/misc/time_nl.cpp | 45 +++++++++++++---------------- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/code/nel/include/nel/misc/time_nl.h b/code/nel/include/nel/misc/time_nl.h index ac9ea619c..e7deaae24 100644 --- a/code/nel/include/nel/misc/time_nl.h +++ b/code/nel/include/nel/misc/time_nl.h @@ -84,16 +84,10 @@ public: * the value can jump backwards if the system time is changed by a user or a NTP time sync process. * The value is different on 2 different computers; use the CUniTime class to get a universal * time that is the same on all computers. - * \warning On Win32, the value is on 32 bits only, and uses the low-res timer. It wraps around to 0 every about 49.71 days. + * \warning On Win32, the value is on 32 bits only, and uses the low-res timer unless probeTimerInfo was called and a high resolution timer can be used. It wraps around to 0 every about 49.71 days. */ static TTime getLocalTime(); - /** Same as getLocalTime, but prefers high resolution timers. - * Must call probe once in the beginning of the application before using, - * to ensure the correct settings are applied. - */ - static TTime getLocalTimeHR(); - /** Return the time in processor ticks. Use it for profile purpose. * If the performance time is not supported on this hardware, it returns 0. * \warning On a multiprocessor system, the value returned by each processor may diff --git a/code/nel/src/misc/time_nl.cpp b/code/nel/src/misc/time_nl.cpp index 065db103e..9237229d7 100644 --- a/code/nel/src/misc/time_nl.cpp +++ b/code/nel/src/misc/time_nl.cpp @@ -242,9 +242,28 @@ TTime CTime::getLocalTime () //else //{ // This is not affected by system time changes. But it cycles every 49 days. - return timeGetTime(); + // return timeGetTime(); // Only this was left active before it was commented. //} + /* + * The above is no longer relevant. + */ + + if (a_HaveQueryPerformance) + { + // On a (fast) 15MHz timer this rolls over after 7000 days. + // If my calculations are right. + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + counter.QuadPart *= (LONGLONG)1000L; + counter.QuadPart /= a_QueryPerformanceFrequency.QuadPart; + } + else + { + // Use default reliable low resolution timer. + return getLocalTime(); + } + #elif defined (NL_OS_UNIX) static bool initdone = false; @@ -301,30 +320,6 @@ TTime CTime::getLocalTime () #endif } -/// Same as above but prefer high resolution timer -TTime CTime::getLocalTimeHR() -{ -#ifdef NL_OS_WINDOWS - if (a_HaveQueryPerformance) - { - // On a (fast) 15MHz timer this rolls over after 7000 days. - // If my calculations are right. - LARGE_INTEGER counter; - QueryPerformanceCounter(&counter); - counter.QuadPart *= (LONGLONG)1000L; - counter.QuadPart /= a_QueryPerformanceFrequency.QuadPart; - } - else - { - // Use default reliable low resolution timer. - return getLocalTime(); - } -#else - // Other OS always use the best available high resolution timer. - return getLocalTime(); -#endif -} - /* Return the time in processor ticks. Use it for profile purpose. * If the performance time is not supported on this hardware, it returns 0. * \warning On a multiprocessor system, the value returned by each processor may From ebabe8ed7364f77ca8923cfb7b206a5198e78f4c Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 27 Jul 2012 22:51:38 +0200 Subject: [PATCH 6/9] Fixed: Missed something --- code/ryzom/client/src/time_client.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ryzom/client/src/time_client.h b/code/ryzom/client/src/time_client.h index 436cf1465..9243a908d 100644 --- a/code/ryzom/client/src/time_client.h +++ b/code/ryzom/client/src/time_client.h @@ -136,7 +136,7 @@ void updateSmoothedTime(); inline NLMISC::TTime ryzomGetLocalTime() { - return NLMISC::CTime::getLocalTimeHR(); + return NLMISC::CTime::getLocalTime(); } inline NLMISC::TTicks ryzomGetPerformanceTime() From d14bbaf331be0106142f8d616f0f6f9a84e4f58d Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 28 Jul 2012 01:32:48 +0200 Subject: [PATCH 7/9] Added: Implementation of timer tests for linux --- code/nel/src/misc/p_thread.cpp | 51 ++++++--- code/nel/src/misc/time_nl.cpp | 182 +++++++++++++++++++++++---------- 2 files changed, 168 insertions(+), 65 deletions(-) diff --git a/code/nel/src/misc/p_thread.cpp b/code/nel/src/misc/p_thread.cpp index 4c4fe1101..ab5530342 100644 --- a/code/nel/src/misc/p_thread.cpp +++ b/code/nel/src/misc/p_thread.cpp @@ -34,16 +34,16 @@ struct CPMainThread : public CPThread { 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."); if(pthread_setspecific(threadSpecificKey, this) != 0) 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."); } }; @@ -139,7 +139,7 @@ void CPThread::start() /* setting the size of the stack also */ ret = pthread_attr_setstacksize(&tattr, _StackSize); } - + bool detach_old_thread = false; pthread_t old_thread_handle; if (_State != ThreadStateNone) @@ -221,6 +221,9 @@ void CPThread::wait () bool CPThread::setCPUMask(uint64 cpuMask) { #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); if (res) @@ -228,9 +231,14 @@ bool CPThread::setCPUMask(uint64 cpuMask) nlwarning("pthread_setaffinity_np() returned %d", res); return false; } -#endif // __USE_GNU return true; + +#else // __USE_GNU + + return false; + +#endif // __USE_GNU } /* @@ -238,9 +246,12 @@ bool CPThread::setCPUMask(uint64 cpuMask) */ uint64 CPThread::getCPUMask() { - uint64 cpuMask = 1; - #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); if (res) @@ -248,9 +259,14 @@ uint64 CPThread::getCPUMask() nlwarning("pthread_getaffinity_np() returned %d", res); return 0; } -#endif // __USE_GNU return cpuMask; + +#else // __USE_GNU + + return 0; + +#endif // __USE_GNU } void CPThread::setPriority(TThreadPriority priority) @@ -311,9 +327,9 @@ IProcess *IProcess::getCurrentProcess () */ uint64 CPProcess::getCPUMask() { - uint64 cpuMask = 1; - #ifdef __USE_GNU + + uint64 cpuMask = 0; sint res = sched_getaffinity(getpid(), sizeof(uint64), (cpu_set_t*)&cpuMask); if (res) @@ -321,15 +337,21 @@ uint64 CPProcess::getCPUMask() nlwarning("sched_getaffinity() returned %d, errno = %d: %s", res, errno, strerror(errno)); return 0; } -#endif // __USE_GNU return cpuMask; + +#else // __USE_GNU + + return 0; + +#endif // __USE_GNU } /// set the CPU mask bool CPProcess::setCPUMask(uint64 cpuMask) { #ifdef __USE_GNU + sint res = sched_setaffinity(getpid(), sizeof(uint64), (const cpu_set_t*)&cpuMask); if (res) @@ -337,9 +359,14 @@ bool CPProcess::setCPUMask(uint64 cpuMask) nlwarning("sched_setaffinity() returned %d, errno = %d: %s", res, errno, strerror(errno)); return false; } -#endif // __USE_GNU return true; + +#else // __USE_GNU + + return false; + +#endif // __USE_GNU } diff --git a/code/nel/src/misc/time_nl.cpp b/code/nel/src/misc/time_nl.cpp index 9237229d7..304426e26 100644 --- a/code/nel/src/misc/time_nl.cpp +++ b/code/nel/src/misc/time_nl.cpp @@ -42,13 +42,63 @@ namespace { bool a_HaveQueryPerformance = false; LARGE_INTEGER a_QueryPerformanceFrequency; #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) { breakable { - #ifdef NL_OS_WINDOWS +#ifdef NL_OS_WINDOWS LARGE_INTEGER winPerfFreq; LARGE_INTEGER winPerfCount; DWORD lowResTime; @@ -78,21 +128,34 @@ void CTime::probeTimerInfo(CTime::CTimerInfo &result) { lowResTime = timeGetTime(); } - #else - // nldebug("Probe of timer info not implemented"); - result.IsHighPrecisionAvailable = false; - result.RequiresSingleCore = true; - break; - #endif +#else + + // Other platforms are awesome. Generic implementation for now. + TTime localTime = getLocalTime(); + result.IsHighPrecisionAvailable = true; + 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 threadMask = IThread::getCurrentThread()->getCPUMask(); - - #ifdef NL_OS_WINDOWS - - #else - TTicks timerFrequency = 0; - #endif +#ifdef NL_OS_WINDOWS + uint64 threadMask = IThread::getCurrentThread()->getCPUMask(); // broken on linux, don't expect it to work anywhere +#else + uint64 threadMask = cpuMask; +#endif 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. @@ -109,7 +172,12 @@ void CTime::probeTimerInfo(CTime::CTimerInfo &result) { 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 // Make sure the thread is rescheduled. SwitchToThread(); @@ -152,13 +220,53 @@ void CTime::probeTimerInfo(CTime::CTimerInfo &result) ++regular; 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 } currentBit <<= 1; } } +#ifdef NL_OS_WINDOWS IThread::getCurrentThread()->setCPUMask(threadMask); +#else + IProcess::getCurrentProcess()->setCPUMask(threadMask); +#endif 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); @@ -245,10 +353,10 @@ TTime CTime::getLocalTime () // return timeGetTime(); // Only this was left active before it was commented. //} - /* + /* * The above is no longer relevant. */ - + if (a_HaveQueryPerformance) { // On a (fast) 15MHz timer this rolls over after 7000 days. @@ -266,49 +374,17 @@ TTime CTime::getLocalTime () #elif defined (NL_OS_UNIX) - static bool initdone = false; - static bool isMonotonicClockSupported = false; - if ( ! initdone ) - { - -#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) -#if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK >= 0) +#ifdef NL_MONOTONIC_CLOCK - /* 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 ) + if (hasMonotonicClock()) { - struct timespec tv; + timespec tv; // This is not affected by system time changes. if ( clock_gettime( CLOCK_MONOTONIC, &tv ) != 0 ) nlerror ("Can't get clock time again"); return (TTime)tv.tv_sec * (TTime)1000 + (TTime)((tv.tv_nsec/*+500*/) / 1000000); } -#endif #endif // This is affected by system time changes. @@ -346,7 +422,7 @@ TTicks CTime::getPerformanceTime () return (hi << 32) | (lo & 0xffffffff); #elif defined(HAVE_X86) and !defined(NL_OS_MAC) 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)); return x; #else // HAVE_X86 From e029348d9aef9fe2b3c5466d7c0ef59dad62a284 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 28 Jul 2012 11:31:28 +0200 Subject: [PATCH 8/9] Fixed: Typos --- code/nel/src/misc/time_nl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/nel/src/misc/time_nl.cpp b/code/nel/src/misc/time_nl.cpp index 304426e26..dd43fd2f7 100644 --- a/code/nel/src/misc/time_nl.cpp +++ b/code/nel/src/misc/time_nl.cpp @@ -365,11 +365,12 @@ TTime CTime::getLocalTime () QueryPerformanceCounter(&counter); counter.QuadPart *= (LONGLONG)1000L; counter.QuadPart /= a_QueryPerformanceFrequency.QuadPart; + return counter.QuadPart; } else { // Use default reliable low resolution timer. - return getLocalTime(); + return timeGetTime(); } #elif defined (NL_OS_UNIX) From 1f5e08b20ff6befcf1958ef12746cd6db1bd9775 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 28 Jul 2012 12:40:36 +0200 Subject: [PATCH 9/9] Fixed: A strange loading crash with bad textures --- .../nel/src/3d/driver/direct3d/driver_direct3d_texture.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_texture.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_texture.cpp index b50709cd2..2cd16bf1c 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_texture.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_texture.cpp @@ -868,6 +868,13 @@ bool CDriverD3D::uploadTextureInternal (ITexture& tex, CRect& rect, uint8 destMi D3DFORMAT destFormat, D3DFORMAT srcFormat) { H_AUTO_D3D(CDriverD3D_uploadTextureInternal) + + if (rect.Width == 0 || rect.Height == 0) + { + nlwarning("Rectangle width or height cannot be 0"); + return false; + } + // The D3D texture CTextureDrvInfosD3D* d3dtext = getTextureD3D(tex);