xpcom/ds/TimeStamp.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef mozilla_TimeStamp_h
michael@0 8 #define mozilla_TimeStamp_h
michael@0 9
michael@0 10 #include <stdint.h>
michael@0 11 #include "mozilla/Assertions.h"
michael@0 12 #include "mozilla/Attributes.h"
michael@0 13 #include "nscore.h"
michael@0 14
michael@0 15 namespace IPC {
michael@0 16 template <typename T> struct ParamTraits;
michael@0 17 }
michael@0 18
michael@0 19 #ifdef XP_WIN
michael@0 20 // defines TimeStampValue as a complex value keeping both
michael@0 21 // GetTickCount and QueryPerformanceCounter values
michael@0 22 #include "TimeStamp_windows.h"
michael@0 23 #endif
michael@0 24
michael@0 25 namespace mozilla {
michael@0 26
michael@0 27 #ifndef XP_WIN
michael@0 28 typedef uint64_t TimeStampValue;
michael@0 29 #endif
michael@0 30
michael@0 31 class TimeStamp;
michael@0 32
michael@0 33 /**
michael@0 34 * Instances of this class represent the length of an interval of time.
michael@0 35 * Negative durations are allowed, meaning the end is before the start.
michael@0 36 *
michael@0 37 * Internally the duration is stored as a int64_t in units of
michael@0 38 * PR_TicksPerSecond() when building with NSPR interval timers, or a
michael@0 39 * system-dependent unit when building with system clocks. The
michael@0 40 * system-dependent unit must be constant, otherwise the semantics of
michael@0 41 * this class would be broken.
michael@0 42 */
michael@0 43 class TimeDuration
michael@0 44 {
michael@0 45 public:
michael@0 46 // The default duration is 0.
michael@0 47 MOZ_CONSTEXPR TimeDuration() : mValue(0) {}
michael@0 48 // Allow construction using '0' as the initial value, for readability,
michael@0 49 // but no other numbers (so we don't have any implicit unit conversions).
michael@0 50 struct _SomethingVeryRandomHere;
michael@0 51 TimeDuration(_SomethingVeryRandomHere* aZero) : mValue(0) {
michael@0 52 MOZ_ASSERT(!aZero, "Who's playing funny games here?");
michael@0 53 }
michael@0 54 // Default copy-constructor and assignment are OK
michael@0 55
michael@0 56 double ToSeconds() const;
michael@0 57 // Return a duration value that includes digits of time we think to
michael@0 58 // be significant. This method should be used when displaying a
michael@0 59 // time to humans.
michael@0 60 double ToSecondsSigDigits() const;
michael@0 61 double ToMilliseconds() const {
michael@0 62 return ToSeconds() * 1000.0;
michael@0 63 }
michael@0 64 double ToMicroseconds() const {
michael@0 65 return ToMilliseconds() * 1000.0;
michael@0 66 }
michael@0 67
michael@0 68 // Using a double here is safe enough; with 53 bits we can represent
michael@0 69 // durations up to over 280,000 years exactly. If the units of
michael@0 70 // mValue do not allow us to represent durations of that length,
michael@0 71 // long durations are clamped to the max/min representable value
michael@0 72 // instead of overflowing.
michael@0 73 static inline TimeDuration FromSeconds(double aSeconds) {
michael@0 74 return FromMilliseconds(aSeconds * 1000.0);
michael@0 75 }
michael@0 76 static TimeDuration FromMilliseconds(double aMilliseconds);
michael@0 77 static inline TimeDuration FromMicroseconds(double aMicroseconds) {
michael@0 78 return FromMilliseconds(aMicroseconds / 1000.0);
michael@0 79 }
michael@0 80
michael@0 81 static TimeDuration Forever() {
michael@0 82 return FromTicks(INT64_MAX);
michael@0 83 }
michael@0 84
michael@0 85 TimeDuration operator+(const TimeDuration& aOther) const {
michael@0 86 return TimeDuration::FromTicks(mValue + aOther.mValue);
michael@0 87 }
michael@0 88 TimeDuration operator-(const TimeDuration& aOther) const {
michael@0 89 return TimeDuration::FromTicks(mValue - aOther.mValue);
michael@0 90 }
michael@0 91 TimeDuration& operator+=(const TimeDuration& aOther) {
michael@0 92 mValue += aOther.mValue;
michael@0 93 return *this;
michael@0 94 }
michael@0 95 TimeDuration& operator-=(const TimeDuration& aOther) {
michael@0 96 mValue -= aOther.mValue;
michael@0 97 return *this;
michael@0 98 }
michael@0 99
michael@0 100 private:
michael@0 101 // Block double multiplier (slower, imprecise if long duration) - Bug 853398.
michael@0 102 // If required, use MultDouble explicitly and with care.
michael@0 103 TimeDuration operator*(const double aMultiplier) const MOZ_DELETE;
michael@0 104
michael@0 105 public:
michael@0 106 TimeDuration MultDouble(double aMultiplier) const {
michael@0 107 return TimeDuration::FromTicks(static_cast<int64_t>(mValue * aMultiplier));
michael@0 108 }
michael@0 109 TimeDuration operator*(const int32_t aMultiplier) const {
michael@0 110 return TimeDuration::FromTicks(mValue * int64_t(aMultiplier));
michael@0 111 }
michael@0 112 TimeDuration operator*(const uint32_t aMultiplier) const {
michael@0 113 return TimeDuration::FromTicks(mValue * int64_t(aMultiplier));
michael@0 114 }
michael@0 115 TimeDuration operator*(const int64_t aMultiplier) const {
michael@0 116 return TimeDuration::FromTicks(mValue * int64_t(aMultiplier));
michael@0 117 }
michael@0 118 TimeDuration operator/(const int64_t aDivisor) const {
michael@0 119 return TimeDuration::FromTicks(mValue / aDivisor);
michael@0 120 }
michael@0 121 double operator/(const TimeDuration& aOther) const {
michael@0 122 return static_cast<double>(mValue) / aOther.mValue;
michael@0 123 }
michael@0 124
michael@0 125 bool operator<(const TimeDuration& aOther) const {
michael@0 126 return mValue < aOther.mValue;
michael@0 127 }
michael@0 128 bool operator<=(const TimeDuration& aOther) const {
michael@0 129 return mValue <= aOther.mValue;
michael@0 130 }
michael@0 131 bool operator>=(const TimeDuration& aOther) const {
michael@0 132 return mValue >= aOther.mValue;
michael@0 133 }
michael@0 134 bool operator>(const TimeDuration& aOther) const {
michael@0 135 return mValue > aOther.mValue;
michael@0 136 }
michael@0 137 bool operator==(const TimeDuration& aOther) const {
michael@0 138 return mValue == aOther.mValue;
michael@0 139 }
michael@0 140
michael@0 141 // Return a best guess at the system's current timing resolution,
michael@0 142 // which might be variable. TimeDurations below this order of
michael@0 143 // magnitude are meaningless, and those at the same order of
michael@0 144 // magnitude or just above are suspect.
michael@0 145 static TimeDuration Resolution();
michael@0 146
michael@0 147 // We could define additional operators here:
michael@0 148 // -- convert to/from other time units
michael@0 149 // -- scale duration by a float
michael@0 150 // but let's do that on demand.
michael@0 151 // Comparing durations for equality will only lead to bugs on
michael@0 152 // platforms with high-resolution timers.
michael@0 153
michael@0 154 private:
michael@0 155 friend class TimeStamp;
michael@0 156 friend struct IPC::ParamTraits<mozilla::TimeDuration>;
michael@0 157
michael@0 158 static TimeDuration FromTicks(int64_t aTicks) {
michael@0 159 TimeDuration t;
michael@0 160 t.mValue = aTicks;
michael@0 161 return t;
michael@0 162 }
michael@0 163
michael@0 164 static TimeDuration FromTicks(double aTicks) {
michael@0 165 // NOTE: this MUST be a >= test, because int64_t(double(INT64_MAX))
michael@0 166 // overflows and gives INT64_MIN.
michael@0 167 if (aTicks >= double(INT64_MAX))
michael@0 168 return TimeDuration::FromTicks(INT64_MAX);
michael@0 169
michael@0 170 // This MUST be a <= test.
michael@0 171 if (aTicks <= double(INT64_MIN))
michael@0 172 return TimeDuration::FromTicks(INT64_MIN);
michael@0 173
michael@0 174 return TimeDuration::FromTicks(int64_t(aTicks));
michael@0 175 }
michael@0 176
michael@0 177 // Duration, result is implementation-specific difference of two TimeStamps
michael@0 178 int64_t mValue;
michael@0 179 };
michael@0 180
michael@0 181 /**
michael@0 182 * Instances of this class represent moments in time, or a special
michael@0 183 * "null" moment. We do not use the non-monotonic system clock or
michael@0 184 * local time, since they can be reset, causing apparent backward
michael@0 185 * travel in time, which can confuse algorithms. Instead we measure
michael@0 186 * elapsed time according to the system. This time can never go
michael@0 187 * backwards (i.e. it never wraps around, at least not in less than
michael@0 188 * five million years of system elapsed time). It might not advance
michael@0 189 * while the system is sleeping. If TimeStamp::SetNow() is not called
michael@0 190 * at all for hours or days, we might not notice the passage of some
michael@0 191 * of that time.
michael@0 192 *
michael@0 193 * We deliberately do not expose a way to convert TimeStamps to some
michael@0 194 * particular unit. All you can do is compute a difference between two
michael@0 195 * TimeStamps to get a TimeDuration. You can also add a TimeDuration
michael@0 196 * to a TimeStamp to get a new TimeStamp. You can't do something
michael@0 197 * meaningless like add two TimeStamps.
michael@0 198 *
michael@0 199 * Internally this is implemented as either a wrapper around
michael@0 200 * - high-resolution, monotonic, system clocks if they exist on this
michael@0 201 * platform
michael@0 202 * - PRIntervalTime otherwise. We detect wraparounds of
michael@0 203 * PRIntervalTime and work around them.
michael@0 204 *
michael@0 205 * This class is similar to C++11's time_point, however it is
michael@0 206 * explicitly nullable and provides an IsNull() method. time_point
michael@0 207 * is initialized to the clock's epoch and provides a
michael@0 208 * time_since_epoch() method that functions similiarly. i.e.
michael@0 209 * t.IsNull() is equivalent to t.time_since_epoch() == decltype(t)::duration::zero();
michael@0 210 */
michael@0 211 class TimeStamp
michael@0 212 {
michael@0 213 public:
michael@0 214 /**
michael@0 215 * Initialize to the "null" moment
michael@0 216 */
michael@0 217 MOZ_CONSTEXPR TimeStamp() : mValue(0) {}
michael@0 218 // Default copy-constructor and assignment are OK
michael@0 219
michael@0 220 /**
michael@0 221 * Return true if this is the "null" moment
michael@0 222 */
michael@0 223 bool IsNull() const { return mValue == 0; }
michael@0 224 /**
michael@0 225 * Return a timestamp reflecting the current elapsed system time. This
michael@0 226 * is monotonically increasing (i.e., does not decrease) over the
michael@0 227 * lifetime of this process' XPCOM session.
michael@0 228 *
michael@0 229 * Now() is trying to ensure the best possible precision on each platform,
michael@0 230 * at least one millisecond.
michael@0 231 *
michael@0 232 * NowLoRes() has been introduced to workaround performance problems of
michael@0 233 * QueryPerformanceCounter on the Windows platform. NowLoRes() is giving
michael@0 234 * lower precision, usually 15.6 ms, but with very good performance benefit.
michael@0 235 * Use it for measurements of longer times, like >200ms timeouts.
michael@0 236 */
michael@0 237 static TimeStamp Now() { return Now(true); }
michael@0 238 static TimeStamp NowLoRes() { return Now(false); }
michael@0 239
michael@0 240 /**
michael@0 241 * Return a timestamp representing the time when the current process was
michael@0 242 * created which will be comparable with other timestamps taken with this
michael@0 243 * class. If the actual process creation time is detected to be inconsistent
michael@0 244 * the @a aIsInconsistent parameter will be set to true, the returned
michael@0 245 * timestamp however will still be valid though inaccurate.
michael@0 246 *
michael@0 247 * @param aIsInconsistent Set to true if an inconsistency was detected in the
michael@0 248 * process creation time
michael@0 249 * @returns A timestamp representing the time when the process was created,
michael@0 250 * this timestamp is always valid even when errors are reported
michael@0 251 */
michael@0 252 static TimeStamp ProcessCreation(bool& aIsInconsistent);
michael@0 253
michael@0 254 /**
michael@0 255 * Records a process restart. After this call ProcessCreation() will return
michael@0 256 * the time when the browser was restarted instead of the actual time when
michael@0 257 * the process was created.
michael@0 258 */
michael@0 259 static void RecordProcessRestart();
michael@0 260
michael@0 261 /**
michael@0 262 * Compute the difference between two timestamps. Both must be non-null.
michael@0 263 */
michael@0 264 TimeDuration operator-(const TimeStamp& aOther) const {
michael@0 265 MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
michael@0 266 MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
michael@0 267 static_assert(-INT64_MAX > INT64_MIN, "int64_t sanity check");
michael@0 268 int64_t ticks = int64_t(mValue - aOther.mValue);
michael@0 269 // Check for overflow.
michael@0 270 if (mValue > aOther.mValue) {
michael@0 271 if (ticks < 0) {
michael@0 272 ticks = INT64_MAX;
michael@0 273 }
michael@0 274 } else {
michael@0 275 if (ticks > 0) {
michael@0 276 ticks = INT64_MIN;
michael@0 277 }
michael@0 278 }
michael@0 279 return TimeDuration::FromTicks(ticks);
michael@0 280 }
michael@0 281
michael@0 282 TimeStamp operator+(const TimeDuration& aOther) const {
michael@0 283 MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
michael@0 284 return TimeStamp(mValue + aOther.mValue);
michael@0 285 }
michael@0 286 TimeStamp operator-(const TimeDuration& aOther) const {
michael@0 287 MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
michael@0 288 return TimeStamp(mValue - aOther.mValue);
michael@0 289 }
michael@0 290 TimeStamp& operator+=(const TimeDuration& aOther) {
michael@0 291 MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
michael@0 292 mValue += aOther.mValue;
michael@0 293 return *this;
michael@0 294 }
michael@0 295 TimeStamp& operator-=(const TimeDuration& aOther) {
michael@0 296 MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
michael@0 297 mValue -= aOther.mValue;
michael@0 298 return *this;
michael@0 299 }
michael@0 300
michael@0 301 bool operator<(const TimeStamp& aOther) const {
michael@0 302 MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
michael@0 303 MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
michael@0 304 return mValue < aOther.mValue;
michael@0 305 }
michael@0 306 bool operator<=(const TimeStamp& aOther) const {
michael@0 307 MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
michael@0 308 MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
michael@0 309 return mValue <= aOther.mValue;
michael@0 310 }
michael@0 311 bool operator>=(const TimeStamp& aOther) const {
michael@0 312 MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
michael@0 313 MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
michael@0 314 return mValue >= aOther.mValue;
michael@0 315 }
michael@0 316 bool operator>(const TimeStamp& aOther) const {
michael@0 317 MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
michael@0 318 MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
michael@0 319 return mValue > aOther.mValue;
michael@0 320 }
michael@0 321 bool operator==(const TimeStamp& aOther) const {
michael@0 322 // Maybe it's ok to check == with null timestamps?
michael@0 323 MOZ_ASSERT(!IsNull() && "Cannot compute with a null value");
michael@0 324 MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
michael@0 325 return mValue == aOther.mValue;
michael@0 326 }
michael@0 327 bool operator!=(const TimeStamp& aOther) const {
michael@0 328 // Maybe it's ok to check != with null timestamps?
michael@0 329 MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
michael@0 330 MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
michael@0 331 return mValue != aOther.mValue;
michael@0 332 }
michael@0 333
michael@0 334 // Comparing TimeStamps for equality should be discouraged. Adding
michael@0 335 // two TimeStamps, or scaling TimeStamps, is nonsense and must never
michael@0 336 // be allowed.
michael@0 337
michael@0 338 static NS_HIDDEN_(nsresult) Startup();
michael@0 339 static NS_HIDDEN_(void) Shutdown();
michael@0 340
michael@0 341 private:
michael@0 342 friend struct IPC::ParamTraits<mozilla::TimeStamp>;
michael@0 343 friend void StartupTimelineRecordExternal(int, uint64_t);
michael@0 344
michael@0 345 TimeStamp(TimeStampValue aValue) : mValue(aValue) {}
michael@0 346
michael@0 347 static TimeStamp Now(bool aHighResolution);
michael@0 348
michael@0 349 /**
michael@0 350 * Computes the uptime of the current process in microseconds. The result
michael@0 351 * is platform-dependent and needs to be checked against existing timestamps
michael@0 352 * for consistency.
michael@0 353 *
michael@0 354 * @returns The number of microseconds since the calling process was started
michael@0 355 * or 0 if an error was encountered while computing the uptime
michael@0 356 */
michael@0 357 static uint64_t ComputeProcessUptime();
michael@0 358
michael@0 359 /**
michael@0 360 * When built with PRIntervalTime, a value of 0 means this instance
michael@0 361 * is "null". Otherwise, the low 32 bits represent a PRIntervalTime,
michael@0 362 * and the high 32 bits represent a counter of the number of
michael@0 363 * rollovers of PRIntervalTime that we've seen. This counter starts
michael@0 364 * at 1 to avoid a real time colliding with the "null" value.
michael@0 365 *
michael@0 366 * PR_INTERVAL_MAX is set at 100,000 ticks per second. So the minimum
michael@0 367 * time to wrap around is about 2^64/100000 seconds, i.e. about
michael@0 368 * 5,849,424 years.
michael@0 369 *
michael@0 370 * When using a system clock, a value is system dependent.
michael@0 371 */
michael@0 372 TimeStampValue mValue;
michael@0 373 };
michael@0 374
michael@0 375 }
michael@0 376
michael@0 377 #endif /* mozilla_TimeStamp_h */

mercurial