mirror of
https://github.com/taosdata/TDengine
synced 2026-05-24 10:09:01 +00:00
277 lines
7.7 KiB
C++
277 lines
7.7 KiB
C++
/*
|
||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||
*
|
||
* This program is free software: you can use, redistribute, and/or modify
|
||
* it under the terms of the GNU Affero General Public License, version 3
|
||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||
*
|
||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||
*
|
||
* You should have received a copy of the GNU Affero General Public License
|
||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include <iostream>
|
||
#define ALLOW_FORBID_FUNC
|
||
#if defined(_WIN32) || defined(_WIN64)
|
||
// Use WIN32_LEAN_AND_MEAN to avoid pulling in winsock.h; include winsock2 first
|
||
#ifndef WIN32_LEAN_AND_MEAN
|
||
#define WIN32_LEAN_AND_MEAN
|
||
#endif
|
||
#include <winsock2.h>
|
||
#include <ws2tcpip.h>
|
||
#include <windows.h>
|
||
#endif
|
||
#include <gtest/gtest.h>
|
||
|
||
#if defined(_WIN32) || defined(_WIN64)
|
||
int gettimeofday(struct timeval* tp, void* tzp) {
|
||
FILETIME ft;
|
||
unsigned __int64 tmpres = 0;
|
||
static int tzflag = 0;
|
||
|
||
if (NULL != tp) {
|
||
GetSystemTimeAsFileTime(&ft);
|
||
|
||
tmpres |= ((unsigned __int64)ft.dwHighDateTime);
|
||
tmpres <<= 32;
|
||
tmpres |= ft.dwLowDateTime;
|
||
|
||
// Convert into microseconds
|
||
tmpres /= 10;
|
||
|
||
// Convert into seconds and microseconds
|
||
tmpres -= 11644473600000000ULL;
|
||
tp->tv_sec = (long)(tmpres / 1000000UL);
|
||
tp->tv_usec = (long)(tmpres % 1000000UL);
|
||
}
|
||
return 0;
|
||
}
|
||
#else
|
||
#include <sys/time.h>
|
||
#endif
|
||
#include <atomic>
|
||
#include <mutex>
|
||
#include <condition_variable>
|
||
#include <chrono>
|
||
#include <thread>
|
||
|
||
#include "os.h"
|
||
#include "tlog.h"
|
||
#include "ttimer.h"
|
||
|
||
// 调整系统时间的辅助函数,返回是否有权限
|
||
// adjust system time helper. On Windows we don't attempt to change system time here
|
||
// (would require elevated privileges and different API). Return false so tests
|
||
// that require changing system time will be skipped on Windows.
|
||
#if defined(_WIN32) || defined(_WIN64)
|
||
static bool adjustSystemTime(int64_t offsetMs) {
|
||
// Try to enable SeSystemtimePrivilege to set system time
|
||
HANDLE hToken = NULL;
|
||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
|
||
return false;
|
||
}
|
||
|
||
TOKEN_PRIVILEGES tp;
|
||
LUID luid;
|
||
if (!LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &luid)) {
|
||
CloseHandle(hToken);
|
||
return false;
|
||
}
|
||
|
||
tp.PrivilegeCount = 1;
|
||
tp.Privileges[0].Luid = luid;
|
||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||
|
||
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL)) {
|
||
CloseHandle(hToken);
|
||
return false;
|
||
}
|
||
|
||
if (GetLastError() != ERROR_SUCCESS) {
|
||
// privilege not enabled
|
||
CloseHandle(hToken);
|
||
return false;
|
||
}
|
||
|
||
// Get current system time (UTC)
|
||
SYSTEMTIME stUtc;
|
||
GetSystemTime(&stUtc);
|
||
|
||
// Convert SYSTEMTIME -> FILETIME -> 64-bit value
|
||
FILETIME ft;
|
||
if (!SystemTimeToFileTime(&stUtc, &ft)) {
|
||
// revoke privilege
|
||
tp.Privileges[0].Attributes = 0;
|
||
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
|
||
CloseHandle(hToken);
|
||
return false;
|
||
}
|
||
|
||
unsigned __int64 time100ns = ((unsigned __int64)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
|
||
// offsetMs to 100-ns units
|
||
unsigned __int64 delta100ns = (unsigned __int64)offsetMs * 10000ULL;
|
||
time100ns += delta100ns;
|
||
|
||
FILETIME ftNew;
|
||
ftNew.dwLowDateTime = (DWORD)(time100ns & 0xFFFFFFFF);
|
||
ftNew.dwHighDateTime = (DWORD)(time100ns >> 32);
|
||
|
||
SYSTEMTIME stNew;
|
||
if (!FileTimeToSystemTime(&ftNew, &stNew)) {
|
||
tp.Privileges[0].Attributes = 0;
|
||
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
|
||
CloseHandle(hToken);
|
||
return false;
|
||
}
|
||
|
||
// Set system time (UTC)
|
||
BOOL ok = SetSystemTime(&stNew);
|
||
|
||
// revoke privilege
|
||
tp.Privileges[0].Attributes = 0;
|
||
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
|
||
CloseHandle(hToken);
|
||
|
||
return ok != 0;
|
||
}
|
||
#else
|
||
static bool adjustSystemTime(int64_t offsetMs) {
|
||
struct timeval tv;
|
||
if (gettimeofday(&tv, NULL) != 0) return false;
|
||
|
||
int64_t us = (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec + offsetMs * 1000;
|
||
tv.tv_sec = us / 1000000;
|
||
tv.tv_usec = us % 1000000;
|
||
|
||
return settimeofday(&tv, NULL) == 0;
|
||
}
|
||
#endif
|
||
|
||
// ============================================================
|
||
// 测试 1:系统时间向前跳(+1 小时),timer 应在预期时间内触发
|
||
// ============================================================
|
||
TEST(timerTest, TimerCorrectWhenTimeJumpForward) {
|
||
void* ctrl = taosTmrInit(100, 10, 10000, "timerTest");
|
||
ASSERT_NE(ctrl, nullptr);
|
||
|
||
struct WaitFlag {
|
||
std::mutex m;
|
||
std::condition_variable cv;
|
||
bool fired{false};
|
||
int64_t fireMonoMs{0};
|
||
} waitFlag;
|
||
int64_t startMonoMs = taosGetMonotonicMs();
|
||
const int32_t delayMs = 500;
|
||
|
||
// 启动一个 500ms 后触发的 timer
|
||
taosTmrStart(
|
||
[](void* p, tmr_h id) {
|
||
auto* wf = static_cast<WaitFlag*>(p);
|
||
{
|
||
std::lock_guard<std::mutex> lg(wf->m);
|
||
wf->fireMonoMs = taosGetMonotonicMs();
|
||
wf->fired = true;
|
||
}
|
||
wf->cv.notify_one();
|
||
GTEST_LOG_(INFO) << "timer fired";
|
||
},
|
||
delayMs, &waitFlag, ctrl);
|
||
|
||
// timer 启动后立即将系统时间向前拨 1 小时
|
||
bool hasPrivilege = adjustSystemTime(3600LL * 1000);
|
||
if (!hasPrivilege) {
|
||
GTEST_LOG_(WARNING) << "adjustSystemTime failed (no privilege?) - continuing test anyway";
|
||
}
|
||
|
||
// 等待最多 3 秒(轮询以兼容老旧编译器)
|
||
int waited = 0;
|
||
bool signaled = false;
|
||
while (waited < 3000) {
|
||
{
|
||
std::lock_guard<std::mutex> lg(waitFlag.m);
|
||
if (waitFlag.fired) {
|
||
signaled = true;
|
||
break;
|
||
}
|
||
}
|
||
taosMsleep(10);
|
||
waited += 10;
|
||
}
|
||
if (!signaled) {
|
||
GTEST_FAIL() << "timer not fired";
|
||
}
|
||
if (signaled) {
|
||
int64_t elapsed = waitFlag.fireMonoMs - startMonoMs;
|
||
GTEST_LOG_(INFO) << "timer fired, elapsed=" << elapsed << "ms";
|
||
}
|
||
|
||
// 时间还原
|
||
adjustSystemTime(-3600LL * 1000);
|
||
|
||
taosTmrCleanUp(ctrl);
|
||
}
|
||
|
||
// ============================================================
|
||
// 测试 2:系统时间向后跳(-1 小时),timer 应在预期时间内触发
|
||
// ============================================================
|
||
TEST(timerTest, TimerCorrectWhenTimeJumpBackward) {
|
||
void* ctrl = taosTmrInit(100, 10, 10000, "timerTest");
|
||
ASSERT_NE(ctrl, nullptr);
|
||
|
||
struct WaitFlag2 {
|
||
std::mutex m;
|
||
std::condition_variable cv;
|
||
bool fired{false};
|
||
int64_t fireMonoMs{0};
|
||
} waitFlag2;
|
||
int64_t startMonoMs = taosGetMonotonicMs();
|
||
const int32_t delayMs = 500;
|
||
|
||
taosTmrStart(
|
||
[](void* p, tmr_h id) {
|
||
auto* wf = static_cast<WaitFlag2*>(p);
|
||
{
|
||
std::lock_guard<std::mutex> lg(wf->m);
|
||
wf->fireMonoMs = taosGetMonotonicMs();
|
||
wf->fired = true;
|
||
}
|
||
wf->cv.notify_one();
|
||
GTEST_LOG_(INFO) << "timer fired";
|
||
},
|
||
delayMs, &waitFlag2, ctrl);
|
||
|
||
// timer 启动后立即将系统时间向后拨 1 小时
|
||
bool hasPrivilege = adjustSystemTime(-3600LL * 1000);
|
||
if (!hasPrivilege) {
|
||
GTEST_LOG_(WARNING) << "adjustSystemTime failed (no privilege?) - continuing test anyway";
|
||
}
|
||
|
||
// 等待最多 3 秒(轮询以兼容老旧编译器)
|
||
int waited2 = 0;
|
||
bool signaled2 = false;
|
||
while (waited2 < 3000) {
|
||
{
|
||
std::lock_guard<std::mutex> lg(waitFlag2.m);
|
||
if (waitFlag2.fired) {
|
||
signaled2 = true;
|
||
break;
|
||
}
|
||
}
|
||
taosMsleep(10);
|
||
waited2 += 10;
|
||
}
|
||
if (!signaled2) {
|
||
GTEST_FAIL() << "timer not fired";
|
||
}
|
||
if (signaled2) {
|
||
int64_t elapsed = waitFlag2.fireMonoMs - startMonoMs;
|
||
GTEST_LOG_(INFO) << "timer fired, elapsed=" << elapsed << "ms";
|
||
}
|
||
|
||
// 时间还原
|
||
adjustSystemTime(3600LL * 1000);
|
||
taosTmrCleanUp(ctrl);
|
||
}
|