Coverage Report

Created: 2024-09-23 06:29

/src/abseil-cpp/absl/synchronization/internal/kernel_timeout.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2023 The Abseil Authors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include "absl/synchronization/internal/kernel_timeout.h"
16
17
#ifndef _WIN32
18
#include <sys/types.h>
19
#endif
20
21
#include <algorithm>
22
#include <chrono>  // NOLINT(build/c++11)
23
#include <cstdint>
24
#include <cstdlib>
25
#include <cstring>
26
#include <ctime>
27
#include <limits>
28
29
#include "absl/base/attributes.h"
30
#include "absl/base/call_once.h"
31
#include "absl/base/config.h"
32
#include "absl/time/time.h"
33
34
namespace absl {
35
ABSL_NAMESPACE_BEGIN
36
namespace synchronization_internal {
37
38
#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
39
constexpr uint64_t KernelTimeout::kNoTimeout;
40
constexpr int64_t KernelTimeout::kMaxNanos;
41
#endif
42
43
0
int64_t KernelTimeout::SteadyClockNow() {
44
0
  if (!SupportsSteadyClock()) {
45
0
    return absl::GetCurrentTimeNanos();
46
0
  }
47
0
  return std::chrono::duration_cast<std::chrono::nanoseconds>(
48
0
             std::chrono::steady_clock::now().time_since_epoch())
49
0
      .count();
50
0
}
51
52
0
KernelTimeout::KernelTimeout(absl::Time t) {
53
  // `absl::InfiniteFuture()` is a common "no timeout" value and cheaper to
54
  // compare than convert.
55
0
  if (t == absl::InfiniteFuture()) {
56
0
    rep_ = kNoTimeout;
57
0
    return;
58
0
  }
59
60
0
  int64_t unix_nanos = absl::ToUnixNanos(t);
61
62
  // A timeout that lands before the unix epoch is converted to 0.
63
  // In theory implementations should expire these timeouts immediately.
64
0
  if (unix_nanos < 0) {
65
0
    unix_nanos = 0;
66
0
  }
67
68
  // Values greater than or equal to kMaxNanos are converted to infinite.
69
0
  if (unix_nanos >= kMaxNanos) {
70
0
    rep_ = kNoTimeout;
71
0
    return;
72
0
  }
73
74
0
  rep_ = static_cast<uint64_t>(unix_nanos) << 1;
75
0
}
76
77
0
KernelTimeout::KernelTimeout(absl::Duration d) {
78
  // `absl::InfiniteDuration()` is a common "no timeout" value and cheaper to
79
  // compare than convert.
80
0
  if (d == absl::InfiniteDuration()) {
81
0
    rep_ = kNoTimeout;
82
0
    return;
83
0
  }
84
85
0
  int64_t nanos = absl::ToInt64Nanoseconds(d);
86
87
  // Negative durations are normalized to 0.
88
  // In theory implementations should expire these timeouts immediately.
89
0
  if (nanos < 0) {
90
0
    nanos = 0;
91
0
  }
92
93
0
  int64_t now = SteadyClockNow();
94
0
  if (nanos > kMaxNanos - now) {
95
    // Durations that would be greater than kMaxNanos are converted to infinite.
96
0
    rep_ = kNoTimeout;
97
0
    return;
98
0
  }
99
100
0
  nanos += now;
101
0
  rep_ = (static_cast<uint64_t>(nanos) << 1) | uint64_t{1};
102
0
}
103
104
0
int64_t KernelTimeout::MakeAbsNanos() const {
105
0
  if (!has_timeout()) {
106
0
    return kMaxNanos;
107
0
  }
108
109
0
  int64_t nanos = RawAbsNanos();
110
111
0
  if (is_relative_timeout()) {
112
    // We need to change epochs, because the relative timeout might be
113
    // represented by an absolute timestamp from another clock.
114
0
    nanos = std::max<int64_t>(nanos - SteadyClockNow(), 0);
115
0
    int64_t now = absl::GetCurrentTimeNanos();
116
0
    if (nanos > kMaxNanos - now) {
117
      // Overflow.
118
0
      nanos = kMaxNanos;
119
0
    } else {
120
0
      nanos += now;
121
0
    }
122
0
  } else if (nanos == 0) {
123
    // Some callers have assumed that 0 means no timeout, so instead we return a
124
    // time of 1 nanosecond after the epoch.
125
0
    nanos = 1;
126
0
  }
127
128
0
  return nanos;
129
0
}
130
131
0
int64_t KernelTimeout::InNanosecondsFromNow() const {
132
0
  if (!has_timeout()) {
133
0
    return kMaxNanos;
134
0
  }
135
136
0
  int64_t nanos = RawAbsNanos();
137
0
  if (is_absolute_timeout()) {
138
0
    return std::max<int64_t>(nanos - absl::GetCurrentTimeNanos(), 0);
139
0
  }
140
0
  return std::max<int64_t>(nanos - SteadyClockNow(), 0);
141
0
}
142
143
0
struct timespec KernelTimeout::MakeAbsTimespec() const {
144
0
  return absl::ToTimespec(absl::Nanoseconds(MakeAbsNanos()));
145
0
}
146
147
0
struct timespec KernelTimeout::MakeRelativeTimespec() const {
148
0
  return absl::ToTimespec(absl::Nanoseconds(InNanosecondsFromNow()));
149
0
}
150
151
#ifndef _WIN32
152
0
struct timespec KernelTimeout::MakeClockAbsoluteTimespec(clockid_t c) const {
153
0
  if (!has_timeout()) {
154
0
    return absl::ToTimespec(absl::Nanoseconds(kMaxNanos));
155
0
  }
156
157
0
  int64_t nanos = RawAbsNanos();
158
0
  if (is_absolute_timeout()) {
159
0
    nanos -= absl::GetCurrentTimeNanos();
160
0
  } else {
161
0
    nanos -= SteadyClockNow();
162
0
  }
163
164
0
  struct timespec now;
165
0
  ABSL_RAW_CHECK(clock_gettime(c, &now) == 0, "clock_gettime() failed");
166
0
  absl::Duration from_clock_epoch =
167
0
      absl::DurationFromTimespec(now) + absl::Nanoseconds(nanos);
168
0
  if (from_clock_epoch <= absl::ZeroDuration()) {
169
    // Some callers have assumed that 0 means no timeout, so instead we return a
170
    // time of 1 nanosecond after the epoch. For safety we also do not return
171
    // negative values.
172
0
    return absl::ToTimespec(absl::Nanoseconds(1));
173
0
  }
174
0
  return absl::ToTimespec(from_clock_epoch);
175
0
}
176
#endif
177
178
0
KernelTimeout::DWord KernelTimeout::InMillisecondsFromNow() const {
179
0
  constexpr DWord kInfinite = std::numeric_limits<DWord>::max();
180
181
0
  if (!has_timeout()) {
182
0
    return kInfinite;
183
0
  }
184
185
0
  constexpr uint64_t kNanosInMillis = uint64_t{1'000'000};
186
0
  constexpr uint64_t kMaxValueNanos =
187
0
      std::numeric_limits<int64_t>::max() - kNanosInMillis + 1;
188
189
0
  uint64_t ns_from_now = static_cast<uint64_t>(InNanosecondsFromNow());
190
0
  if (ns_from_now >= kMaxValueNanos) {
191
    // Rounding up would overflow.
192
0
    return kInfinite;
193
0
  }
194
  // Convert to milliseconds, always rounding up.
195
0
  uint64_t ms_from_now = (ns_from_now + kNanosInMillis - 1) / kNanosInMillis;
196
0
  if (ms_from_now > kInfinite) {
197
0
    return kInfinite;
198
0
  }
199
0
  return static_cast<DWord>(ms_from_now);
200
0
}
201
202
std::chrono::time_point<std::chrono::system_clock>
203
0
KernelTimeout::ToChronoTimePoint() const {
204
0
  if (!has_timeout()) {
205
0
    return std::chrono::time_point<std::chrono::system_clock>::max();
206
0
  }
207
208
  // The cast to std::microseconds is because (on some platforms) the
209
  // std::ratio used by std::chrono::steady_clock doesn't convert to
210
  // std::nanoseconds, so it doesn't compile.
211
0
  auto micros = std::chrono::duration_cast<std::chrono::microseconds>(
212
0
      std::chrono::nanoseconds(MakeAbsNanos()));
213
0
  return std::chrono::system_clock::from_time_t(0) + micros;
214
0
}
215
216
0
std::chrono::nanoseconds KernelTimeout::ToChronoDuration() const {
217
0
  if (!has_timeout()) {
218
0
    return std::chrono::nanoseconds::max();
219
0
  }
220
0
  return std::chrono::nanoseconds(InNanosecondsFromNow());
221
0
}
222
223
}  // namespace synchronization_internal
224
ABSL_NAMESPACE_END
225
}  // namespace absl