Timer::TimerThreadProc(): keep a Timer alive while it's running

to prevent the case: Timer callback destroys parent object -> destroys
Timer -> ~Timer() -> Stop(true) -> waits for the Timer callback to finish
-> deadlock.
This commit is contained in:
Alexander A. Klimov 2023-03-21 11:45:47 +01:00
parent 9b00c1c4dd
commit 1badbab002
2 changed files with 20 additions and 6 deletions

View File

@ -12,6 +12,7 @@
#include <condition_variable>
#include <mutex>
#include <thread>
#include <utility>
using namespace icinga;
@ -61,6 +62,15 @@ static int l_AliveTimers = 0;
static Defer l_ShutdownTimersCleanlyOnExit (&Timer::Uninitialize);
Timer::Ptr Timer::Create()
{
Ptr t (new Timer());
t->m_Self = t;
return t;
}
/**
* Destructor for the Timer class.
*/
@ -316,11 +326,18 @@ void Timer::TimerThreadProc()
* until the current call is completed. */
l_Timers.erase(timer);
auto keepAlive (timer->m_Self.lock());
if (!keepAlive) {
// The last std::shared_ptr is gone, let ~Timer() proceed
continue;
}
timer->m_Running = true;
lock.unlock();
/* Asynchronously call the timer. */
Utility::QueueAsyncCallback([timer]() { timer->Call(); });
Utility::QueueAsyncCallback([timer=std::move(keepAlive)]() { timer->Call(); });
}
}

View File

@ -21,11 +21,7 @@ class Timer final
public:
typedef std::shared_ptr<Timer> Ptr;
static inline
Ptr Create()
{
return Ptr(new Timer());
}
static Ptr Create();
~Timer();
@ -52,6 +48,7 @@ private:
double m_Next{0}; /**< When the next event should happen. */
bool m_Started{false}; /**< Whether the timer is enabled. */
bool m_Running{false}; /**< Whether the timer proc is currently running. */
std::weak_ptr<Timer> m_Self;
Timer() = default;
void Call();