userver: userver/engine/deadline.hpp Source File
Loading...
Searching...
No Matches
deadline.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/engine/deadline.hpp
4/// @brief Internal representation of a deadline time point
5
6#include <chrono>
7#include <type_traits>
8
9#include <userver/utils/assert.hpp>
10
11USERVER_NAMESPACE_BEGIN
12
13namespace engine {
14
15/// @brief Internal representation of a deadline time point
16class Deadline final {
17 public:
18 using Clock = std::chrono::steady_clock;
19 using TimePoint = Clock::time_point;
20 using Duration = TimePoint::duration;
21
22 /// Creates an unreachable deadline
23 constexpr Deadline() = default;
24
25 /// Returns whether the deadline can be reached
26 constexpr bool IsReachable() const noexcept { return value_ != TimePoint{}; }
27
28 /// Returns whether the deadline is reached
29 bool IsReached() const noexcept;
30
31 /// Returns whether the deadline is reached. Will report false-negatives, will
32 /// never report false-positives.
33 bool IsSurelyReachedApprox() const noexcept;
34
35 /// Returns the duration of time left before the reachable deadline
36 Duration TimeLeft() const noexcept;
37
38 /// Returns the approximate duration of time left before the reachable
39 /// deadline. May be faster than TimeLeft.
40 /// @see utils::datetime::SteadyCoarseClock
41 Duration TimeLeftApprox() const noexcept;
42
43 /// Converts duration to a Deadline
44 template <typename Rep, typename Period>
45 static Deadline FromDuration(
46 const std::chrono::duration<Rep, Period>& incoming_duration) noexcept {
47 using IncomingDuration = std::chrono::duration<Rep, Period>;
48
49 if (incoming_duration.count() < 0) {
50 return Deadline::Passed();
51 }
52
53 const auto now = TimePoint::clock::now();
54 constexpr auto max_now = TimePoint::clock::time_point::max();
55
56 // If:
57 // 1. incoming_duration would overflow Duration,
58 // 2. or adding it to 'now' would overflow,
59 // then set deadline to unreachable right away.
60
61 // Implementation strategy:
62 // 1. Check that resolution of Duration >= that of IncomingDuration.
63 static_assert(std::is_constructible_v<Duration, IncomingDuration>);
64
65 // 2. As it is higher, then the range is lower (or equal). So casting
66 // Duration::max to IncomingDuration is safe. Do a quick check
67 // that Duration{incoming_duration} won't overflow.
68 if (incoming_duration >
69 std::chrono::duration_cast<IncomingDuration>(Duration::max())) {
70 OnDurationOverflow(
71 std::chrono::duration_cast<std::chrono::duration<double>>(
72 incoming_duration));
73 return Deadline{};
74 }
75
76 // 3. Check that now + Duration{incoming_duration} won't overflow.
77 UASSERT(max_now - now >= Duration{incoming_duration});
78
79 return Deadline(now + Duration{incoming_duration});
80 }
81
82 /// @brief Converts time point to a Deadline
83 ///
84 /// Non-steady clocks may produce inaccurate Deadlines. Prefer using
85 /// Deadline::FromDuration or std::chrono::steady_clock::time_point
86 /// if possible.
87 template <typename Clock, typename Duration>
88 static Deadline FromTimePoint(
89 const std::chrono::time_point<Clock, Duration>& time_point) noexcept {
90 return FromDuration(time_point - Clock::now());
91 }
92
93 /// @cond
94 /// Specialization for the native time point type
95 constexpr static Deadline FromTimePoint(const TimePoint& time_point) {
96 return Deadline(time_point);
97 }
98 /// @endcond
99
100 /// A Deadline that is guaranteed to be IsReached
101 constexpr static Deadline Passed() noexcept { return Deadline{kPassed}; }
102
103 constexpr bool operator==(const Deadline& r) const noexcept {
104 return value_ == r.value_;
105 }
106
107 constexpr bool operator<(const Deadline& r) const noexcept {
108 if (!IsReachable()) return false;
109 if (!r.IsReachable()) return true;
110 return value_ < r.value_;
111 }
112
113 private:
114 constexpr explicit Deadline(TimePoint value) noexcept : value_(value) {}
115
116 static void OnDurationOverflow(
117 std::chrono::duration<double> incoming_duration);
118
119 static constexpr TimePoint kPassed = TimePoint::min();
120
121 TimePoint value_;
122};
123
124} // namespace engine
125
126USERVER_NAMESPACE_END