Skip to content

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 migration
  • srchybrid/ClientCredits.cpp
  • srchybrid/UploadQueue.cpp
  • 40+ additional files with ::GetTickCount() calls

Acceptance Criteria

  • [ ] No ::GetTickCount() calls remain in the codebase
  • [ ] CTimeTick / TimeTick.h deleted
  • [ ] SetTimer for background upload tick replaced with thread-pool timer or Asio steady_timer
  • [ ] Elapsed-time comparisons use typed duration objects, not raw DWORD subtraction
  • [ ] 49-day wrap scenario does not produce incorrect elapsed times