#pragma once #include #ifdef PIO_UNIT_TESTING #include #endif // Monotonic uptime clock, injectable so tests can drive a virtual timebase instead of sleeping. // Uptime only; see gps/RTC.h for wall-clock. Not named Time.h: -Isrc would shadow C's . namespace Time { #ifdef PIO_UNIT_TESTING // Test-only virtual clock; OFF by default so suites relying on real time are unaffected. Atomic so // a suite can step the clock from one thread while others read it - the concurrent-reader cases in // test_uptime_clock/ do exactly that. inline std::atomic testNowMs{0}; inline std::atomic useTestClock{false}; using MonotonicPublishHook = void (*)(); inline void setTestMillis(uint32_t ms) { testNowMs.store(ms, std::memory_order_relaxed); useTestClock.store(true, std::memory_order_relaxed); } inline void advanceTestMillis(uint32_t deltaMs) { testNowMs.fetch_add(deltaMs, std::memory_order_relaxed); useTestClock.store(true, std::memory_order_relaxed); } // Restore real-clock behaviour (call in test tearDown if a suite mixes real and fake time). inline void useRealClock() { useTestClock.store(false, std::memory_order_relaxed); testNowMs.store(0, std::memory_order_relaxed); } // Zero the published wrap carry. Suites that assert absolute uptime values call this in setUp(): // a previous case that moved the test clock backwards left a counted wrap behind. void resetMonotonicForTests(); void setMonotonicPublishHookForTests(MonotonicPublishHook hook); #endif /// Milliseconds since boot, 32-bit (wraps ~49.7 days). Drop-in for millis(). For "has this interval /// elapsed / deadline arrived" use Throttle (isWithinTimespanMs / hasElapsed / deadlinePassed), /// which is wrap-correct with no carry state at all. uint32_t getMillis(); /// Milliseconds since boot as a monotonic 64-bit count. /// /// A pure read: it derives its answer from a complete snapshot published by serviceMonotonic() /// plus the unsigned elapsed time since that snapshot, which is exact across the wrap. A reader /// that preempts publication uses the previous snapshot. If publication completes during a copy, /// the reader retries; it never waits for a publish in progress. /// /// Not intended for ISR call sites because lock-free std::atomic operations are not guaranteed by /// every supported toolchain. ISRs use getMillis(); the publication protocol itself never waits. uint64_t getMillisMonotonic(); /// Whole seconds since boot, derived from getMillisMonotonic() (~136 years of range). This is /// the unit to store when an instant must be dated before the wall clock is trustworthy. uint32_t getUptimeSecs(); /// Advances the published wrap carry. THE ONLY WRITER - call it from the main loop and nowhere /// else. Two concurrent callers could count one wrap twice, jumping every uptime and wall-clock /// reading ~49.7 days forward for the rest of the boot. /// /// Must run at least once per ~49.7-day wrap window; the main loop calls it every iteration. void serviceMonotonic(); } // namespace Time