monotonic.cpp 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. #include "monotonic.h"
  2. #include <chrono>
  3. #include <optional>
  4. #include <system_error>
  5. #include <util/system/platform.h>
  6. #ifdef _linux_
  7. #include <time.h>
  8. #include <string.h>
  9. #endif
  10. namespace NMonotonic {
  11. namespace {
  12. #if defined(_linux_) && defined(CLOCK_BOOTTIME)
  13. std::optional<ui64> GetClockBootTimeMicroSeconds() {
  14. struct timespec t;
  15. std::optional<ui64> r;
  16. if (0 == ::clock_gettime(CLOCK_BOOTTIME, &t)) {
  17. r.emplace(t.tv_nsec / 1000ULL + t.tv_sec * 1000000ULL);
  18. }
  19. return r;
  20. }
  21. #endif
  22. struct TMonotonicSupport {
  23. #if defined(_linux_) && defined(CLOCK_BOOTTIME)
  24. // We remember initial offset to measure time relative to program
  25. // start and so we never return a zero time.
  26. std::optional<ui64> BootTimeOffset;
  27. #endif
  28. // Unfortunately time_since_epoch() is sometimes negative on wine
  29. // Remember initial time point at program start and use offsets from that
  30. std::chrono::steady_clock::time_point SteadyClockOffset;
  31. TMonotonicSupport() {
  32. #if defined(_linux_) && defined(CLOCK_BOOTTIME)
  33. BootTimeOffset = GetClockBootTimeMicroSeconds();
  34. #endif
  35. SteadyClockOffset = std::chrono::steady_clock::now();
  36. }
  37. ui64 GetMicroSeconds() const {
  38. #if defined(_linux_) && defined(CLOCK_BOOTTIME)
  39. if (Y_LIKELY(BootTimeOffset)) {
  40. auto r = GetClockBootTimeMicroSeconds();
  41. if (Y_UNLIKELY(!r)) {
  42. throw std::system_error(
  43. std::error_code(errno, std::system_category()),
  44. "clock_gettime(CLOCK_BOOTTIME) failed");
  45. }
  46. // Note: we add 1 so we never return zero
  47. return *r - *BootTimeOffset + 1;
  48. }
  49. #endif
  50. auto elapsed = std::chrono::steady_clock::now() - SteadyClockOffset;
  51. auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
  52. // Steady clock is supposed to never jump backwards, but it's
  53. // better to be safe in case of buggy implementations
  54. if (Y_UNLIKELY(microseconds < 0)) {
  55. microseconds = 0;
  56. }
  57. // Note: we add 1 so we never return zero
  58. return ui64(microseconds) + 1;
  59. }
  60. };
  61. TMonotonicSupport MonotonicSupport;
  62. }
  63. ui64 GetMonotonicMicroSeconds() {
  64. return MonotonicSupport.GetMicroSeconds();
  65. }
  66. }
  67. template <>
  68. void Out<NMonotonic::TMonotonic>(
  69. IOutputStream& o,
  70. NMonotonic::TMonotonic t)
  71. {
  72. o << t - NMonotonic::TMonotonic::Zero();
  73. }