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.
179 lines
5.7 KiB
C++
179 lines
5.7 KiB
C++
#pragma once
|
|
|
|
#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()
|
|
{
|
|
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;
|
|
|
|
CRITICAL_SECTION crit;
|
|
deque<WaitData> waitPool;
|
|
MMRESULT timer;
|
|
LARGE_INTEGER freq;
|
|
LARGE_INTEGER halfMsec;
|
|
};
|
|
|