--- dispenso/detail/epoch_waiter.h.orig
+++ dispenso/detail/epoch_waiter.h
@@ -192,7 +192,12 @@ class EpochWaiter {
       return current;
     }
 
-    int msWait = std::max(1, static_cast<int>(relTimeUs / 1000));
+    // WaitOnAddress takes DWORD (unsigned long) milliseconds.
+    // Clamp to the largest finite timeout to avoid overflow or
+    // accidentally passing INFINITE (0xFFFFFFFF = wait forever).
+    uint64_t msVal = std::max(uint64_t{1}, relTimeUs / 1000);
+    unsigned long msWait = msVal >= kInfiniteWin ? static_cast<unsigned long>(kInfiniteWin - 1)
+                                                 : static_cast<unsigned long>(msVal);
     // Allow spurious wake
     if ((current = epoch_.load(std::memory_order_acquire)) == expectedEpoch) {
       WaitOnAddress(&epoch_, &current, sizeof(uint32_t), msWait);
@@ -250,7 +255,7 @@ class EpochWaiter {
     return epoch_.load(std::memory_order_acquire);
   }
 
-  uint32_t waitFor(uint32_t expectedEpoch, uint32_t relTimeUs) const {
+  uint32_t waitFor(uint32_t expectedEpoch, uint64_t relTimeUs) const {
     uint32_t current;
     if ((current = epoch_.load(std::memory_order_acquire)) != expectedEpoch) {
       return current;
@@ -259,7 +264,7 @@ class EpochWaiter {
     // Allow spurious wake
     if ((current = epoch_.load(std::memory_order_acquire)) == expectedEpoch) {
       std::unique_lock<std::mutex> lk(mtx_);
-      cv_.wait_for(lk, std::chrono::microseconds(relTimeUs));
+      cv_.wait_for(lk, std::chrono::microseconds(static_cast<int64_t>(relTimeUs)));
     } else {
       return current;
     }
