mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-22 17:24:48 -05:00
b2e7b8cdff
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.
252 lines
7.6 KiB
C++
252 lines
7.6 KiB
C++
/*
|
|
* Copyright (c) 2020, the SerenityOS developers.
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/Checked.h>
|
|
#include <AK/Time.h>
|
|
|
|
// Make a reasonable guess as to which timespec/timeval definition to use.
|
|
// It doesn't really matter, since both are identical.
|
|
#ifdef KERNEL
|
|
# include <Kernel/UnixTypes.h>
|
|
#else
|
|
# include <sys/time.h>
|
|
# include <time.h>
|
|
#endif
|
|
|
|
namespace AK {
|
|
|
|
int days_in_month(int year, unsigned month)
|
|
{
|
|
VERIFY(month >= 1 && month <= 12);
|
|
if (month == 2)
|
|
return is_leap_year(year) ? 29 : 28;
|
|
|
|
bool is_long_month = (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12);
|
|
return is_long_month ? 31 : 30;
|
|
}
|
|
|
|
unsigned day_of_week(int year, unsigned month, int day)
|
|
{
|
|
VERIFY(month >= 1 && month <= 12);
|
|
constexpr Array seek_table = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
|
|
if (month < 3)
|
|
--year;
|
|
|
|
return (year + year / 4 - year / 100 + year / 400 + seek_table[month - 1] + day) % 7;
|
|
}
|
|
|
|
Duration Duration::from_ticks(clock_t ticks, time_t ticks_per_second)
|
|
{
|
|
auto secs = ticks % ticks_per_second;
|
|
|
|
i32 nsecs = 1'000'000'000 * (ticks - (ticks_per_second * secs)) / ticks_per_second;
|
|
i32 extra_secs = sane_mod(nsecs, 1'000'000'000);
|
|
return Duration::from_half_sanitized(secs, extra_secs, nsecs);
|
|
}
|
|
|
|
Duration Duration::from_timespec(const struct timespec& ts)
|
|
{
|
|
i32 nsecs = ts.tv_nsec;
|
|
i32 extra_secs = sane_mod(nsecs, 1'000'000'000);
|
|
return Duration::from_half_sanitized(ts.tv_sec, extra_secs, nsecs);
|
|
}
|
|
|
|
Duration Duration::from_timeval(const struct timeval& tv)
|
|
{
|
|
i32 usecs = tv.tv_usec;
|
|
i32 extra_secs = sane_mod(usecs, 1'000'000);
|
|
VERIFY(0 <= usecs && usecs < 1'000'000);
|
|
return Duration::from_half_sanitized(tv.tv_sec, extra_secs, usecs * 1'000);
|
|
}
|
|
|
|
i64 Duration::to_truncated_seconds() const
|
|
{
|
|
VERIFY(m_nanoseconds < 1'000'000'000);
|
|
if (m_seconds < 0 && m_nanoseconds) {
|
|
// Since m_seconds is negative, adding 1 can't possibly overflow
|
|
return m_seconds + 1;
|
|
}
|
|
return m_seconds;
|
|
}
|
|
|
|
i64 Duration::to_truncated_milliseconds() const
|
|
{
|
|
VERIFY(m_nanoseconds < 1'000'000'000);
|
|
Checked<i64> milliseconds((m_seconds < 0) ? m_seconds + 1 : m_seconds);
|
|
milliseconds *= 1'000;
|
|
milliseconds += m_nanoseconds / 1'000'000;
|
|
if (m_seconds < 0) {
|
|
if (m_nanoseconds % 1'000'000 != 0) {
|
|
// Does not overflow: milliseconds <= 1'999.
|
|
milliseconds++;
|
|
}
|
|
// We dropped one second previously, put it back in now that we have handled the rounding.
|
|
milliseconds -= 1'000;
|
|
}
|
|
if (!milliseconds.has_overflow())
|
|
return milliseconds.value();
|
|
return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
|
|
}
|
|
|
|
i64 Duration::to_truncated_microseconds() const
|
|
{
|
|
VERIFY(m_nanoseconds < 1'000'000'000);
|
|
Checked<i64> microseconds((m_seconds < 0) ? m_seconds + 1 : m_seconds);
|
|
microseconds *= 1'000'000;
|
|
microseconds += m_nanoseconds / 1'000;
|
|
if (m_seconds < 0) {
|
|
if (m_nanoseconds % 1'000 != 0) {
|
|
// Does not overflow: microseconds <= 1'999'999.
|
|
microseconds++;
|
|
}
|
|
// We dropped one second previously, put it back in now that we have handled the rounding.
|
|
microseconds -= 1'000'000;
|
|
}
|
|
if (!microseconds.has_overflow())
|
|
return microseconds.value();
|
|
return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
|
|
}
|
|
|
|
i64 Duration::to_seconds() const
|
|
{
|
|
VERIFY(m_nanoseconds < 1'000'000'000);
|
|
if (m_seconds >= 0 && m_nanoseconds) {
|
|
Checked<i64> seconds(m_seconds);
|
|
seconds++;
|
|
return seconds.has_overflow() ? 0x7fff'ffff'ffff'ffffLL : seconds.value();
|
|
}
|
|
return m_seconds;
|
|
}
|
|
|
|
i64 Duration::to_milliseconds() const
|
|
{
|
|
VERIFY(m_nanoseconds < 1'000'000'000);
|
|
Checked<i64> milliseconds((m_seconds < 0) ? m_seconds + 1 : m_seconds);
|
|
milliseconds *= 1'000;
|
|
milliseconds += m_nanoseconds / 1'000'000;
|
|
if (m_seconds >= 0 && m_nanoseconds % 1'000'000 != 0)
|
|
milliseconds++;
|
|
if (m_seconds < 0) {
|
|
// We dropped one second previously, put it back in now that we have handled the rounding.
|
|
milliseconds -= 1'000;
|
|
}
|
|
if (!milliseconds.has_overflow())
|
|
return milliseconds.value();
|
|
return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
|
|
}
|
|
|
|
i64 Duration::to_microseconds() const
|
|
{
|
|
VERIFY(m_nanoseconds < 1'000'000'000);
|
|
Checked<i64> microseconds((m_seconds < 0) ? m_seconds + 1 : m_seconds);
|
|
microseconds *= 1'000'000;
|
|
microseconds += m_nanoseconds / 1'000;
|
|
if (m_seconds >= 0 && m_nanoseconds % 1'000 != 0)
|
|
microseconds++;
|
|
if (m_seconds < 0) {
|
|
// We dropped one second previously, put it back in now that we have handled the rounding.
|
|
microseconds -= 1'000'000;
|
|
}
|
|
if (!microseconds.has_overflow())
|
|
return microseconds.value();
|
|
return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
|
|
}
|
|
|
|
i64 Duration::to_nanoseconds() const
|
|
{
|
|
VERIFY(m_nanoseconds < 1'000'000'000);
|
|
Checked<i64> nanoseconds((m_seconds < 0) ? m_seconds + 1 : m_seconds);
|
|
nanoseconds *= 1'000'000'000;
|
|
nanoseconds += m_nanoseconds;
|
|
if (m_seconds < 0) {
|
|
// We dropped one second previously, put it back in now that we have handled the rounding.
|
|
nanoseconds -= 1'000'000'000;
|
|
}
|
|
if (!nanoseconds.has_overflow())
|
|
return nanoseconds.value();
|
|
return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
|
|
}
|
|
|
|
timespec Duration::to_timespec() const
|
|
{
|
|
VERIFY(m_nanoseconds < 1'000'000'000);
|
|
return { static_cast<time_t>(m_seconds), static_cast<long>(m_nanoseconds) };
|
|
}
|
|
|
|
timeval Duration::to_timeval() const
|
|
{
|
|
VERIFY(m_nanoseconds < 1'000'000'000);
|
|
// This is done because winsock defines tv_sec and tv_usec as long, and Linux64 as long int.
|
|
using sec_type = decltype(declval<timeval>().tv_sec);
|
|
using usec_type = decltype(declval<timeval>().tv_usec);
|
|
return { static_cast<sec_type>(m_seconds), static_cast<usec_type>(m_nanoseconds) / 1000 };
|
|
}
|
|
|
|
Duration Duration::from_half_sanitized(i64 seconds, i32 extra_seconds, u32 nanoseconds)
|
|
{
|
|
VERIFY(nanoseconds < 1'000'000'000);
|
|
|
|
if ((seconds <= 0 && extra_seconds > 0) || (seconds >= 0 && extra_seconds < 0)) {
|
|
// Opposite signs mean that we can definitely add them together without fear of overflowing i64:
|
|
seconds += extra_seconds;
|
|
extra_seconds = 0;
|
|
}
|
|
|
|
// Now the only possible way to become invalid is overflowing i64 towards positive infinity:
|
|
if (Checked<i64>::addition_would_overflow<i64, i64>(seconds, extra_seconds)) {
|
|
if (seconds < 0) {
|
|
return Duration::min();
|
|
} else {
|
|
return Duration::max();
|
|
}
|
|
}
|
|
|
|
return Duration { seconds + extra_seconds, nanoseconds };
|
|
}
|
|
|
|
#ifndef KERNEL
|
|
namespace {
|
|
static Duration now_time_from_clock(clockid_t clock_id)
|
|
{
|
|
timespec now_spec {};
|
|
::clock_gettime(clock_id, &now_spec);
|
|
return Duration::from_timespec(now_spec);
|
|
}
|
|
}
|
|
|
|
Duration Duration::now_monotonic()
|
|
{
|
|
return now_time_from_clock(CLOCK_MONOTONIC);
|
|
}
|
|
|
|
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) };
|
|
}
|
|
|
|
UnixDateTime UnixDateTime::now_coarse()
|
|
{
|
|
return UnixDateTime { now_time_from_clock(CLOCK_REALTIME_COARSE) };
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|