zhangJW_cn 阅读(1065) 评论(5)
如下代码如果初看,很难以为其存在多处陷阱

#include "./GetTime.h"
#ifdef _XBOX360
# include "Console1Includes.h" // Developers of a certain platform will know what to do here.
#elif defined(_WIN32)
# include
static DWORD mProcMask;
static DWORD mSysMask;
static HANDLE mThread;
static LARGE_INTEGER yo = {};
#elif defined(_PS3)
# include "Console2Includes.h"
# include // GetTime.cpp
# include // GetTime.cpp
# include // GetTime.cpp
static uint64_t tps = 0;
#else
# include
# include
static time_t ts = 0;
static timeval tv;
#endif //_XBOX360

static RakNetTimeUS _GetTimeUS( void )
{
#if defined(_PS3)
if ( tps <= 0 ) tps = _PS3_GetTicksPerSecond();
#elif defined(_WIN32)
// Win32
if ( yo.QuadPart <= 0 )
{
# if !defined(_WIN32_WCE)
// Save the current process
HANDLE mProc = GetCurrentProcess();
// Get the current Affinity
# if _MSC_VER >= 1400 && defined(_M_X64)
GetProcessAffinityMask( mProc,(PDWORD_PTR)&mProcMask,(PDWORD_PTR)&mSysMask );
# else
GetProcessAffinityMask( mProc,&mProcMask,&mSysMask );
# endif //_MSC_VER >= 1400 && _M_X64
mThread = GetCurrentThread();
# endif // !defined(_WIN32_WCE)
QueryPerformanceFrequency( &yo );
}
#elif (defined(__GNUC__) || defined(__GCCXML__))
if ( ts <= 0 )
{
const time_t t = time( 0 );
struct tm &tm = *localtime( &t );
ts = t - ( tm.tm_hour * 60 + tm.tm_min ) * 60 - tm.tm_sec;
}
#endif //_PS3

#if defined(_PS3)
RakNetTimeUS curTime;
// Use the function to get elapsed ticks, this is a macro.
_PS3_GetElapsedTicks( curTime );
RakNetTimeUS quotient = curTime / tps;
RakNetTimeUS remainder = curTime % tps;
// Subtract from initialTime so the millisecond conversion does not underflow
return quotient * 1000000 + ( remainder * 1000000 / tps );
#elif defined(_WIN32)
static RakNetTimeUS lastQueryVal = 0;
static unsigned long lastTickCountVal = GetTickCount();

LARGE_INTEGER PerfVal;

# if !defined(_WIN32_WCE)
// Set affinity to the first core
SetThreadAffinityMask( mThread,1 );
# endif // !defined(_WIN32_WCE)

// Docs: On a multiprocessor computer, it should not matter which processor is called.
// However, you can get different results on different processors due to bugs in the basic input/output system (BIOS) or the hardware abstraction layer (HAL). To specify processor affinity for a thread, use the SetThreadAffinityMask function.
// Query the timer
QueryPerformanceCounter( &PerfVal );

# if !defined(_WIN32_WCE)
// Reset affinity
SetThreadAffinityMask( mThread,mProcMask );
# endif // !defined(_WIN32_WCE)

RakNetTimeUS quotient = PerfVal.QuadPart / yo.QuadPart;
RakNetTimeUS remainder = PerfVal.QuadPart % yo.QuadPart;
RakNetTimeUS curTime = quotient * 1000000 + ( remainder * 1000000 / yo.QuadPart );

# if !defined(_WIN32_WCE)
if ( lastQueryVal == 0 )
{
// First call
lastQueryVal = curTime;
return curTime;
}

// To workaround where the timer can sometimes jump forward by hours or days
unsigned long curTickCount = GetTickCount();
unsigned long elapsedTickCount = curTickCount - lastTickCountVal;
const RakNetTimeUS elapsedQueryVal = curTime - lastQueryVal;
if ( elapsedQueryVal/1000 > elapsedTickCount+100 )
{
curTime = lastQueryVal + elapsedTickCount * 1000;
}

lastTickCountVal = curTickCount;
lastQueryVal = curTime;
# endif //!_WIN32_WCE
return curTime;
#elif (defined(__GNUC__) || defined(__GCCXML__))
// GCC
gettimeofday( &tv,0 );
// Subtract from initialTime so the millisecond conversion does not underflow
return ( tv.tv_sec - ts ) * static_cast< RakNetTimeUS >( 1000000 ) + ( tv.tv_usec );
#endif //_PS3
}

即便定位到了该函数,如果因为其跨越多个平台,加上代码宏差分,很难想象其问题何在,如果再加上MS的知识库连接,嗯,这个问题怎么解决呢?难道换用timeGetTime?

