Skip to content

Instantly share code, notes, and snippets.

@Gnomorian
Created July 31, 2021 12:54
Show Gist options
  • Save Gnomorian/b62ce807e53a2764c420a3ffc09a4ba5 to your computer and use it in GitHub Desktop.
Save Gnomorian/b62ce807e53a2764c420a3ffc09a4ba5 to your computer and use it in GitHub Desktop.
Test timer resolution on windows using TimerQueues. the regular StartTimer function has a resoltion of 15ms, this can go down to 1ms it appears
#include <Windows.h>
#include <stdexcept>
#include <string>
#include <memory>
#include <vector>
#include <chrono>
#include <thread>
#include <iostream>
class TimerQueue
{
HANDLE _queue{ getQueueHandle() };
public:
constexpr TimerQueue() = default;
virtual ~TimerQueue()
{
deleteQueueHandle(_queue);
}
HANDLE beginOneShotTimer(int period, WAITORTIMERCALLBACK callback, PVOID param)
{
return createOneShotTimerForQueue(_queue, callback, param, period);
}
HANDLE beginContinuousTimer(int period, WAITORTIMERCALLBACK callback, PVOID param)
{
return createContinuousTimerForQueue(_queue, callback, param, period);
}
void endTimer(HANDLE timerHandle)
{
deleteTimer(_queue, timerHandle);
}
protected:
HANDLE getQueueHandle()
{
const auto handle{ CreateTimerQueue() };
if (handle == nullptr)
{
const auto result{ GetLastError() };
throw std::runtime_error("CreateTimerQueue error: " + std::to_string(result));
}
return handle;
}
void deleteQueueHandle(HANDLE handle)
{
const auto WaitForExecutingTimerCompletion{ INVALID_HANDLE_VALUE };
(void)DeleteTimerQueueEx(handle, WaitForExecutingTimerCompletion);
}
HANDLE createContinuousTimerForQueue(HANDLE queue, WAITORTIMERCALLBACK callback, PVOID param, DWORD period)
{
HANDLE newTimer{};
if (!CreateTimerQueueTimer(&newTimer, queue, callback, param, 0, period, WT_EXECUTELONGFUNCTION))
{
const auto result{ GetLastError() };
throw std::runtime_error("CreateTimerQueueTimer error: " + std::to_string(result));
}
return newTimer;
}
HANDLE createOneShotTimerForQueue(HANDLE queue, WAITORTIMERCALLBACK callback, PVOID param, DWORD period)
{
HANDLE newTimer{};
if (!CreateTimerQueueTimer(&newTimer, queue, callback, param, 0, period, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION))
{
const auto result{ GetLastError() };
throw std::runtime_error("CreateTimerQueueTimer error: " + std::to_string(result));
}
return newTimer;
}
void deleteTimer(HANDLE queue, HANDLE timer)
{
const auto WaitForExecutingTimerCompletion{ INVALID_HANDLE_VALUE };
(void)DeleteTimerQueueTimer(queue, timer, WaitForExecutingTimerCompletion);
}
};
int main()
{
auto timerCallback = [](PVOID param, BOOLEAN wait)
{
auto events{ static_cast<std::vector<std::chrono::high_resolution_clock::time_point>*>(param) };
events->push_back(std::chrono::high_resolution_clock::now());
};
std::vector<std::chrono::high_resolution_clock::time_point> events;
TimerQueue queue;
auto start{ std::chrono::high_resolution_clock::now() };
constexpr int period{ 1 };
constexpr std::chrono::seconds samplePeriod{ 1 };
auto timerHandle{ queue.beginContinuousTimer(period, timerCallback, &events) };
std::this_thread::sleep_for(samplePeriod);
queue.endTimer(timerHandle);
auto end{ std::chrono::high_resolution_clock::now() };
std::cout << events.size() << " timers fired at " << period << "ms resolution over " << samplePeriod.count() << " seconds";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment