Replace GetTickCount / SetTimer with type-safe monotonic clock — std::chrono or Boost
Classification¶
Abandoned by operator decision on 2026-05-21. This historical record preserves the old Boost/POCO analysis only as provenance; do not promote it without a new active item.
Summary¶
GetTickCount() wraps at 2^32 milliseconds (~49.7 days). Code that computes
elapsed time with subtraction (curTick - startTick) silently produces wrong
results every 49 days. Additionally, CTimeTick in TimeTick.h manually wraps
LARGE_INTEGER performance counter logic that already exists in both the C++17
standard library and Boost.
Two equivalent paths are documented. Both fix the 49-day wrap. The main difference is the timer-callback replacement, which depends on whether REF-008 adopts Boost.Asio or Windows IOCP.
Affected Files¶
| File | Line | Issue |
|---|---|---|
srchybrid/ClientCredits.cpp |
51 | m_dwSecureWaitTime = ::GetTickCount(); — 49-day wrap |
srchybrid/UploadQueue.cpp |
74 | m_dwRemovedClientByScore(::GetTickCount()) |
srchybrid/UploadQueue.cpp |
85 | h_timer = ::SetTimer(NULL, 0, SEC2MS(1)/10, UploadTimer) |
srchybrid/UploadQueue.cpp |
112 | const DWORD curTick = ::GetTickCount(); |
srchybrid/TimeTick.h |
11–35 | Custom CTimeTick wrapping LARGE_INTEGER |
| 40+ other files | — | Various ::GetTickCount() usages |
Option A — C++17 std::chrono (no Boost dependency)¶
Preferred if Boost is not adopted. Zero new dependencies.
#include <chrono>
// Replace GetTickCount() — no 49-day wrap, type-safe
using Clock = std::chrono::steady_clock;
using Ms = std::chrono::milliseconds;
auto startTime = Clock::now();
auto elapsedMs = std::chrono::duration_cast<Ms>(Clock::now() - startTime).count();
// Duration arithmetic — unit errors caught at compile time
auto timeout = std::chrono::seconds(30);
if (Clock::now() - startTime > timeout) { /* expired */ }
// Replace CTimeTick — delete TimeTick.h, use:
auto t0 = Clock::now();
// ...
auto wallNs = std::chrono::duration_cast<std::chrono::nanoseconds>(
Clock::now() - t0).count();
Timer callback replacement without Boost.Asio:
Use the Win32 thread-pool timer API (Vista+, always available on Win10):
// Replace SetTimer(NULL, 0, interval, UploadTimer) with:
PTP_TIMER pTimer = CreateThreadpoolTimer(
[](PTP_CALLBACK_INSTANCE, PVOID, PTP_TIMER) { UploadTimerTick(); },
nullptr, nullptr);
ULARGE_INTEGER dueTime;
dueTime.QuadPart = (ULONGLONG)(-(LONGLONG)intervalMs * 10000);
FILETIME ft = { dueTime.LowPart, dueTime.HighPart };
SetThreadpoolTimer(pTimer, &ft, intervalMs, 0);
// Cleanup:
SetThreadpoolTimer(pTimer, nullptr, 0, 0);
WaitForThreadpoolTimerCallbacks(pTimer, TRUE);
CloseThreadpoolTimer(pTimer);
Option B — Boost.Chrono + Boost.Asio timers¶
Requires Boost (see REF-008 Option A).
#include <boost/chrono.hpp>
#include <boost/timer/timer.hpp>
#include <boost/asio/steady_timer.hpp>
// Replace GetTickCount()
auto now = boost::chrono::steady_clock::now();
auto elapsed_ms = boost::chrono::duration_cast<boost::chrono::milliseconds>(
boost::chrono::steady_clock::now() - startTime).count();
// Replace CTimeTick
boost::timer::cpu_timer t;
t.start();
auto wallNs = t.elapsed().wall;
// Replace SetTimer() — integrates with io_context from REF-008 Option A
boost::asio::steady_timer uploadTimer(io_ctx,
boost::chrono::milliseconds(SEC2MS(1) / 10));
uploadTimer.async_wait([](boost::system::error_code ec) {
if (!ec) UploadTimerTick();
});
Transition Helper (both options)¶
Define a compatibility shim during migration to minimize call-site churn:
// TickMs.h — temporary shim, remove once all sites migrated
inline uint64_t TickMs() {
using namespace std::chrono;
return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
}
Replace ::GetTickCount() → TickMs() across 40+ files in a single mechanical
commit, then remove the shim once the duration arithmetic is modernized.
Recommendation¶
Use Option A (std::chrono + Win32 thread-pool timer) regardless of the
Boost decision. std::chrono::steady_clock fixes the 49-day wrap with zero
dependencies. The Win32 thread-pool timer is cleaner than SetTimer for
background work.
Files¶
srchybrid/TimeTick.h/TimeTick.cpp— delete after migrationsrchybrid/ClientCredits.cppsrchybrid/UploadQueue.cpp- 40+ additional files with
::GetTickCount()calls
Acceptance Criteria¶
- [ ] No
::GetTickCount()calls remain in the codebase - [ ]
CTimeTick/TimeTick.hdeleted - [ ]
SetTimerfor background upload tick replaced with thread-pool timer or Asio steady_timer - [ ] Elapsed-time comparisons use typed duration objects, not raw
DWORDsubtraction - [ ] 49-day wrap scenario does not produce incorrect elapsed times