AK: Introduce MonotonicTime

This class takes on the duties of CLOCK_MONOTONIC, a time without a
defined reference point that always increases. This informs some
important design decisions about the class API: MonotonicTime cannot be
constructed from external time data, except as a computation based on
other monotonic time, or the current monotonic time. Importantly, there
is no default constructor, since the reference point of monotonic time
is unspecified and therefore without meaning as a default.

The current use of monotonic time (via Duration) includes some potential
problems that may be caught when we move most to all code to
MonotonicTime in the next commit.

The API restrictions have one important relaxation:
Kernel::TimeManagement is allowed to exchange raw time data within
MonotonicTime freely. This is required for the clock-agnostic time
accessors for timeouts and syscalls, as well as creating monotonic time
data from hardware in the first place.
This commit is contained in:
kleines Filmröllchen 2023-03-13 22:42:09 +01:00 committed by Jelle Raaijmakers
parent 69e27169c4
commit b2e7b8cdff
Notes: sideshowbarker 2024-07-17 07:31:31 +09:00
2 changed files with 79 additions and 0 deletions

View file

@ -227,6 +227,16 @@ Duration Duration::now_monotonic_coarse()
return now_time_from_clock(CLOCK_MONOTONIC_COARSE);
}
MonotonicTime MonotonicTime::now()
{
return MonotonicTime { now_time_from_clock(CLOCK_MONOTONIC) };
}
MonotonicTime MonotonicTime::now_coarse()
{
return MonotonicTime { now_time_from_clock(CLOCK_MONOTONIC_COARSE) };
}
UnixDateTime UnixDateTime::now()
{
return UnixDateTime { now_time_from_clock(CLOCK_REALTIME) };

View file

@ -8,6 +8,7 @@
#include <AK/Array.h>
#include <AK/Assertions.h>
#include <AK/Badge.h>
#include <AK/Checked.h>
#include <AK/Platform.h>
#include <AK/Types.h>
@ -15,6 +16,12 @@
#if defined(AK_OS_SERENITY) && defined(KERNEL)
# include <Kernel/API/POSIX/sys/time.h>
# include <Kernel/API/POSIX/time.h>
// We need a Badge<TimeManagement> for some MonotonicTime operations.
namespace Kernel {
class TimeManagement;
}
#else
# include <sys/time.h>
# include <time.h>
@ -456,6 +463,67 @@ private:
}
};
// Monotonic time represents time returned from the CLOCK_MONOTONIC clock, which has an arbitrary fixed reference point.
class MonotonicTime : private Detail::UnawareTime {
public:
// Monotonic time does not have a defined reference point.
// A MonotonicTime at the reference point is therefore meaningless.
MonotonicTime() = delete;
constexpr MonotonicTime(MonotonicTime const&) = default;
constexpr MonotonicTime(MonotonicTime&&) = default;
constexpr MonotonicTime& operator=(MonotonicTime const&) = default;
constexpr MonotonicTime& operator=(MonotonicTime&&) = default;
#ifndef KERNEL
[[nodiscard]] static MonotonicTime now();
[[nodiscard]] static MonotonicTime now_coarse();
#endif
// clang-format off
// Clang-format likes to expand this function for some reason.
[[nodiscard]] i64 seconds() const { return m_offset.to_seconds(); }
// clang-format on
[[nodiscard]] i64 milliseconds() const { return m_offset.to_milliseconds(); }
[[nodiscard]] i64 nanoseconds() const { return m_offset.to_nanoseconds(); }
// Never returns a point in the future, since fractional seconds are cut off.
[[nodiscard]] i64 truncated_seconds() const { return m_offset.to_truncated_seconds(); }
[[nodiscard]] i64 nanoseconds_within_second() const { return to_timespec().tv_nsec; }
// clang-format off
constexpr bool operator==(MonotonicTime const& other) const { return this->m_offset == other.m_offset; }
// clang-format on
constexpr int operator<=>(MonotonicTime const& other) const { return this->m_offset <=> other.m_offset; }
constexpr MonotonicTime operator+(Duration const& other) const { return MonotonicTime { m_offset + other }; }
constexpr MonotonicTime& operator+=(Duration const& other)
{
this->m_offset = this->m_offset + other;
return *this;
}
constexpr MonotonicTime operator-(Duration const& other) const { return MonotonicTime { m_offset - other }; }
constexpr Duration operator-(MonotonicTime const& other) const { return m_offset - other.m_offset; }
#ifdef KERNEL
// Required in the Kernel in order to create monotonic time information from hardware timers.
[[nodiscard]] static MonotonicTime from_hardware_time(Badge<Kernel::TimeManagement>, time_t seconds, long nanoseconds)
{
return MonotonicTime { Duration::from_timespec({ seconds, nanoseconds }) };
}
// "Start" is whenever the hardware timers started counting (e.g. for HPET it's most certainly boot).
[[nodiscard]] Duration time_since_start(Badge<Kernel::TimeManagement>)
{
return m_offset;
}
#endif
private:
constexpr explicit MonotonicTime(Duration offset)
: Detail::UnawareTime(offset)
{
}
};
template<typename TimevalType>
inline void timeval_sub(TimevalType const& a, TimevalType const& b, TimevalType& result)
{
@ -545,6 +613,7 @@ using AK::days_in_year;
using AK::days_since_epoch;
using AK::Duration;
using AK::is_leap_year;
using AK::MonotonicTime;
using AK::seconds_since_epoch_to_year;
using AK::timespec_add;
using AK::timespec_add_timeval;