Thu, 22 Jan 2015 13:21:57 +0100
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 */ |