diff --git a/README.md b/README.md index 806877f..1686c52 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ ## 版本 ``` -v1| 初始化 -V1.1| 修改默认日志配置文件 +V1.0.1 +修复用户将系统时间修改到过去, 导致出现部分逻辑假死的现象 ``` diff --git a/src/components/thread/signal.cpp b/src/components/thread/signal.cpp index 59b83a2..667fffd 100644 --- a/src/components/thread/signal.cpp +++ b/src/components/thread/signal.cpp @@ -2,35 +2,78 @@ namespace iflytop { namespace core { using namespace std; + +static struct timespec add_microseconds(struct timespec ts, long microseconds) { + // 将微秒转换为秒和纳秒 + long extra_sec = microseconds / 1000000L; + long extra_nsec = (microseconds % 1000000L) * 1000L; // 微秒转纳秒 + + // 加到原时间上 + ts.tv_sec += extra_sec; + ts.tv_nsec += extra_nsec; + + // 处理纳秒进位 + if (ts.tv_nsec >= 1000000000L) { + ts.tv_sec += ts.tv_nsec / 1000000000L; + ts.tv_nsec = ts.tv_nsec % 1000000000L; + } + + return ts; +} + +Singal::Singal() { + // Set clock to monotonic + pthread_condattr_init(&m_attr); + pthread_condattr_setclock(&m_attr, CLOCK_MONOTONIC); + pthread_cond_init(&m_cond, &m_attr); +} + bool Singal::sleep_for_us(int64_t us) { - std::unique_lock lck(mtx_); + pthread_mutex_lock(&m_mutex); if (signalNum != 0) { signalNum--; + pthread_mutex_unlock(&m_mutex); return true; } - if (us < 0) return true; - if (cv_.wait_for(lck, std::chrono::microseconds(us)) == - std::cv_status::timeout) { - if (signalNum != 0) signalNum--; + if (us < 0) { + pthread_mutex_unlock(&m_mutex); return true; } + + // Wait on data + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + ts = add_microseconds(ts, us); + + int rc = pthread_cond_timedwait(&m_cond, &m_mutex, &ts); + if (rc == ETIMEDOUT) { + pthread_mutex_unlock(&m_mutex); + return true; + } + if (signalNum != 0) signalNum--; + pthread_mutex_unlock(&m_mutex); return false; }; bool Singal::sleep() { - std::unique_lock lck(mtx_); + pthread_mutex_lock(&m_mutex); if (signalNum != 0) { signalNum--; + pthread_mutex_unlock(&m_mutex); return true; } - cv_.wait(lck); + pthread_cond_wait(&m_cond, &m_mutex); if (signalNum != 0) signalNum--; + + pthread_mutex_unlock(&m_mutex); return true; } void Singal::notify() { - std::unique_lock lck(mtx_); - cv_.notify_all(); + pthread_mutex_lock(&m_mutex); signalNum++; + pthread_mutex_unlock(&m_mutex); + + pthread_cond_broadcast(&m_cond); } -} -} // namespace zwsd \ No newline at end of file +} // namespace core +} // namespace iflytop \ No newline at end of file diff --git a/src/components/thread/signal.hpp b/src/components/thread/signal.hpp index 8b8b1fa..ec32b9b 100644 --- a/src/components/thread/signal.hpp +++ b/src/components/thread/signal.hpp @@ -17,15 +17,18 @@ namespace iflytop { namespace core { using namespace std; class Singal { - condition_variable cv_; - std::mutex mtx_; atomic signalNum = {0}; + pthread_mutex_t m_mutex = PTHREAD_MUTEX_INITIALIZER; + pthread_cond_t m_cond; + pthread_condattr_t m_attr; + public: + Singal(); bool sleep_for_us(int64_t us); bool sleep(); void notify(); }; -} -} // namespace zwsd \ No newline at end of file +} // namespace core +} // namespace iflytop \ No newline at end of file diff --git a/src/components/thread/signal_test.cpp b/src/components/thread/signal_test.cpp new file mode 100644 index 0000000..f0f8ead --- /dev/null +++ b/src/components/thread/signal_test.cpp @@ -0,0 +1,208 @@ + + +#ifdef SIGNAL_TEST +#include "signal.hpp" + +#include + +#include +#include + +using namespace iflytop; +using namespace core; +using namespace std; +using namespace std::chrono; + +int64_t getnow() { return duration_cast(steady_clock::now().time_since_epoch()).count(); } + +#define TITLE(title) \ + printf("========================================\n"); \ + printf("= %s\n", title); + +#define LOG(str, ...) printf("= " str "\n", ##__VA_ARGS__); +#define CTR() printf("=\n"); +#define END() printf("=\n"); + +#define CHECK(passed) \ + if (!(passed)) { \ + printf("= check %s failed\n", #passed); \ + exit(1); \ + } else { \ + printf("= check %s passed\n", #passed); \ + } + +#define CHECK_EQ(a, b, maxdiff) \ + if (abs((a) - (b)) > (maxdiff)) { \ + printf("= check %s == %s failed\n", #a, #b); \ + exit(1); \ + } else { \ + printf("= check %s == %s passed\n", #a, #b); \ + } + +int main(int argc, char const *argv[]) { + /* code */ + + { + TITLE("=test sleep_for_us cost 1500000 us"); + + Singal signal; + int64_t now = getnow(); + signal.sleep_for_us(1500 * 1000); + int64_t after = getnow(); + int64_t real = after - now; + + LOG("real:%ld, diff:%ld ms ", real, (real - 1500000) / 1000); + + CTR(); + CHECK(((real - 1500000) / 1000) < 5); + + END(); + } + printf("\n"); + + { + TITLE("test wake up \n"); + Singal signal; + + int64_t start, callwakeup, wakeup, jointime; + + thread t1([&]() { + LOG("thread 1 start sleep"); + signal.sleep(); + wakeup = getnow(); + LOG("thread 1 wake up"); + }); + + start = getnow(); + usleep(1000 * 1000); // 等待线程1开始睡眠 + LOG("thread 2 start notify"); + callwakeup = getnow(); + signal.notify(); + t1.join(); + jointime = getnow(); + + int64_t notify_delay = wakeup - callwakeup; + int64_t sleep_time = wakeup - start; + + LOG("thread 2 notify cost %ld us, thread 1 sleep %ld ms", notify_delay, sleep_time / 1000); + LOG("thread 2 join cost %ld ms", (jointime - callwakeup) / 1000); + + CTR(); + CHECK(notify_delay < 1000); // 通知延迟应该小于30us + CHECK(abs(sleep_time - 1000 * 1000) < 3000); // 睡眠时间应该大于1秒 + CTR(); + } + printf("\n"); + + { + TITLE("test wake up 2"); + Singal signal; + + int64_t start, callwakeup, wakeup, jointime; + + thread t1([&]() { + LOG("thread 1 start sleep"); + signal.sleep_for_us(10 * 1000 * 1000); + wakeup = getnow(); + LOG("thread 1 wake up"); + }); + + start = getnow(); + usleep(1000 * 1000); // 等待线程1开始睡眠 + LOG("thread 2 start notify"); + callwakeup = getnow(); + signal.notify(); + t1.join(); + jointime = getnow(); + + int64_t notify_delay = wakeup - callwakeup; + int64_t sleep_time = wakeup - start; + + LOG("thread 2 notify cost %ld us, thread 1 sleep %ld ms", notify_delay, sleep_time / 1000); + LOG("thread 2 join cost %ld ms", (jointime - callwakeup) / 1000); + + CTR(); + CHECK_EQ(notify_delay, 0, 1000); + CHECK_EQ(sleep_time / 1000, 1000, 2); + CTR(); + } + printf("\n"); + + { + TITLE("test wake up twice"); + Singal signal; + + int64_t start, callwakeup, wakeup, jointime; + + thread t1([&]() { + LOG("thread 1 start sleep"); + signal.sleep_for_us(3 * 1000 * 1000); + signal.sleep_for_us(3 * 1000 * 1000); + wakeup = getnow(); + LOG("thread 1 wake up"); + }); + + start = getnow(); + usleep(1000 * 1000); // 等待线程1开始睡眠 + LOG("thread 2 start notify"); + callwakeup = getnow(); + signal.notify(); + signal.notify(); + t1.join(); + jointime = getnow(); + + int64_t notify_delay = wakeup - callwakeup; + int64_t sleep_time = wakeup - start; + + LOG("thread 2 notify cost %ld us, thread 1 sleep %ld ms", notify_delay, sleep_time / 1000); + LOG("thread 2 join cost %ld ms", (jointime - callwakeup) / 1000); + + CTR(); + CHECK_EQ(notify_delay, 0, 1000); + CHECK_EQ(sleep_time / 1000, 1000, 2); + CTR(); + } + printf("\n"); + + { + TITLE("test wake up twice 2"); + Singal signal; + + int64_t start, callwakeup, wakeup, jointime; + + thread t1([&]() { + LOG("thread 1 start sleep"); + signal.sleep_for_us(3 * 1000 * 1000); + signal.sleep_for_us(3 * 1000 * 1000); + wakeup = getnow(); + LOG("thread 1 wake up"); + }); + + start = getnow(); + usleep(1000 * 1000); // 等待线程1开始睡眠 + LOG("thread 2 start notify"); + callwakeup = getnow(); + signal.notify(); + t1.join(); + jointime = getnow(); + + int64_t notify_delay = wakeup - callwakeup; + int64_t sleep_time = wakeup - start; + + LOG("thread 2 notify cost %ld us, thread 1 sleep %ld ms", notify_delay, sleep_time / 1000); + LOG("thread 2 join cost %ld ms", (jointime - callwakeup) / 1000); + + CTR(); + CHECK_EQ(notify_delay, 3000000, 1000); // 通知延迟应该等于3秒 + CHECK_EQ(sleep_time / 1000, 4000, 2); // 睡眠时间应该等于4秒 + CTR(); + } + printf("\n"); + + printf("========================================\n"); + printf("= All tests passed\n"); + printf("========================================\n"); + + return 0; +} +#endif diff --git a/src/configs/version.hpp b/src/configs/version.hpp index 674d705..e6486a7 100644 --- a/src/configs/version.hpp +++ b/src/configs/version.hpp @@ -1,2 +1,2 @@ #pragma once -#define VERSION "1.0.0" \ No newline at end of file +#define VERSION "1.0.1" \ No newline at end of file