TDengine/source/util/test/timerTest.cpp

277 lines
7.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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);
}