From c1ffc94f82778e50a6f379042093311b078317b6 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 10 Nov 2022 15:59:37 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E6=B7=BB=E5=8A=A0LoopWDog=E7=B1=BB?= =?UTF-8?q?=EF=BC=8C=E6=9C=AA=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/eventx/loop_wdog.cpp | 161 +++++++++++++++++++++++++++++++++++ modules/eventx/loop_wdog.h | 32 +++++++ 2 files changed, 193 insertions(+) create mode 100644 modules/eventx/loop_wdog.cpp create mode 100644 modules/eventx/loop_wdog.h diff --git a/modules/eventx/loop_wdog.cpp b/modules/eventx/loop_wdog.cpp new file mode 100644 index 0000000..a2b22a0 --- /dev/null +++ b/modules/eventx/loop_wdog.cpp @@ -0,0 +1,161 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "thread_wdog.h" +#include + +namespace tbox { +namespace util { + +namespace { + +void OnLoopDie(pid_t tid, const std::string &name); + +class LoopInfo { + public: + void initialize(const std::string &name, uint16_t sec) + { + thread_name_ = name; + timeout_sec_ = sec; + remain_sec_ = sec; + } + + void resetRemainSec() + { + remain_sec_ = timeout_sec_; + } + + bool decRemainSecAndCheck() + { + if (remain_sec_ > 0) { + --remain_sec_; + return false; + } else { + return true; + } + } + + std::string getName() const { return thread_name_; } + + private: + std::string thread_name_; //! 线程名 + uint16_t timeout_sec_ = 0; //! 线程超时时间长度 + uint16_t remain_sec_ = 0; //! 剩余时间长度 +}; + +using PidToLoopInfo = std::map >; + +PidToLoopInfo pid_to_thread_info; //! pid --> 线程信息表 +std::mutex pid_to_thread_info_mutex; //! 锁 +std::shared_ptr sp_thread; //! 线程对象 +bool keep_running = false; //! 线程是否继续工作标记 +LoopWDog::LoopDieCallback thread_die_cb = OnLoopDie; //! 回调函数 + +pid_t CurrentLoopID() +{ + return ::syscall(SYS_gettid); +} + +void UpdateAllLoopInfo() +{ + std::lock_guard g(pid_to_thread_info_mutex); + for (auto &item : pid_to_thread_info) { + auto tid = item.first; + auto sp_info = item.second; + if (sp_info->decRemainSecAndCheck()) { + if (thread_die_cb) + thread_die_cb(tid, sp_info->getName()); + } + } +} + +//! 监控线程函数 +void LoopProc() +{ + while (keep_running) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + UpdateAllLoopInfo(); + } +} + +//! 默认线程超时执行函数 +void OnLoopDie(const std::string &name) +{ + LogWarn("loop \"%s\" die!", name.c_str()); +} + +} + +void LoopWDog::SetLoopDieCallback(const LoopDieCallback &cb) +{ + thread_die_cb = cb; +} + +void LoopWDog::Start() +{ + keep_running = true; + sp_thread = std::make_shared(LoopProc); +} + +void LoopWDog::Register(const std::string &thread_name, uint16_t timeout_sec) +{ + pid_t curr_pid = CurrentLoopID(); + + std::shared_ptr sp_info; + + std::lock_guard g(pid_to_thread_info_mutex); + auto iter = pid_to_thread_info.find(curr_pid); + if (iter == pid_to_thread_info.end()) { //! 如果没有找到那么创建 + LogInfo("new thread \"%s\"", thread_name.c_str()); + auto sp_new_info = std::make_shared(); + pid_to_thread_info.insert(std::make_pair(curr_pid, sp_new_info)); + sp_info = sp_new_info; + } else { + LogInfo("update thread"); + sp_info = iter->second; + } + + sp_info->initialize(thread_name, timeout_sec); +} + +void LoopWDog::Unregister() +{ + pid_t curr_pid = CurrentLoopID(); + + LogInfo("delete thread"); + + std::lock_guard g(pid_to_thread_info_mutex); + pid_to_thread_info.erase(curr_pid); +} + +void LoopWDog::FeedDog() +{ + pid_t curr_pid = CurrentLoopID(); + + std::lock_guard g(pid_to_thread_info_mutex); + auto iter = pid_to_thread_info.find(curr_pid); + if (iter != pid_to_thread_info.end()) { //! 如果没有找到那么创建 + auto sp_info = iter->second; + sp_info->resetRemainSec(); + } else { + LogWarn("no current thread"); + } +} + +void LoopWDog::Stop() +{ + keep_running = false; + sp_thread->join(); + + pid_to_thread_info.clear(); + sp_thread.reset(); +} + +} +} diff --git a/modules/eventx/loop_wdog.h b/modules/eventx/loop_wdog.h new file mode 100644 index 0000000..4b5d596 --- /dev/null +++ b/modules/eventx/loop_wdog.h @@ -0,0 +1,32 @@ +#ifndef TBOX_LOOP_WDOG_H_20221110 +#define TBOX_LOOP_WDOG_H_20221110 + +#include +#include + +namespace tbox { +namespace eventx { + +/** + * Loop看门狗 + */ +class LoopWDog { + public: + using LoopDieCallback = std::function; + + //! 在 main() 中调用 + static void Start(); //! 启动线程监护 + static void Stop(); //! 停止线程监护 + + //! (可选) 设置线程异常时的回调 + static void SetLoopDieCallback(const LoopDieCallback &cb); + + //! 由被监控的线程调用 + static void Register(event::Loop *loop, const std::string &loop_name); //! 注册线程 + static void Unregister(event::Loop *loop); //! 注销线程 +}; + +} +} + +#endif //TBOX_LOOP_WDOG_H_20221110 -- Gitee From 4f837b513de947563beba857320bfbbe36347c43 Mon Sep 17 00:00:00 2001 From: Hevake Date: Mon, 14 Nov 2022 07:56:46 +0800 Subject: [PATCH 2/8] Add LoopWDog --- modules/eventx/Makefile | 3 + modules/eventx/loop_wdog.cpp | 197 ++++++++++++------------------ modules/eventx/loop_wdog.h | 2 +- modules/eventx/loop_wdog_test.cpp | 0 4 files changed, 85 insertions(+), 117 deletions(-) create mode 100644 modules/eventx/loop_wdog_test.cpp diff --git a/modules/eventx/Makefile b/modules/eventx/Makefile index fd537b4..9f025ee 100644 --- a/modules/eventx/Makefile +++ b/modules/eventx/Makefile @@ -8,11 +8,13 @@ HEAD_FILES = \ timer_pool.h \ timeout_monitor.h \ request_pool.hpp \ + loop_wdog.h \ CPP_SRC_FILES = \ thread_pool.cpp \ timer_pool.cpp \ timeout_monitor.cpp \ + loop_wdog.cpp \ CXXFLAGS := -DLOG_MODULE_ID='"eventx"' $(CXXFLAGS) @@ -21,6 +23,7 @@ TEST_CPP_SRC_FILES = \ timer_pool_test.cpp \ timeout_monitor_test.cpp \ request_pool_test.cpp \ + loop_wdog_test.cpp \ TEST_LDFLAGS := $(LDFLAGS) -ltbox_event -ltbox_base ENABLE_SHARED_LIB = no diff --git a/modules/eventx/loop_wdog.cpp b/modules/eventx/loop_wdog.cpp index a2b22a0..ce2a5d6 100644 --- a/modules/eventx/loop_wdog.cpp +++ b/modules/eventx/loop_wdog.cpp @@ -1,13 +1,11 @@ -#include -#include - #include #include #include -#include -#include +#include +#include + +#include "loop_wdog.h" -#include "thread_wdog.h" #include namespace tbox { @@ -15,146 +13,113 @@ namespace util { namespace { -void OnLoopDie(pid_t tid, const std::string &name); - -class LoopInfo { - public: - void initialize(const std::string &name, uint16_t sec) - { - thread_name_ = name; - timeout_sec_ = sec; - remain_sec_ = sec; - } - - void resetRemainSec() - { - remain_sec_ = timeout_sec_; - } - - bool decRemainSecAndCheck() - { - if (remain_sec_ > 0) { - --remain_sec_; - return false; - } else { - return true; - } - } +void OnLoopDie(const std::string &name); - std::string getName() const { return thread_name_; } +struct LoopInfo { + LoopInfo() : tag(new bool(true)) { } + ~LoopInfo() { delete tag; } - private: - std::string thread_name_; //! 线程名 - uint16_t timeout_sec_ = 0; //! 线程超时时间长度 - uint16_t remain_sec_ = 0; //! 剩余时间长度 + event::Loop* loop; + std::string name; //! 线程名 + bool *tag; }; -using PidToLoopInfo = std::map >; +using LoopInfoVec = std::vector; -PidToLoopInfo pid_to_thread_info; //! pid --> 线程信息表 -std::mutex pid_to_thread_info_mutex; //! 锁 -std::shared_ptr sp_thread; //! 线程对象 -bool keep_running = false; //! 线程是否继续工作标记 -LoopWDog::LoopDieCallback thread_die_cb = OnLoopDie; //! 回调函数 +LoopInfoVec _loop_info_vec; //! 线程信息表 +std::mutex _mutex_lock; //! 锁 +std::thread* _sp_thread; //! 线程对象 +bool _keep_running = false; //! 线程是否继续工作标记 -pid_t CurrentLoopID() -{ - return ::syscall(SYS_gettid); -} +LoopWDog::LoopDieCallback _loop_die_cb = OnLoopDie; //! 回调函数 -void UpdateAllLoopInfo() -{ - std::lock_guard g(pid_to_thread_info_mutex); - for (auto &item : pid_to_thread_info) { - auto tid = item.first; - auto sp_info = item.second; - if (sp_info->decRemainSecAndCheck()) { - if (thread_die_cb) - thread_die_cb(tid, sp_info->getName()); - } +void SendLoopFunc() { + std::lock_guard lg(_mutex_lock); + for (auto &loop_info : _loop_info_vec) { + auto tag = loop_info.tag; + if (*tag) { + *tag = false; + loop_info.loop->runInLoop([tag] { *tag = true; }); } + } } -//! 监控线程函数 -void LoopProc() -{ - while (keep_running) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - UpdateAllLoopInfo(); +void CheckLoopTag() { + std::lock_guard lg(_mutex_lock); + for (auto loop_info: _loop_info_vec) { + if (!*loop_info.tag) { + _loop_die_cb(loop_info.name); } + } } -//! 默认线程超时执行函数 -void OnLoopDie(const std::string &name) -{ - LogWarn("loop \"%s\" die!", name.c_str()); -} +//! 监控线程函数 +void LoopProc() { + while (_keep_running) { + SendLoopFunc(); -} + for (int i = 0; i < 10 && _keep_running; ++i) + std::this_thread::sleep_for(std::chrono::milliseconds(100)); -void LoopWDog::SetLoopDieCallback(const LoopDieCallback &cb) -{ - thread_die_cb = cb; -} + CheckLoopTag(); -void LoopWDog::Start() -{ - keep_running = true; - sp_thread = std::make_shared(LoopProc); + for (int i = 0; i < 40 && _keep_running; ++i) + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } } -void LoopWDog::Register(const std::string &thread_name, uint16_t timeout_sec) -{ - pid_t curr_pid = CurrentLoopID(); - - std::shared_ptr sp_info; - - std::lock_guard g(pid_to_thread_info_mutex); - auto iter = pid_to_thread_info.find(curr_pid); - if (iter == pid_to_thread_info.end()) { //! 如果没有找到那么创建 - LogInfo("new thread \"%s\"", thread_name.c_str()); - auto sp_new_info = std::make_shared(); - pid_to_thread_info.insert(std::make_pair(curr_pid, sp_new_info)); - sp_info = sp_new_info; - } else { - LogInfo("update thread"); - sp_info = iter->second; - } +//! 默认线程超时执行函数 +void OnLoopDie(const std::string &name) { + LogWarn("loop \"%s\" die!", name.c_str()); +} - sp_info->initialize(thread_name, timeout_sec); } -void LoopWDog::Unregister() -{ - pid_t curr_pid = CurrentLoopID(); +void LoopWDog::SetLoopDieCallback(const LoopDieCallback &cb) { thread_die_cb = cb; } - LogInfo("delete thread"); +void LoopWDog::Start() { + std::lock_guard lg(_mutex_lock); + if (!_keep_running) { + _keep_running = true; + _sp_thread = new std::thread(LoopProc); + } +} - std::lock_guard g(pid_to_thread_info_mutex); - pid_to_thread_info.erase(curr_pid); +void LoopWDog::Stop() { + std::lock_guard lg(_mutex_lock); + if (_keep_running) { + _keep_running = false; + _sp_thread->join(); + delete _sp_thread; + _loop_info_vec.clear(); + } } -void LoopWDog::FeedDog() -{ - pid_t curr_pid = CurrentLoopID(); - - std::lock_guard g(pid_to_thread_info_mutex); - auto iter = pid_to_thread_info.find(curr_pid); - if (iter != pid_to_thread_info.end()) { //! 如果没有找到那么创建 - auto sp_info = iter->second; - sp_info->resetRemainSec(); - } else { - LogWarn("no current thread"); +void LoopWDog::Register(event::Loop *loop, const std::string &name) { + std::lock_guard lg(_mutex_lock); + auto iter = std::find_if(_loop_info_vec.begin(), _loop_info_vec.end(), + [loop, name] (const LoopInfo &loop_info) { + return loop_info.loop == loop; } + ); + + if (iter == _loop_info_vec.end()) { //! 如果没有找到那么创建 + _loop_info_vec.emplace(LoopInfo{loop, name}); + } } -void LoopWDog::Stop() +void LoopWDog::Unregister(event::Loop *loop) { - keep_running = false; - sp_thread->join(); + std::lock_guard lg(_mutex_lock); + auto iter = std::remove_if(_loop_info_vec.begin(), _loop_info_vec.end(), + [loop, name] (const LoopInfo &loop_info) { + return loop_info.loop == loop; + } + ); - pid_to_thread_info.clear(); - sp_thread.reset(); + if (iter == _loop_info_vec.end()) { + _loop_info_vec.erase(iter, _loop_info_vec.end()); + } } } diff --git a/modules/eventx/loop_wdog.h b/modules/eventx/loop_wdog.h index 4b5d596..b1df552 100644 --- a/modules/eventx/loop_wdog.h +++ b/modules/eventx/loop_wdog.h @@ -2,7 +2,7 @@ #define TBOX_LOOP_WDOG_H_20221110 #include -#include +#include namespace tbox { namespace eventx { diff --git a/modules/eventx/loop_wdog_test.cpp b/modules/eventx/loop_wdog_test.cpp new file mode 100644 index 0000000..e69de29 -- Gitee From 87d670cbd5c0476e39cf0823aaee9afbeba42c9e Mon Sep 17 00:00:00 2001 From: Hevake Date: Mon, 14 Nov 2022 08:06:38 +0800 Subject: [PATCH 3/8] feat(evenx) fix LoopWDog's compile error --- modules/eventx/loop_wdog.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/modules/eventx/loop_wdog.cpp b/modules/eventx/loop_wdog.cpp index ce2a5d6..0575c73 100644 --- a/modules/eventx/loop_wdog.cpp +++ b/modules/eventx/loop_wdog.cpp @@ -9,14 +9,15 @@ #include namespace tbox { -namespace util { +namespace eventx { namespace { void OnLoopDie(const std::string &name); struct LoopInfo { - LoopInfo() : tag(new bool(true)) { } + LoopInfo(event::Loop *l, const std::string &n) : + loop(l), name(n), tag(new bool(true)) { } ~LoopInfo() { delete tag; } event::Loop* loop; @@ -24,11 +25,11 @@ struct LoopInfo { bool *tag; }; -using LoopInfoVec = std::vector; +using LoopInfoVec = std::vector; LoopInfoVec _loop_info_vec; //! 线程信息表 std::mutex _mutex_lock; //! 锁 -std::thread* _sp_thread; //! 线程对象 +std::thread* _sp; //! 线程对象 bool _keep_running = false; //! 线程是否继续工作标记 LoopWDog::LoopDieCallback _loop_die_cb = OnLoopDie; //! 回调函数 @@ -75,13 +76,13 @@ void OnLoopDie(const std::string &name) { } -void LoopWDog::SetLoopDieCallback(const LoopDieCallback &cb) { thread_die_cb = cb; } +void LoopWDog::SetLoopDieCallback(const LoopDieCallback &cb) { _loop_die_cb = cb; } void LoopWDog::Start() { std::lock_guard lg(_mutex_lock); if (!_keep_running) { _keep_running = true; - _sp_thread = new std::thread(LoopProc); + _sp = new std::thread(LoopProc); } } @@ -89,8 +90,8 @@ void LoopWDog::Stop() { std::lock_guard lg(_mutex_lock); if (_keep_running) { _keep_running = false; - _sp_thread->join(); - delete _sp_thread; + _sp->join(); + delete _sp; _loop_info_vec.clear(); } } @@ -104,7 +105,7 @@ void LoopWDog::Register(event::Loop *loop, const std::string &name) { ); if (iter == _loop_info_vec.end()) { //! 如果没有找到那么创建 - _loop_info_vec.emplace(LoopInfo{loop, name}); + _loop_info_vec.emplace_back(LoopInfo(loop, name)); } } @@ -112,7 +113,7 @@ void LoopWDog::Unregister(event::Loop *loop) { std::lock_guard lg(_mutex_lock); auto iter = std::remove_if(_loop_info_vec.begin(), _loop_info_vec.end(), - [loop, name] (const LoopInfo &loop_info) { + [loop] (const LoopInfo &loop_info) { return loop_info.loop == loop; } ); -- Gitee From aa631c8cfd6e315b2e994a2d11037dd2371777de Mon Sep 17 00:00:00 2001 From: Hevake Date: Tue, 15 Nov 2022 07:56:01 +0800 Subject: [PATCH 4/8] feat(eventx/LoopWDog):add test for LoopWDog --- modules/eventx/loop_wdog.cpp | 46 ++++++++++------ modules/eventx/loop_wdog_test.cpp | 92 +++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 18 deletions(-) diff --git a/modules/eventx/loop_wdog.cpp b/modules/eventx/loop_wdog.cpp index 0575c73..73fdccb 100644 --- a/modules/eventx/loop_wdog.cpp +++ b/modules/eventx/loop_wdog.cpp @@ -2,11 +2,14 @@ #include #include #include +#include #include #include "loop_wdog.h" #include +#include +#include namespace tbox { namespace eventx { @@ -17,19 +20,20 @@ void OnLoopDie(const std::string &name); struct LoopInfo { LoopInfo(event::Loop *l, const std::string &n) : - loop(l), name(n), tag(new bool(true)) { } - ~LoopInfo() { delete tag; } + loop(l), name(n), + tag(std::make_shared(true)) + { } event::Loop* loop; - std::string name; //! 线程名 - bool *tag; + std::string name; + std::shared_ptr tag; }; using LoopInfoVec = std::vector; LoopInfoVec _loop_info_vec; //! 线程信息表 std::mutex _mutex_lock; //! 锁 -std::thread* _sp; //! 线程对象 +std::thread* _sp_thread = nullptr; //! 线程对象 bool _keep_running = false; //! 线程是否继续工作标记 LoopWDog::LoopDieCallback _loop_die_cb = OnLoopDie; //! 回调函数 @@ -37,10 +41,12 @@ LoopWDog::LoopDieCallback _loop_die_cb = OnLoopDie; //! 回调函数 void SendLoopFunc() { std::lock_guard lg(_mutex_lock); for (auto &loop_info : _loop_info_vec) { - auto tag = loop_info.tag; - if (*tag) { - *tag = false; - loop_info.loop->runInLoop([tag] { *tag = true; }); + if (loop_info.loop->isRunning()) { + auto tag = loop_info.tag; + if (*tag) { + *tag = false; + loop_info.loop->runInLoop([tag] { *tag = true; }); + } } } } @@ -48,21 +54,23 @@ void SendLoopFunc() { void CheckLoopTag() { std::lock_guard lg(_mutex_lock); for (auto loop_info: _loop_info_vec) { - if (!*loop_info.tag) { + auto tag = loop_info.tag; + if (!(*tag)) { _loop_die_cb(loop_info.name); } } } //! 监控线程函数 -void LoopProc() { +void ThreadProc() { while (_keep_running) { SendLoopFunc(); for (int i = 0; i < 10 && _keep_running; ++i) std::this_thread::sleep_for(std::chrono::milliseconds(100)); - CheckLoopTag(); + if (_keep_running) + CheckLoopTag(); for (int i = 0; i < 40 && _keep_running; ++i) std::this_thread::sleep_for(std::chrono::milliseconds(100)); @@ -76,13 +84,16 @@ void OnLoopDie(const std::string &name) { } -void LoopWDog::SetLoopDieCallback(const LoopDieCallback &cb) { _loop_die_cb = cb; } +void LoopWDog::SetLoopDieCallback(const LoopDieCallback &cb) { + assert(cb != nullptr); + _loop_die_cb = cb; +} void LoopWDog::Start() { std::lock_guard lg(_mutex_lock); if (!_keep_running) { _keep_running = true; - _sp = new std::thread(LoopProc); + _sp_thread = new std::thread(ThreadProc); } } @@ -90,8 +101,8 @@ void LoopWDog::Stop() { std::lock_guard lg(_mutex_lock); if (_keep_running) { _keep_running = false; - _sp->join(); - delete _sp; + _sp_thread->join(); + CHECK_DELETE_RESET_OBJ(_sp_thread); _loop_info_vec.clear(); } } @@ -109,8 +120,7 @@ void LoopWDog::Register(event::Loop *loop, const std::string &name) { } } -void LoopWDog::Unregister(event::Loop *loop) -{ +void LoopWDog::Unregister(event::Loop *loop) { std::lock_guard lg(_mutex_lock); auto iter = std::remove_if(_loop_info_vec.begin(), _loop_info_vec.end(), [loop] (const LoopInfo &loop_info) { diff --git a/modules/eventx/loop_wdog_test.cpp b/modules/eventx/loop_wdog_test.cpp index e69de29..9249662 100644 --- a/modules/eventx/loop_wdog_test.cpp +++ b/modules/eventx/loop_wdog_test.cpp @@ -0,0 +1,92 @@ +#include +#include +#include "loop_wdog.h" +#include +#include + +namespace tbox { +namespace eventx { + +using namespace event; +using namespace std::chrono; + +TEST(LoopWDog, Normal) +{ + auto sp_loop = Loop::New(); + SetScopeExitAction([=] {delete sp_loop;}); + + int die_cb_count = 0; + LoopWDog::SetLoopDieCallback( + [&](const std::string &name) { ++die_cb_count; } + ); + LoopWDog::Start(); + sp_loop->exitLoop(std::chrono::seconds(6)); + LoopWDog::Register(sp_loop, "main_loop"); + sp_loop->runLoop(); + LoopWDog::Unregister(sp_loop); + LoopWDog::Stop(); + + EXPECT_EQ(die_cb_count, 0); +} + +TEST(LoopWDog, MainLoopBlock) +{ + auto sp_loop = Loop::New(); + SetScopeExitAction([=] {delete sp_loop;}); + LoopWDog::Register(sp_loop, "main_loop"); + + int die_cb_count = 0; + LoopWDog::SetLoopDieCallback( + [&](const std::string &name) { + EXPECT_EQ(name, "main_loop"); + ++die_cb_count; + } + ); + + sp_loop->runInLoop([] { std::this_thread::sleep_for(std::chrono::seconds(2)); }); + sp_loop->exitLoop(std::chrono::seconds(7)); + + LoopWDog::Start(); + sp_loop->runLoop(); + LoopWDog::Stop(); + + EXPECT_EQ(die_cb_count, 1); + LoopWDog::Unregister(sp_loop); +} + +TEST(LoopWDog, WorkLoopBlock) +{ + auto sp_loop = Loop::New(); + auto sp_work_loop = Loop::New(); + + SetScopeExitAction([=] { + delete sp_loop; + delete sp_work_loop; + } + ); + + int die_cb_count = 0; + LoopWDog::SetLoopDieCallback( + [&](const std::string &name) { + EXPECT_EQ(name, "work_loop"); + ++die_cb_count; + } + ); + + LoopWDog::Register(sp_loop, "main_loop"); + LoopWDog::Register(sp_work_loop, "work_loop"); + + sp_loop->exitLoop(std::chrono::seconds(7)); + + LoopWDog::Start(); + sp_loop->runLoop(); + LoopWDog::Stop(); + + LoopWDog::Unregister(sp_work_loop); + LoopWDog::Unregister(sp_loop); + + EXPECT_EQ(die_cb_count, 2); +} + +} +} -- Gitee From be28951a12dd349354da6e01c1567450dedede28 Mon Sep 17 00:00:00 2001 From: Hevake Date: Tue, 15 Nov 2022 23:48:23 +0800 Subject: [PATCH 5/8] feat: Add unittest for LoogWDog, and pass --- modules/eventx/loop_wdog.cpp | 2 +- modules/eventx/loop_wdog_test.cpp | 43 +++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/modules/eventx/loop_wdog.cpp b/modules/eventx/loop_wdog.cpp index 73fdccb..29e5d5b 100644 --- a/modules/eventx/loop_wdog.cpp +++ b/modules/eventx/loop_wdog.cpp @@ -128,7 +128,7 @@ void LoopWDog::Unregister(event::Loop *loop) { } ); - if (iter == _loop_info_vec.end()) { + if (iter != _loop_info_vec.end()) { _loop_info_vec.erase(iter, _loop_info_vec.end()); } } diff --git a/modules/eventx/loop_wdog_test.cpp b/modules/eventx/loop_wdog_test.cpp index 9249662..4a78ee3 100644 --- a/modules/eventx/loop_wdog_test.cpp +++ b/modules/eventx/loop_wdog_test.cpp @@ -2,7 +2,8 @@ #include #include "loop_wdog.h" #include -#include +//#include +//#include namespace tbox { namespace eventx { @@ -12,17 +13,20 @@ using namespace std::chrono; TEST(LoopWDog, Normal) { + LoopWDog::Start(); + auto sp_loop = Loop::New(); SetScopeExitAction([=] {delete sp_loop;}); + LoopWDog::Register(sp_loop, "main_loop"); int die_cb_count = 0; LoopWDog::SetLoopDieCallback( [&](const std::string &name) { ++die_cb_count; } ); - LoopWDog::Start(); + sp_loop->exitLoop(std::chrono::seconds(6)); - LoopWDog::Register(sp_loop, "main_loop"); sp_loop->runLoop(); + LoopWDog::Unregister(sp_loop); LoopWDog::Stop(); @@ -31,6 +35,8 @@ TEST(LoopWDog, Normal) TEST(LoopWDog, MainLoopBlock) { + LoopWDog::Start(); + auto sp_loop = Loop::New(); SetScopeExitAction([=] {delete sp_loop;}); LoopWDog::Register(sp_loop, "main_loop"); @@ -43,19 +49,22 @@ TEST(LoopWDog, MainLoopBlock) } ); - sp_loop->runInLoop([] { std::this_thread::sleep_for(std::chrono::seconds(2)); }); - sp_loop->exitLoop(std::chrono::seconds(7)); + sp_loop->runInLoop([] { std::this_thread::sleep_for(std::chrono::seconds(7)); }); + sp_loop->exitLoop(std::chrono::seconds(8)); - LoopWDog::Start(); sp_loop->runLoop(); + + LoopWDog::Unregister(sp_loop); LoopWDog::Stop(); EXPECT_EQ(die_cb_count, 1); - LoopWDog::Unregister(sp_loop); } TEST(LoopWDog, WorkLoopBlock) { + //LogOutput_Initialize(); + LoopWDog::Start(); + auto sp_loop = Loop::New(); auto sp_work_loop = Loop::New(); @@ -76,16 +85,28 @@ TEST(LoopWDog, WorkLoopBlock) LoopWDog::Register(sp_loop, "main_loop"); LoopWDog::Register(sp_work_loop, "work_loop"); - sp_loop->exitLoop(std::chrono::seconds(7)); + std::thread t([=] { sp_work_loop->runLoop(); }); + sp_work_loop->runInLoop( + [] { + //LogTrace("begin"); + std::this_thread::sleep_for(std::chrono::seconds(7)); + //LogTrace("end"); + } + ); - LoopWDog::Start(); + sp_loop->exitLoop(std::chrono::seconds(8)); sp_loop->runLoop(); - LoopWDog::Stop(); + + sp_work_loop->runInLoop([sp_work_loop] { sp_work_loop->exitLoop(); }); LoopWDog::Unregister(sp_work_loop); LoopWDog::Unregister(sp_loop); - EXPECT_EQ(die_cb_count, 2); + t.join(); + + EXPECT_EQ(die_cb_count, 1); + LoopWDog::Stop(); + //LogOutput_Cleanup(); } } -- Gitee From 653ef4a4231b549fd40e0fd528a79fc32f380e27 Mon Sep 17 00:00:00 2001 From: Hevake Date: Tue, 15 Nov 2022 23:57:43 +0800 Subject: [PATCH 6/8] feat(main): Use LoopWDog instead of ThreadWDog --- modules/main/main.cpp | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/modules/main/main.cpp b/modules/main/main.cpp index 7609ef9..c51aa3c 100644 --- a/modules/main/main.cpp +++ b/modules/main/main.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include @@ -109,16 +109,10 @@ int Main(int argc, char **argv) void Run(ContextImp &ctx, Module &apps, int loop_exit_wait) { - auto feeddog_timer = ctx.loop()->newTimerEvent(); auto stop_signal = ctx.loop()->newSignalEvent(); //! 预定在离开时自动释放对象,确保无内存泄漏 - SetScopeExitAction( - [=] { - delete stop_signal; - delete feeddog_timer; - } - ); + SetScopeExitAction([stop_signal] { delete stop_signal; }); stop_signal->initialize({SIGINT, SIGTERM}, event::Event::Mode::kOneshot); stop_signal->setCallback( @@ -131,15 +125,10 @@ void Run(ContextImp &ctx, Module &apps, int loop_exit_wait) } ); - //! 创建喂狗定时器 - feeddog_timer->initialize(std::chrono::seconds(2), event::Event::Mode::kPersist); - feeddog_timer->setCallback(util::ThreadWDog::FeedDog); - //! 启动前准备 - util::ThreadWDog::Start(); - util::ThreadWDog::Register("main", 3); + eventx::LoopWDog::Start(); + eventx::LoopWDog::Register(ctx.loop(), "main"); - feeddog_timer->enable(); stop_signal->enable(); LogInfo("Start!"); @@ -154,8 +143,8 @@ void Run(ContextImp &ctx, Module &apps, int loop_exit_wait) LogInfo("Stoped"); - util::ThreadWDog::Unregister(); - util::ThreadWDog::Stop(); + eventx::LoopWDog::Unregister(ctx.loop()); + eventx::LoopWDog::Stop(); } } -- Gitee From 6e3ab4369a7a5c6b4670a84bdc57ccf8af6d45f9 Mon Sep 17 00:00:00 2001 From: Hevake Date: Wed, 16 Nov 2022 00:13:29 +0800 Subject: [PATCH 7/8] feat(example): add main/05_loop_block --- examples/main/05_loop_block/Makefile | 22 ++++++++++++++++++ examples/main/05_loop_block/README.txt | 1 + examples/main/05_loop_block/app.cpp | 24 ++++++++++++++++++++ examples/main/05_loop_block/app.h | 20 +++++++++++++++++ examples/main/05_loop_block/main.cpp | 31 ++++++++++++++++++++++++++ 5 files changed, 98 insertions(+) create mode 100644 examples/main/05_loop_block/Makefile create mode 100644 examples/main/05_loop_block/README.txt create mode 100644 examples/main/05_loop_block/app.cpp create mode 100644 examples/main/05_loop_block/app.h create mode 100644 examples/main/05_loop_block/main.cpp diff --git a/examples/main/05_loop_block/Makefile b/examples/main/05_loop_block/Makefile new file mode 100644 index 0000000..06b2b09 --- /dev/null +++ b/examples/main/05_loop_block/Makefile @@ -0,0 +1,22 @@ +EXE_NAME := example/main/05_loop_block + +CPP_SRC_FILES = \ + app.cpp \ + main.cpp \ + +CXXFLAGS := -DLOG_MODULE_ID='"$(EXE_NAME)"' $(CXXFLAGS) +LDFLAGS += \ + -ltbox_main \ + -ltbox_terminal \ + -ltbox_network \ + -ltbox_eventx \ + -ltbox_eventx \ + -ltbox_event \ + -ltbox_log \ + -ltbox_util \ + -ltbox_base \ + -lpthread \ + -ldl \ + -rdynamic + +include ${TOP_DIR}/tools/exe_common.mk diff --git a/examples/main/05_loop_block/README.txt b/examples/main/05_loop_block/README.txt new file mode 100644 index 0000000..bc60bf4 --- /dev/null +++ b/examples/main/05_loop_block/README.txt @@ -0,0 +1 @@ +TODO: diff --git a/examples/main/05_loop_block/app.cpp b/examples/main/05_loop_block/app.cpp new file mode 100644 index 0000000..4b92b9a --- /dev/null +++ b/examples/main/05_loop_block/app.cpp @@ -0,0 +1,24 @@ +#include "app.h" +#include +#include + +App::App(tbox::main::Context &ctx) : + Module("app", ctx) +{ } + +bool App::onStart() { + timer_ = ctx().timer_pool()->doEvery( + std::chrono::seconds(1), + [] (tbox::eventx::TimerPool::TimerToken) { + LogDbg("begin sleep 2s"); + std::this_thread::sleep_for(std::chrono::seconds(2)); + LogDbg("end"); + } + ); + return true; +} + +void App::onStop() { + ctx().timer_pool()->cancel(timer_); + timer_.reset(); +} diff --git a/examples/main/05_loop_block/app.h b/examples/main/05_loop_block/app.h new file mode 100644 index 0000000..6ccdd67 --- /dev/null +++ b/examples/main/05_loop_block/app.h @@ -0,0 +1,20 @@ +#ifndef TBOX_MAIN_EXAMPLE_SAMPLE_H_20211226 +#define TBOX_MAIN_EXAMPLE_SAMPLE_H_20211226 + +#include +#include + +class App : public tbox::main::Module +{ + public: + App(tbox::main::Context &ctx); + + protected: + virtual bool onStart() override; + virtual void onStop() override; + + private: + tbox::eventx::TimerPool::TimerToken timer_; +}; + +#endif //TBOX_MAIN_EXAMPLE_SAMPLE_H_20211226 diff --git a/examples/main/05_loop_block/main.cpp b/examples/main/05_loop_block/main.cpp new file mode 100644 index 0000000..67ca64d --- /dev/null +++ b/examples/main/05_loop_block/main.cpp @@ -0,0 +1,31 @@ +#include +#include "app.h" + +namespace tbox { +namespace main { + +void RegisterApps(Module &apps, Context &ctx) +{ + apps.add(new App(ctx)); +} + +std::string GetAppDescribe() +{ + return "This loop block error example"; +} + +std::string GetAppBuildTime() +{ + return __DATE__ " " __TIME__; +} + +void GetAppVersion(int &major, int &minor, int &rev, int &build) +{ + major = 0; + minor = 0; + rev = 1; + build = 0; +} + +} +} -- Gitee From 308d4fa89279d0c426b0f32478b10f6b4a3eaf73 Mon Sep 17 00:00:00 2001 From: Hevake Date: Wed, 16 Nov 2022 07:05:41 +0800 Subject: [PATCH 8/8] =?UTF-8?q?style(LoopWDog):=20=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E5=90=8D=E3=80=81=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes:#I608XM --- examples/main/05_loop_block/README.txt | 2 +- modules/eventx/loop_wdog.cpp | 10 +++++----- modules/eventx/loop_wdog.h | 20 +++++++++++++------- modules/eventx/loop_wdog_test.cpp | 6 +++--- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/examples/main/05_loop_block/README.txt b/examples/main/05_loop_block/README.txt index bc60bf4..6e311f1 100644 --- a/examples/main/05_loop_block/README.txt +++ b/examples/main/05_loop_block/README.txt @@ -1 +1 @@ -TODO: +这个示例演示主线程被阻塞下的提示功能 diff --git a/modules/eventx/loop_wdog.cpp b/modules/eventx/loop_wdog.cpp index 29e5d5b..8a9dfdb 100644 --- a/modules/eventx/loop_wdog.cpp +++ b/modules/eventx/loop_wdog.cpp @@ -16,7 +16,7 @@ namespace eventx { namespace { -void OnLoopDie(const std::string &name); +void OnLoopBlock(const std::string &name); struct LoopInfo { LoopInfo(event::Loop *l, const std::string &n) : @@ -36,7 +36,7 @@ std::mutex _mutex_lock; //! 锁 std::thread* _sp_thread = nullptr; //! 线程对象 bool _keep_running = false; //! 线程是否继续工作标记 -LoopWDog::LoopDieCallback _loop_die_cb = OnLoopDie; //! 回调函数 +LoopWDog::LoopBlockCallback _loop_die_cb = OnLoopBlock; //! 回调函数 void SendLoopFunc() { std::lock_guard lg(_mutex_lock); @@ -78,13 +78,13 @@ void ThreadProc() { } //! 默认线程超时执行函数 -void OnLoopDie(const std::string &name) { - LogWarn("loop \"%s\" die!", name.c_str()); +void OnLoopBlock(const std::string &name) { + LogWarn("loop \"%s\" block!", name.c_str()); } } -void LoopWDog::SetLoopDieCallback(const LoopDieCallback &cb) { +void LoopWDog::SetLoopBlockCallback(const LoopBlockCallback &cb) { assert(cb != nullptr); _loop_die_cb = cb; } diff --git a/modules/eventx/loop_wdog.h b/modules/eventx/loop_wdog.h index b1df552..a0489c1 100644 --- a/modules/eventx/loop_wdog.h +++ b/modules/eventx/loop_wdog.h @@ -8,22 +8,28 @@ namespace tbox { namespace eventx { /** - * Loop看门狗 + * Loop看门狗(Loop阻塞监控器) + * + * 正常情况下,Loop线程是不可以执行有阻塞性的动作的。如果Loop发生了阻塞, + * 希望能被立即暴露出来。LoopWDog就是实现该功能而设计的。 + * 它每5秒让已注册的Loop执行心跳操作,然后等待1秒去检查这些Loop是否已经执行了心跳操作。 + * 如果Loop所在线程发生了阻塞,Loop所在的线程不能及时地执行心跳动作,从而可以判定Loop + * 所在的线程是否已发生阻塞,达到对Loop进行监控的目的 */ class LoopWDog { public: - using LoopDieCallback = std::function; + using LoopBlockCallback = std::function; //! 在 main() 中调用 static void Start(); //! 启动线程监护 static void Stop(); //! 停止线程监护 - //! (可选) 设置线程异常时的回调 - static void SetLoopDieCallback(const LoopDieCallback &cb); + //! (可选) 设置Loop阻塞时的回调 + static void SetLoopBlockCallback(const LoopBlockCallback &cb); - //! 由被监控的线程调用 - static void Register(event::Loop *loop, const std::string &loop_name); //! 注册线程 - static void Unregister(event::Loop *loop); //! 注销线程 + //! 注册与删除要被监控的Loop + static void Register(event::Loop *loop, const std::string &loop_name); + static void Unregister(event::Loop *loop); }; } diff --git a/modules/eventx/loop_wdog_test.cpp b/modules/eventx/loop_wdog_test.cpp index 4a78ee3..4b68a4b 100644 --- a/modules/eventx/loop_wdog_test.cpp +++ b/modules/eventx/loop_wdog_test.cpp @@ -20,7 +20,7 @@ TEST(LoopWDog, Normal) LoopWDog::Register(sp_loop, "main_loop"); int die_cb_count = 0; - LoopWDog::SetLoopDieCallback( + LoopWDog::SetLoopBlockCallback( [&](const std::string &name) { ++die_cb_count; } ); @@ -42,7 +42,7 @@ TEST(LoopWDog, MainLoopBlock) LoopWDog::Register(sp_loop, "main_loop"); int die_cb_count = 0; - LoopWDog::SetLoopDieCallback( + LoopWDog::SetLoopBlockCallback( [&](const std::string &name) { EXPECT_EQ(name, "main_loop"); ++die_cb_count; @@ -75,7 +75,7 @@ TEST(LoopWDog, WorkLoopBlock) ); int die_cb_count = 0; - LoopWDog::SetLoopDieCallback( + LoopWDog::SetLoopBlockCallback( [&](const std::string &name) { EXPECT_EQ(name, "work_loop"); ++die_cb_count; -- Gitee