//----------------------------------------------------------------------------- // // // File: qwiktime.cpp // // Description: Implementation of CAQQuickTime class // // Author: Mike Swafford (MikeSwa) // // History: // 7/9/98 - MikeSwa Created // // Copyright (C) 1998 Microsoft Corporation // //----------------------------------------------------------------------------- #include "aqprecmp.h" #include "qwiktime.h" //The basic premise is to use GetTickCount to get a reasonable accurate //time information in a DWORD. GetTickCount is only valid for 50 days, and //has an accuracy of 1 ms. 50 days is just not long enough. //MSchofie was kind enough to prepare the following table: //Bit shift Resolution (seconds) Uptime(years) //3 0.008 1.088824217 //4 0.016 2.177648434 //5 0.032 4.355296868 //6 0.064 8.710593736 //7 0.128 17.42118747 //8 0.256 34.84237495 //9 0.512 69.68474989 //10 1.024 139.3694998 //11 2.048 278.7389996 //12 4.096 557.4779991 //13 8.192 1114.955998 //14 16.384 2229.911997 //15 32.768 4459.823993 //16 65.536 8919.647986 //The initial implementation used 16 bits, which had an error of about 66 //seconds, which drove our poor Y2K tester nuts. 8 bits (34 years uptime) //seems much more reasonable. //The system tick is shifted right INTERNAL_TIME_TICK_BITS and stored in //the least significant INTERNAL_TIME_TICK_BITS bits of the internal time. //The upper 32-INTERNAL_TIME_TICK_BITS bits //is used to count the number of times the count rolls over #define INTERNAL_TIME_TICK_BITS 8 #define INTERNAL_TIME_TICK_MASK 0x00FFFFFF //Number where tick count has wrapped const LONGLONG INTERNAL_TIME_TICK_ROLLOVER_COUNT = (1 << (32-INTERNAL_TIME_TICK_BITS)); const LONGLONG TICKS_PER_INTERNAL_TIME = (1 << INTERNAL_TIME_TICK_BITS); //conversion constants //There are 10^6 milliseconds per nanosecond (FILETIME is in 100 nanosecond counts) const LONGLONG FILETIMES_PER_TICK = 10000; const LONGLONG FILETIMES_PER_INTERNAL_TIME = (FILETIMES_PER_TICK*TICKS_PER_INTERNAL_TIME); const LONGLONG FILETIMES_PER_MINUTE = (FILETIMES_PER_TICK*1000*60); //---[ CAQQuickTime::CAQQuickTime ]-------------------------------------------- // // // Description: // Default Constructor for CAQQuickTime. Will call GetSystemTime once to // get start up time... GetTickCount is used for all other calls. // Parameters: // - // Returns: // // History: // 7/9/98 - MikeSwa Created // //----------------------------------------------------------------------------- CAQQuickTime::CAQQuickTime() { DWORD dwTickCount = GetTickCount(); LARGE_INTEGER *pLargeIntSystemStart = (LARGE_INTEGER *) &m_ftSystemStart; m_dwSignature = QUICK_TIME_SIG; //Get internal time and start file time GetSystemTimeAsFileTime(&m_ftSystemStart); //convert tick count to internal time m_dwLastInternalTime = dwTickCount >> INTERNAL_TIME_TICK_BITS; //adjust start time so that it is the time when the tick count was zero pLargeIntSystemStart->QuadPart -= (LONGLONG) dwTickCount * FILETIMES_PER_TICK; //Some asserts to validate constants _ASSERT(!(INTERNAL_TIME_TICK_ROLLOVER_COUNT & INTERNAL_TIME_TICK_MASK)); _ASSERT((INTERNAL_TIME_TICK_ROLLOVER_COUNT >> 1) & INTERNAL_TIME_TICK_MASK); } //---[ CAQQuickTime::dwGetInternalTime ]--------------------------------------- // // // Description: // Gets internal time using GetTickCount... and makes sure that when // GetTickCount wraps, the correct time is returned. // Parameters: // - // Returns: // DWORD internal time // History: // 7/9/98 - MikeSwa Created // //----------------------------------------------------------------------------- DWORD CAQQuickTime::dwGetInternalTime() { DWORD dwCurrentTick = GetTickCount(); DWORD dwLastInternalTime = m_dwLastInternalTime; DWORD dwCurrentInternalTime; DWORD dwCheck; dwCurrentInternalTime = dwCurrentTick >> INTERNAL_TIME_TICK_BITS; _ASSERT(dwCurrentInternalTime == (INTERNAL_TIME_TICK_MASK & dwCurrentInternalTime)); //see if rolled over our tick count while ((dwLastInternalTime & INTERNAL_TIME_TICK_MASK) > dwCurrentInternalTime) { dwLastInternalTime = m_dwLastInternalTime; //it is possible that we have rolled over the tick count //first make sure it is not just a thread-timing issue dwCurrentTick = GetTickCount(); dwCurrentInternalTime = dwCurrentTick >> INTERNAL_TIME_TICK_BITS; if ((dwLastInternalTime & INTERNAL_TIME_TICK_MASK) > dwCurrentInternalTime) { dwCurrentInternalTime |= (~INTERNAL_TIME_TICK_MASK & dwLastInternalTime); dwCurrentInternalTime += INTERNAL_TIME_TICK_ROLLOVER_COUNT; //attempt interlocked exchange to update internal last internal time dwCheck = (DWORD) InterlockedCompareExchange((PLONG) &m_dwLastInternalTime, (LONG) dwCurrentInternalTime, (LONG) dwLastInternalTime); if (dwCheck == dwLastInternalTime) //exchange worked goto Exit; } } _ASSERT(dwCurrentInternalTime == (INTERNAL_TIME_TICK_MASK & dwCurrentInternalTime)); dwCurrentInternalTime |= (~INTERNAL_TIME_TICK_MASK & m_dwLastInternalTime); Exit: return dwCurrentInternalTime; } //---[ CAQQuickTime::GetExpireTime ]------------------------------------------- // // // Description: // Get the expriation time for cMinutesExpireTime from now. // Parameters: // IN cMinutesExpireTime # of minutes in future to set time // IN OUT pftExpireTime Filetime to store new expire time // IN OUT pdwExpireContext If non-zero will use the same tick count // as previous calls (saves call to GetTickCount) // Returns: // // History: // 7/10/98 - MikeSwa Created // //----------------------------------------------------------------------------- void CAQQuickTime::GetExpireTime( IN DWORD cMinutesExpireTime, IN OUT FILETIME *pftExpireTime, IN OUT DWORD *pdwExpireContext) { TraceFunctEnterEx((LPARAM) this, "CAQQuickTime::GetExpireTime"); _ASSERT(pftExpireTime); DWORD dwInternalTime = 0; LARGE_INTEGER *pLargeIntTime = (LARGE_INTEGER *) pftExpireTime; if (pdwExpireContext) dwInternalTime = *pdwExpireContext; if (!dwInternalTime) { dwInternalTime = dwGetInternalTime(); //save internal time as context if (pdwExpireContext) *pdwExpireContext = dwInternalTime; } memcpy(pftExpireTime, &m_ftSystemStart, sizeof(FILETIME)); //set to current time pLargeIntTime->QuadPart += (LONGLONG) dwInternalTime * FILETIMES_PER_INTERNAL_TIME; //set cMinutesExpireTime into the future pLargeIntTime->QuadPart += (LONGLONG) cMinutesExpireTime * FILETIMES_PER_MINUTE; DebugTrace((LPARAM) this, "INFO: Creating file time for %d minutes of 0x%08X %08X", cMinutesExpireTime, pLargeIntTime->HighPart, pLargeIntTime->LowPart); TraceFunctLeave(); } //---[ CAQQuickTime::GetExpireTime ]------------------------------------------- // // // Description: // Get the expriation time for cMinutesExpireTime from ftStartTime // Parameters: // IN cMinutesExpireTime # of minutes in future to set time // IN OUT pftExpireTime Filetime to store new expire time // IN ftStartTime Time to add expire minutes to // Returns: // // History: // 5/16/2001 - dbraun created from GetExpireTime above // //----------------------------------------------------------------------------- void CAQQuickTime::GetExpireTime( IN FILETIME ftStartTime, IN DWORD cMinutesExpireTime, IN OUT FILETIME *pftExpireTime) { TraceFunctEnterEx((LPARAM) this, "CAQQuickTime::GetExpireTime"); _ASSERT(pftExpireTime); LARGE_INTEGER *pLargeIntTime = (LARGE_INTEGER *) pftExpireTime; // Set to start time memcpy(pftExpireTime, &ftStartTime, sizeof(FILETIME)); //set cMinutesExpireTime into the future pLargeIntTime->QuadPart += (LONGLONG) cMinutesExpireTime * FILETIMES_PER_MINUTE; DebugTrace((LPARAM) this, "INFO: Creating file time for %d minutes of 0x%08X %08X", cMinutesExpireTime, pLargeIntTime->HighPart, pLargeIntTime->LowPart); TraceFunctLeave(); } //---[ CAQQuickTime::fInPast ]------------------------------------------------- // // // Description: // Determines if a given file time has already happened // Parameters: // IN pftExpireTime FILETIME with expiration // IN OUT pdwExpireContext If non-zero will use the same tick count // as previous calls (saves call to GetTickCount) // Returns: // TRUE if expire time is in the past // FALSE if expire time is in the future // History: // 7/11/98 - MikeSwa Created // Note: // You should NOT use the same context used to get the filetime, because // it will always return FALSE // //----------------------------------------------------------------------------- BOOL CAQQuickTime::fInPast(IN FILETIME *pftExpireTime, IN OUT DWORD *pdwExpireContext) { _ASSERT(pftExpireTime); DWORD dwInternalTime = 0; FILETIME ftCurrentTime = m_ftSystemStart; LARGE_INTEGER *pLargeIntCurrentTime = (LARGE_INTEGER *) &ftCurrentTime; LARGE_INTEGER *pLargeIntExpireTime = (LARGE_INTEGER *) pftExpireTime; BOOL fInPast = FALSE; if (pdwExpireContext) dwInternalTime = *pdwExpireContext; if (!dwInternalTime) { dwInternalTime = dwGetInternalTime(); //save internal time as context if (pdwExpireContext) *pdwExpireContext = dwInternalTime; } //Get current time pLargeIntCurrentTime->QuadPart += (LONGLONG) dwInternalTime * FILETIMES_PER_INTERNAL_TIME; if (pLargeIntCurrentTime->QuadPart > pLargeIntExpireTime->QuadPart) fInPast = TRUE; return fInPast; }