unixtime.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include "base/unixtime.h"
  8. #include <QDateTime>
  9. #include <QReadWriteLock>
  10. #ifdef Q_OS_WIN
  11. #include <windows.h>
  12. #elif defined Q_OS_MAC
  13. #include <mach/mach_time.h>
  14. #else
  15. #include <time.h>
  16. #endif
  17. namespace base {
  18. namespace unixtime {
  19. namespace {
  20. constexpr auto kIgnoreTimeDifference = TimeId(3);
  21. std::atomic<bool> ValueUpdated/* = false*/;
  22. std::atomic<TimeId> ValueShift/* = 0*/;
  23. std::atomic<bool> HttpValueValid/* = false*/;
  24. std::atomic<TimeId> HttpValueShift/* = 0*/;
  25. class MsgIdManager {
  26. public:
  27. MsgIdManager();
  28. void update();
  29. [[nodiscard]] uint64 next();
  30. private:
  31. void initialize();
  32. QReadWriteLock _lock;
  33. uint64 _startId = 0;
  34. std::atomic<uint32> _incrementedPart = 0;
  35. uint64 _startCounter = 0;
  36. const uint64 _randomPart = 0;
  37. const float64 _multiplier = 0.;
  38. };
  39. MsgIdManager GlobalMsgIdManager;
  40. [[nodiscard]] float64 GetMultiplier() {
  41. // 0xFFFF0000 instead of 0x100000000 to make msgId grow slightly slower,
  42. // than unixtime and we had time to reconfigure.
  43. #ifdef Q_OS_WIN
  44. LARGE_INTEGER li;
  45. QueryPerformanceFrequency(&li);
  46. return float64(0xFFFF0000L) / float64(li.QuadPart);
  47. #elif defined Q_OS_MAC // Q_OS_WIN
  48. mach_timebase_info_data_t tb = { 0, 0 };
  49. mach_timebase_info(&tb);
  50. const auto frequency = (float64(tb.numer) / tb.denom) / 1000000.;
  51. return frequency * (float64(0xFFFF0000L) / 1000.);
  52. #else // Q_OS_MAC || Q_OS_WIN
  53. return float64(0xFFFF0000L) / 1000000000.;
  54. #endif // Q_OS_MAC || Q_OS_WIN
  55. }
  56. [[nodiscard]] uint64 GetCounter() {
  57. #ifdef Q_OS_WIN
  58. LARGE_INTEGER li;
  59. QueryPerformanceCounter(&li);
  60. return li.QuadPart;
  61. #elif defined Q_OS_MAC // Q_OS_WIN
  62. return mach_absolute_time();
  63. #else // Q_OS_MAC || Q_OS_WIN
  64. timespec ts;
  65. clock_gettime(CLOCK_MONOTONIC, &ts);
  66. return 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec);
  67. #endif // Q_OS_MAC || Q_OS_WIN
  68. }
  69. [[nodiscard]] uint32 PrepareRandomPart() {
  70. auto generator = std::mt19937(std::random_device()());
  71. auto distribution = std::uniform_int_distribution<uint32>();
  72. return distribution(generator);
  73. }
  74. MsgIdManager::MsgIdManager()
  75. : _randomPart(PrepareRandomPart())
  76. , _multiplier(GetMultiplier()) {
  77. initialize();
  78. srand(uint32(_startCounter & 0xFFFFFFFFUL));
  79. }
  80. void MsgIdManager::update() {
  81. QWriteLocker lock(&_lock);
  82. initialize();
  83. }
  84. void MsgIdManager::initialize() {
  85. _startCounter = GetCounter();
  86. _startId = ((uint64(uint32(now()))) << 32) | _randomPart;
  87. }
  88. uint64 MsgIdManager::next() {
  89. const auto counter = GetCounter();
  90. QReadLocker lock(&_lock);
  91. const auto startCounter = _startCounter;
  92. const auto startId = _startId;
  93. lock.unlock();
  94. const auto incrementedPart = (_incrementedPart += 4);
  95. const auto delta = (counter - startCounter);
  96. const auto result = startId + (uint64)floor(delta * _multiplier);
  97. return (result & ~0x03L) + incrementedPart;
  98. }
  99. TimeId local() {
  100. return (TimeId)time(nullptr);
  101. }
  102. rpl::event_stream<> &UpdatesStream() {
  103. static auto result = rpl::event_stream<>();
  104. return result;
  105. }
  106. } // namespace
  107. TimeId now() {
  108. return local() + ValueShift.load();
  109. }
  110. void update(TimeId now, bool force) {
  111. if (force) {
  112. ValueUpdated = true;
  113. } else if (ValueUpdated) {
  114. return;
  115. }
  116. const auto shift = now - local();
  117. const auto ignore = !force
  118. && (std::abs(shift - ValueShift) < kIgnoreTimeDifference);
  119. if (ignore) {
  120. return;
  121. } else if (!force) {
  122. auto expected = false;
  123. if (!ValueUpdated.compare_exchange_strong(expected, true)) {
  124. return;
  125. }
  126. }
  127. const auto old = ValueShift.exchange(shift);
  128. HttpValueShift = 0;
  129. HttpValueValid = false;
  130. if (old != shift) {
  131. GlobalMsgIdManager.update();
  132. crl::on_main([] {
  133. UpdatesStream().fire({});
  134. });
  135. }
  136. }
  137. rpl::producer<> updates() {
  138. return UpdatesStream().events();
  139. }
  140. QDateTime parse(TimeId value) {
  141. return (value > 0)
  142. ? QDateTime::fromSecsSinceEpoch(value - ValueShift)
  143. : QDateTime();
  144. }
  145. TimeId serialize(const QDateTime &date) {
  146. return date.isNull() ? TimeId(0) : date.toSecsSinceEpoch() + ValueShift;
  147. }
  148. bool http_valid() {
  149. return HttpValueValid;
  150. }
  151. TimeId http_now() {
  152. return now() + HttpValueShift;
  153. }
  154. void http_update(TimeId now) {
  155. HttpValueShift = now - base::unixtime::now();
  156. HttpValueValid = true;
  157. }
  158. void http_invalidate() {
  159. HttpValueValid = false;
  160. }
  161. uint64 mtproto_msg_id() {
  162. return GlobalMsgIdManager.next();
  163. }
  164. } // namespace unixtime
  165. } // namespace base