Files
tdesktop/Telegram/lib_base/base/unixtime.cpp
allhaileris afb81b8278
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
init
2026-02-16 15:50:16 +03:00

208 lines
4.4 KiB
C++

// 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