/* * * Copyright (c) 2020 Project CHIP Authors * Copyright (c) 2018 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file * This is an internal header file that defines the interface to a platform-supplied * function for retrieving the current system time. */ #pragma once // Include configuration headers #include // Include dependent headers #include #include #include #if CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_POSIX_SOCKETS #include #endif // CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_POSIX_SOCKETS #if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_SOCKETS #include #endif #include #include #if CHIP_DEVICE_LAYER_TARGET_DARWIN || CHIP_DEVICE_LAYER_TARGET_LINUX #define CHIP_DEVICE_LAYER_USE_ATOMICS_FOR_CLOCK 1 #endif // CHIP_DEVICE_LAYER_TARGET_DARWIN || CHIP_DEVICE_LAYER_TARGET_LINUX #ifndef CHIP_DEVICE_LAYER_USE_ATOMICS_FOR_CLOCK #define CHIP_DEVICE_LAYER_USE_ATOMICS_FOR_CLOCK 0 #endif namespace chip { namespace System { namespace Clock { /* * We use `std::chrono::duration` for clock types to provide type safety. But unlike the predefined std types * (`std::chrono::milliseconds` et al), CHIP uses unsigned base types, and types are explicity sized, with * smaller-size types available for members and arguments where appropriate. * * Most conversions are handled by the types transparently. To convert with possible loss of information, use * `std::chrono::duration_cast<>()`. */ using Microseconds64 = std::chrono::duration; using Microseconds32 = std::chrono::duration; using Milliseconds64 = std::chrono::duration; using Milliseconds32 = std::chrono::duration; using Milliseconds16 = std::chrono::duration; using Seconds64 = std::chrono::duration; using Seconds32 = std::chrono::duration; using Seconds16 = std::chrono::duration; inline constexpr Seconds16 kZero{ 0 }; namespace Literals { constexpr Microseconds64 operator""_us(unsigned long long int us) { return Microseconds64(us); } constexpr Microseconds64 operator""_us64(unsigned long long int us) { return Microseconds64(us); } constexpr Microseconds32 operator""_us32(unsigned long long int us) { return Microseconds32(us); } constexpr Milliseconds64 operator""_ms(unsigned long long int ms) { return Milliseconds64(ms); } constexpr Milliseconds64 operator""_ms64(unsigned long long int ms) { return Milliseconds64(ms); } constexpr Milliseconds32 operator""_ms32(unsigned long long int ms) { return Milliseconds32(ms); } constexpr Milliseconds16 operator""_ms16(unsigned long long int ms) { return Milliseconds16(ms); } constexpr Seconds64 operator""_s(unsigned long long int s) { return Seconds64(s); } constexpr Seconds64 operator""_s64(unsigned long long int s) { return Seconds64(s); } constexpr Seconds32 operator""_s32(unsigned long long int s) { return Seconds32(s); } constexpr Seconds16 operator""_s16(unsigned long long int s) { return Seconds16(s); } } // namespace Literals /** * Type for System time stamps. */ using Timestamp = Milliseconds64; /** * Type for System time offsets (i.e. `StartTime()` duration). * * It is required of platforms that time stamps from `GetMonotonic…()` have the high bit(s) zero, * so the sum of a `Milliseconds64` time stamp and `Milliseconds32` offset will never overflow. */ using Timeout = Milliseconds32; class ClockBase { public: virtual ~ClockBase() = default; /** * Returns a monotonic system time. * * This function returns an elapsed time since an arbitrary, platform-defined epoch. * The value returned is guaranteed to be ever-increasing (i.e. never wrapping or decreasing) between * reboots of the system. Additionally, the underlying time source is guaranteed to tick * continuously during any system sleep modes that do not entail a restart upon wake. * * Although some platforms may choose to return a value that measures the time since boot for the * system, applications must *not* rely on this. * * WARNING: *** It is up to each platform to ensure that GetMonotonicTimestamp can be * called safely in a re-entrant way from multiple contexts if making use * of this method from the application, outside the Matter stack execution * serialization context. *** */ virtual Timestamp GetMonotonicTimestamp(); /** * Returns a monotonic system time in units of microseconds, from the platform. * * This function returns an elapsed time in microseconds since an arbitrary, platform-defined epoch. * The value returned MUST BE guaranteed to be ever-increasing (i.e. never wrapping or decreasing) until * reboot of the system. Additionally, the underlying time source is guaranteed to tick * continuously during any system sleep modes that do not entail a restart upon wake. * * Although some platforms may choose to return a value that measures the time since boot for the * system, applications must *not* rely on this. * * Applications must not rely on the time returned by GetMonotonicMicroseconds64() actually having * granularity finer than milliseconds. * * Platform implementations *must* use the same epoch for GetMonotonicMicroseconds64() and GetMonotonicMilliseconds64(). * * Platforms *must* use an epoch such that the upper bit of a value returned by GetMonotonicMicroseconds64() is zero * for the expected operational life of the system. * * @returns Elapsed time in microseconds since an arbitrary, platform-defined epoch. */ virtual Microseconds64 GetMonotonicMicroseconds64() = 0; /** * Returns a monotonic system time in units of microseconds, from the platform. * * This function returns an elapsed time in milliseconds since an arbitrary, platform-defined epoch. * The value returned MUST BE guaranteed to be ever-increasing (i.e. never wrapping or decreasing) until * reboot of the system. Additionally, the underlying time source is guaranteed to tick * continuously during any system sleep modes that do not entail a restart upon wake. * * Although some platforms may choose to return a value that measures the time since boot for the * system, applications must *not* rely on this. * * Platform implementations *must* use the same epoch for GetMonotonicMicroseconds64() and GetMonotonicMilliseconds64(). * (As a consequence of this, and the requirement for GetMonotonicMicroseconds64() to return high bit zero, values * returned by GetMonotonicMilliseconds64() will have the high ten bits zero.) * * @returns Elapsed time in milliseconds since an arbitrary, platform-defined epoch. */ virtual Milliseconds64 GetMonotonicMilliseconds64() = 0; /** * @brief * Platform-specific function for getting the current real (civil) time in microsecond Unix time * format. * * This function is expected to return the local platform's notion of current real time, expressed * as a Unix time value scaled to microseconds. The underlying clock is required to tick at a * rate of least at whole seconds (values of 1,000,000), but may tick faster. * * On those platforms that are capable of tracking real time, GetClock_RealTime() must return the * error CHIP_ERROR_REAL_TIME_NOT_SYNCED whenever the system is unsynchronized with real time. * * Platforms that are incapable of tracking real time should not implement the GetClock_RealTime() * function, thereby forcing link-time failures of features that depend on access to real time. * Alternatively, such platforms may supply an implementation of GetClock_RealTime() that returns * the error CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE. * * This function is expected to be thread-safe on any platform that employs threading. * * @note * This function is reserved for internal use by the System Clock. Users of the System * Clock should call System::Clock::GetClock_RealTime(). * * @param[out] aCurTime The current time, expressed as Unix time scaled to microseconds. * * @retval #CHIP_NO_ERROR If the method succeeded. * @retval #CHIP_ERROR_REAL_TIME_NOT_SYNCED * If the platform is capable of tracking real time, but is * is currently unsynchronized. * @retval #CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE * If the platform is incapable of tracking real time. */ virtual CHIP_ERROR GetClock_RealTime(Microseconds64 & aCurTime) = 0; /** * @brief * Platform-specific function for getting the current real (civil) time in millisecond Unix time * format. * * This function is expected to return the local platform's notion of current real time, expressed * as a Unix time value scaled to milliseconds. * * See the documentation for GetClock_RealTime() for details on the expected behavior. * * @note * This function is reserved for internal use by the System Clock. Users of the System * Clock should call System::Clock::GetClock_RealTimeMS(). * * @param[out] aCurTime The current time, expressed as Unix time scaled to milliseconds. * * @retval #CHIP_NO_ERROR If the method succeeded. * @retval #CHIP_ERROR_REAL_TIME_NOT_SYNCED * If the platform is capable of tracking real time, but is * is currently unsynchronized. * @retval #CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE * If the platform is incapable of tracking real time. */ virtual CHIP_ERROR GetClock_RealTimeMS(Milliseconds64 & aCurTime) = 0; /** * @brief * Platform-specific function for setting the current real (civil) time. * * This function set the local platform's notion of current real time. The new current * time is expressed as a Unix time value scaled to microseconds. * * Once set, underlying platform clock is expected to track real time with a granularity of at least whole * seconds. * * On platforms that support tracking real time, the SetClock_RealTime() function must return the error * CHIP_ERROR_ACCESS_DENIED if the calling application does not have the privilege to set the * current time. * * Platforms that are incapable of tracking real time, or do not offer the ability to set real time, * should not implement the SetClock_RealTime() function, thereby forcing link-time failures of features * that depend on setting real time. Alternatively, such platforms may supply an implementation of * SetClock_RealTime() that returns the error CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE. * * This function is expected to be thread-safe on any platform that employs threading. * * @note * This function is reserved for internal use by the System Clock. Users of the System * Clock should call System::Clock::GetClock_RealTimeMS(). * * @param[in] aNewCurTime The new current time, expressed as Unix time scaled to microseconds. * * @retval #CHIP_NO_ERROR If the method succeeded. * @retval #CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE * If the platform is incapable of tracking real time. * @retval #CHIP_ERROR_ACCESS_DENIED * If the calling application does not have the privilege to set the * current time. */ virtual CHIP_ERROR SetClock_RealTime(Microseconds64 aNewCurTime) = 0; protected: uint64_t mLastTimestamp = 0; }; // Currently we have a single implementation class, ClockImpl, whose members are implemented in build-specific files. class ClockImpl : public ClockBase { public: ~ClockImpl() override = default; Microseconds64 GetMonotonicMicroseconds64() override; Milliseconds64 GetMonotonicMilliseconds64() override; CHIP_ERROR GetClock_RealTime(Microseconds64 & aCurTime) override; CHIP_ERROR GetClock_RealTimeMS(Milliseconds64 & aCurTime) override; CHIP_ERROR SetClock_RealTime(Microseconds64 aNewCurTime) override; }; namespace Internal { // This should only be used via SystemClock() below. extern ClockBase * gClockBase; inline void SetSystemClockForTesting(Clock::ClockBase * clock) { Clock::Internal::gClockBase = clock; } // Provide a mock implementation for use by unit tests. class MockClock : public ClockImpl { public: Microseconds64 GetMonotonicMicroseconds64() override { return mSystemTime; } Milliseconds64 GetMonotonicMilliseconds64() override { return std::chrono::duration_cast(mSystemTime); } CHIP_ERROR GetClock_RealTime(Microseconds64 & aCurTime) override { aCurTime = mRealTime; return CHIP_NO_ERROR; } CHIP_ERROR GetClock_RealTimeMS(Milliseconds64 & aCurTime) override { aCurTime = std::chrono::duration_cast(mRealTime); return CHIP_NO_ERROR; } CHIP_ERROR SetClock_RealTime(Microseconds64 aNewCurTime) override { mRealTime = aNewCurTime; return CHIP_NO_ERROR; } void SetMonotonic(Milliseconds64 timestamp) { mSystemTime = timestamp; #if CHIP_DEVICE_LAYER_USE_ATOMICS_FOR_CLOCK __atomic_store_n(&mLastTimestamp, timestamp.count(), __ATOMIC_SEQ_CST); #endif // CHIP_DEVICE_LAYER_USE_ATOMICS_FOR_CLOCK } void AdvanceMonotonic(Milliseconds64 increment) { mSystemTime += increment; } void AdvanceRealTime(Milliseconds64 increment) { mRealTime += increment; } Microseconds64 mSystemTime = Clock::kZero; Microseconds64 mRealTime = Clock::kZero; }; } // namespace Internal #if CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS Microseconds64 TimevalToMicroseconds(const timeval & in); void ToTimeval(Microseconds64 in, timeval & out); #endif // CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS } // namespace Clock inline Clock::ClockBase & SystemClock() { return *Clock::Internal::gClockBase; } } // namespace System } // namespace chip