错误1:SetThreadAffinityMask根本就是混淆视听的(或许是有意为之特设陷阱,布局巧妙也很厉害了)。
错误2:tv和ts多线程显然又要悲剧了(tv是全局变量,多线程恰巧在返回表达式计算的时机切换并改写)。
错误3:函数内的全局变量,多线程又会出问题(如果受到kb/274323的影响,估计很难正好想到该缺陷)。
缺陷4:初始化如果多线程同入,初始值获取无法保障(判断和取值中间可能有线程切换)—— 可以确保单线程初始化解决。

新的参考代码(暂不考虑kb/274323的影响,目前测试正常):

#include "./GetTime.h"
#ifdef _XBOX360
# include "Console1Includes.h" // Developers of a certain platform will know what to do here.
#elif defined(_WIN32)
# include
static DWORD hMaskProc;
static LARGE_INTEGER yo = {};
#elif defined(_PS3)
# include "Console2Includes.h"
# include // GetTime.cpp
# include // GetTime.cpp
# include // GetTime.cpp
static uint64_t tps = 0;
#else
# include
# include
static time_t ts = 0;
#endif //_XBOX360

static RakNetTimeUS _GetTimeUS( void )
{
#if defined( _PS3 )
if ( tps <= 0 ) tps = _PS3_GetTicksPerSecond();
#elif defined( _WIN32 )
// Win32
if ( yo.QuadPart <= 0 )
{
# if !defined( _WIN32_WCE )
// Save the current process
DWORD hMaskSys;
HANDLE hProc = GetCurrentProcess();
// Get the current Affinity
# if _MSC_VER >= 1400 && defined(_M_X64)
GetProcessAffinityMask( hProc,(PDWORD_PTR)&hMaskProc,(PDWORD_PTR)&hMaskSys );
# else
GetProcessAffinityMask( hProc,&hMaskProc,&hMaskSys );
# endif //_MSC_VER >= 1400 && _M_X64
HANDLE hThread = GetCurrentThread();
SetThreadAffinityMask( hThread,1 );
# endif // !defined(_WIN32_WCE)
QueryPerformanceFrequency( &yo );
# if !defined( _WIN32_WCE )
SetThreadAffinityMask( hThread,hMaskProc );
# endif // !defined( _WIN32_WCE )
}
#elif ( defined(__GNUC__) || defined(__GCCXML__) )
if ( ts <= 0 )
{
const time_t t = time( 0 );
struct tm &tm = *localtime( &t );
ts = t - ( tm.tm_hour * 60 + tm.tm_min ) * 60 - tm.tm_sec;
}
#endif //_PS3

#if defined( _PS3 )
RakNetTimeUS curTime;
// Use the function to get elapsed ticks, this is a macro.
_PS3_GetElapsedTicks( curTime );
RakNetTimeUS quotient = curTime / tps;
RakNetTimeUS remainder = curTime % tps;
// Subtract from initialTime so the millisecond conversion does not underflow
return quotient * 1000000 + ( remainder * 1000000 / tps );
#elif defined( _WIN32 )

# if !defined( _WIN32_WCE )
HANDLE hThread = GetCurrentThread();
// Set affinity to the first core
SetThreadAffinityMask( hThread,1 );
# endif // !defined( _WIN32_WCE )

// Docs: On a multiprocessor computer, it should not matter which processor is called.
// However, you can get different results on different processors due to bugs in the basic input/output system (BIOS) or the hardware abstraction layer (HAL). To specify processor affinity for a thread, use the SetThreadAffinityMask function.
// Query the timer
LARGE_INTEGER PerfVal;
QueryPerformanceCounter( &PerfVal );

# if !defined( _WIN32_WCE )
// Reset affinity
SetThreadAffinityMask( hThread,hMaskProc );
# endif // !defined( _WIN32_WCE )

const RakNetTimeUS quotient = PerfVal.QuadPart / yo.QuadPart;
const RakNetTimeUS remainder = PerfVal.QuadPart % yo.QuadPart;
return quotient * 1000000 + ( remainder * 1000000 / yo.QuadPart );
#elif ( defined(__GNUC__) || defined(__GCCXML__) )
// GCC
static timeval tv;
gettimeofday( &tv,0 );
// Subtract from initialTime so the millisecond conversion does not underflow
return ( tv.tv_sec - ts ) * static_cast< RakNetTimeUS >( 1000000 ) + ( tv.tv_usec );
#endif //_PS3
}

哪位同仁如发现仍有缺陷,或有更优解法(低CPU消耗,低内存开销),欢迎指出和感谢分享。—— 注:由使用者保证单线程初始化即可。

评论列表
玻璃小屋
re: 真实的陷阱2 — 多线程案例

牛的一B。。。。看都看不懂。。。
我的麦包包
re: 真实的陷阱2 — 多线程案例
这个太难了吧
brent
re: 真实的陷阱2 — 多线程案例
这代码没用,完全是垃圾,没法接手,没法出错。

写代码的人,严重浪费脑细胞,养生都晚了。
aab
这代码,看着是有点累!

发表评论
切换编辑模式