| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- // This file is part of Desktop App Toolkit,
- // a set of libraries for developing nice desktop applications.
- //
- // For license and copyright information please follow this link:
- // https://github.com/desktop-app/legal/blob/master/LEGAL
- //
- #include "base/unixtime.h"
- #include <QDateTime>
- #include <QReadWriteLock>
- #ifdef Q_OS_WIN
- #include <windows.h>
- #elif defined Q_OS_MAC
- #include <mach/mach_time.h>
- #else
- #include <time.h>
- #endif
- namespace base {
- namespace unixtime {
- namespace {
- constexpr auto kIgnoreTimeDifference = TimeId(3);
- std::atomic<bool> ValueUpdated/* = false*/;
- std::atomic<TimeId> ValueShift/* = 0*/;
- std::atomic<bool> HttpValueValid/* = false*/;
- std::atomic<TimeId> HttpValueShift/* = 0*/;
- class MsgIdManager {
- public:
- MsgIdManager();
- void update();
- [[nodiscard]] uint64 next();
- private:
- void initialize();
- QReadWriteLock _lock;
- uint64 _startId = 0;
- std::atomic<uint32> _incrementedPart = 0;
- uint64 _startCounter = 0;
- const uint64 _randomPart = 0;
- const float64 _multiplier = 0.;
- };
- MsgIdManager GlobalMsgIdManager;
- [[nodiscard]] float64 GetMultiplier() {
- // 0xFFFF0000 instead of 0x100000000 to make msgId grow slightly slower,
- // than unixtime and we had time to reconfigure.
- #ifdef Q_OS_WIN
- LARGE_INTEGER li;
- QueryPerformanceFrequency(&li);
- return float64(0xFFFF0000L) / float64(li.QuadPart);
- #elif defined Q_OS_MAC // Q_OS_WIN
- mach_timebase_info_data_t tb = { 0, 0 };
- mach_timebase_info(&tb);
- const auto frequency = (float64(tb.numer) / tb.denom) / 1000000.;
- return frequency * (float64(0xFFFF0000L) / 1000.);
- #else // Q_OS_MAC || Q_OS_WIN
- return float64(0xFFFF0000L) / 1000000000.;
- #endif // Q_OS_MAC || Q_OS_WIN
- }
- [[nodiscard]] uint64 GetCounter() {
- #ifdef Q_OS_WIN
- LARGE_INTEGER li;
- QueryPerformanceCounter(&li);
- return li.QuadPart;
- #elif defined Q_OS_MAC // Q_OS_WIN
- return mach_absolute_time();
- #else // Q_OS_MAC || Q_OS_WIN
- timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec);
- #endif // Q_OS_MAC || Q_OS_WIN
- }
- [[nodiscard]] uint32 PrepareRandomPart() {
- auto generator = std::mt19937(std::random_device()());
- auto distribution = std::uniform_int_distribution<uint32>();
- return distribution(generator);
- }
- MsgIdManager::MsgIdManager()
- : _randomPart(PrepareRandomPart())
- , _multiplier(GetMultiplier()) {
- initialize();
- srand(uint32(_startCounter & 0xFFFFFFFFUL));
- }
- void MsgIdManager::update() {
- QWriteLocker lock(&_lock);
- initialize();
- }
- void MsgIdManager::initialize() {
- _startCounter = GetCounter();
- _startId = ((uint64(uint32(now()))) << 32) | _randomPart;
- }
- uint64 MsgIdManager::next() {
- const auto counter = GetCounter();
- QReadLocker lock(&_lock);
- const auto startCounter = _startCounter;
- const auto startId = _startId;
- lock.unlock();
- const auto incrementedPart = (_incrementedPart += 4);
- const auto delta = (counter - startCounter);
- const auto result = startId + (uint64)floor(delta * _multiplier);
- return (result & ~0x03L) + incrementedPart;
- }
- TimeId local() {
- return (TimeId)time(nullptr);
- }
- rpl::event_stream<> &UpdatesStream() {
- static auto result = rpl::event_stream<>();
- return result;
- }
- } // namespace
- TimeId now() {
- return local() + ValueShift.load();
- }
- void update(TimeId now, bool force) {
- if (force) {
- ValueUpdated = true;
- } else if (ValueUpdated) {
- return;
- }
- const auto shift = now - local();
- const auto ignore = !force
- && (std::abs(shift - ValueShift) < kIgnoreTimeDifference);
- if (ignore) {
- return;
- } else if (!force) {
- auto expected = false;
- if (!ValueUpdated.compare_exchange_strong(expected, true)) {
- return;
- }
- }
- const auto old = ValueShift.exchange(shift);
- HttpValueShift = 0;
- HttpValueValid = false;
- if (old != shift) {
- GlobalMsgIdManager.update();
- crl::on_main([] {
- UpdatesStream().fire({});
- });
- }
- }
- rpl::producer<> updates() {
- return UpdatesStream().events();
- }
- QDateTime parse(TimeId value) {
- return (value > 0)
- ? QDateTime::fromSecsSinceEpoch(value - ValueShift)
- : QDateTime();
- }
- TimeId serialize(const QDateTime &date) {
- return date.isNull() ? TimeId(0) : date.toSecsSinceEpoch() + ValueShift;
- }
- bool http_valid() {
- return HttpValueValid;
- }
- TimeId http_now() {
- return now() + HttpValueShift;
- }
- void http_update(TimeId now) {
- HttpValueShift = now - base::unixtime::now();
- HttpValueValid = true;
- }
- void http_invalidate() {
- HttpValueValid = false;
- }
- uint64 mtproto_msg_id() {
- return GlobalMsgIdManager.next();
- }
- } // namespace unixtime
- } // namespace base
|