You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mitlib.pub/MITLIB/Include/AccurateTimer.h

181 lines
5.7 KiB
C

#ifndef __ACCURATE_TIMER_H__
#define __ACCURATE_TIMER_H__
#pragma warning ( disable : 4786 )
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <time.h>
#include <deque>
#include <process.h>
#include "mmsystem.h"
using namespace std;
//-------------------------------------------------------------------
// this timer class was designed to give high precision < 2% average
// error and it was designed to work best with multi-threaded clients
// waiting on the single instance of this timer
//
class AccurateTimer
{
public:
static AccurateTimer* Instance()
{
return &instance;
};
virtual ~AccurateTimer()
{
timeKillEvent(timer), timer = 0;
DeleteCriticalSection(&crit);
}
///////////////////////////////////////////////////////////////
// Function name : Wait
// Description : Timer wait method
// :
// Return type : void :
// Argument : int timeout :
///////////////////////////////////////////////////////////////
void Wait(int timeout)
{
//#define TIMING_DEBUG
#ifdef TIMING_DEBUG
LARGE_INTEGER s[2];
QueryPerformanceCounter(&s[0]); // timing starts
#endif
if ( timeout > 0 ) // anything to wait on ?
{
HANDLE tHandle = 0;
HANDLE pHandle = GetCurrentProcess();
DuplicateHandle(pHandle, GetCurrentThread(), pHandle,
&tHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
HANDLE WaitEvent;
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
ResetEvent(WaitEvent);
CTrigger Trg;
Trg.InsertTrigger(WaitEvent);
// wrap pool data access
EnterCriticalSection(&AccurateTimer::Instance()->crit);
waitPool.push_back(WaitData(tHandle, WaitEvent, timeout));
LeaveCriticalSection(&AccurateTimer::Instance()->crit);
//SuspendThread(tHandle); // do the wait
Trg.Wait();
CloseHandle(tHandle);
}
#ifdef TIMING_DEBUG
QueryPerformanceCounter(&s[1]); // wake up call and timing ends
double diff = ((double)s[1].QuadPart - (double)s[0].QuadPart)/(double)freq.QuadPart;
double percent = /*Absolute*/(diff-timeout)*100.0/(timeout?timeout:1);
TRACE("intended to sleep for: %3ld. slept for: %8.4f. error = %8.4f\n",
timeout, diff, percent);
error += percent;
++count;
#endif
}
inline double Absolute(double val) { return val > 0 ? val : -val; }
inline LARGE_INTEGER GetFrequency() { return freq; };
///////////////////////////////////////////////////////////////
// Function name : CALLBACK TimerFunc
// Description : Media callback timer method
// :
// Return type : static void :
// Argument : UINT uID :
// Argument : UINT uMsg :
// Argument : DWORD dwUser :
// Argument : DWORD dw1 :
// Argument : DWORD dw2 :
///////////////////////////////////////////////////////////////
static void CALLBACK TimerFunc(UINT /*uID*/, UINT /*uMsg*/, DWORD_PTR dwUser, DWORD_PTR /*dw1*/, DWORD_PTR /*dw2*/)
{
static LARGE_INTEGER s[2];
static bool init = true;
if ( !init )
{
init = true;
HANDLE tHandle = 0;
HANDLE pHandle = GetCurrentProcess();
DuplicateHandle(pHandle, GetCurrentThread(), pHandle,
&tHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
SetThreadPriority(tHandle, THREAD_PRIORITY_TIME_CRITICAL);
HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, _getpid());
if (!SetPriorityClass(h, REALTIME_PRIORITY_CLASS))
{
DWORD err = GetLastError();
TRACE("SetPriorityClass Error: %d.\n", err);
}
CloseHandle(h);
CloseHandle(tHandle);
}
AccurateTimer* pThis = reinterpret_cast<AccurateTimer*>(dwUser);
QueryPerformanceCounter(&s[1]);
//double diff = (((double)s[1].QuadPart - (double)s[0].QuadPart)/(double)pThis->freq.QuadPart);
__int64 now = s[1].QuadPart/*+pThis->halfMsec.QuadPart*/;
EnterCriticalSection(&pThis->crit);
deque<WaitData>::iterator it = pThis->waitPool.begin();
while ( it != pThis->waitPool.end() )
{
if ( now >= it->timeout.QuadPart )
{
//ResumeThread(it->h);
SetEvent(it->eventHandle);
it = pThis->waitPool.erase(it);
continue;
}
++it;
}
LeaveCriticalSection(&pThis->crit);
s[0] = s[1];
}
volatile static double error;
volatile static int count;
private:
AccurateTimer()
{
error = 0;
count = 0;
QueryPerformanceFrequency(&freq);
halfMsec.QuadPart = freq.QuadPart / 2000;
freq.QuadPart /= 1000; // convert to msecs
InitializeCriticalSection(&crit);
timer = timeSetEvent(UINT(1), UINT(0), TimerFunc, (DWORD_PTR)this, (UINT)TIME_PERIODIC);
}
struct WaitData
{
WaitData(HANDLE _h, HANDLE _evtH, int _t) : h(_h), eventHandle(_evtH)
{
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
timeout.QuadPart = _t * AccurateTimer::Instance()->freq.QuadPart + now.QuadPart;
};
HANDLE h;
HANDLE eventHandle;
LARGE_INTEGER timeout;
};
friend struct WaitData;
static AccurateTimer instance;
CRITICAL_SECTION crit;
deque<WaitData> waitPool;
MMRESULT timer;
LARGE_INTEGER freq;
LARGE_INTEGER halfMsec;
};
#endif // __ACCURATE_TIMER_H__