Debug:Qutex: Add deadlock detection based on elapsed time
We now detect that a deadlock is likely when CONFIG_DEBUG_QUTEX_DEADLOCK_TIMEOUT_MS has elapsed. This is the preliminary work required to do a backtrace through the call stack and figure out if a deadlock has really occured. To do this, we'd have to go through the async call chain and search for a previous caller which acquired the same qutex as the one that first failed during this Lockvoker LockSet acquisition attempt.
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <componentThread.h>
|
||||
#include <lockSet.h>
|
||||
#include <asynchronousContinuation.h>
|
||||
@@ -63,7 +65,8 @@ public:
|
||||
: LockerAndInvokerBase(&serializedContinuation),
|
||||
serializedContinuation(serializedContinuation),
|
||||
target(target),
|
||||
invocationTarget(std::move(invocationTarget))
|
||||
invocationTarget(std::move(invocationTarget)),
|
||||
creationTimestamp(std::chrono::steady_clock::now())
|
||||
{
|
||||
firstWake();
|
||||
}
|
||||
@@ -81,11 +84,28 @@ public:
|
||||
"executing on wrong ComponentThread");
|
||||
}
|
||||
|
||||
Qutex *firstFailedQutexPtr = nullptr;
|
||||
bool isDeadlockLikely = isDeadlockLikely();
|
||||
|
||||
if (!serializedContinuation.requiredLocks.tryAcquireOrBackOff(
|
||||
*this))
|
||||
*this, (isDeadlockLikely ? &firstFailedQutexPtr : nullptr)))
|
||||
{
|
||||
// Just allow this lockvoker to be dropped from its io_service.
|
||||
allowAwakening();
|
||||
|
||||
if (!isDeadlockLikely)
|
||||
{ return; }
|
||||
|
||||
Qutex &firstFailedQutex = *firstFailedQutexPtr;
|
||||
|
||||
std::cerr << __func__ << ": Deadlock likely: "
|
||||
<< "Lockvoker has been waiting for "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - creationTimestamp)
|
||||
.count()
|
||||
<< "ms, failed on qutex @" << &firstFailedQutex
|
||||
<< " (" << firstFailedQutex.name << ")" << std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -137,9 +157,8 @@ public:
|
||||
target->getIoService().post(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allow awakening by resetting the awake flag
|
||||
*/
|
||||
private:
|
||||
// Allow awakening by resetting the awake flag
|
||||
void allowAwakening()
|
||||
{ serializedContinuation.isAwakeOrBeingAwakened.store(false); }
|
||||
|
||||
@@ -158,11 +177,21 @@ public:
|
||||
awaken(true);
|
||||
}
|
||||
|
||||
// Check if CONFIG_QUTEX_DEADLOCK_TIMEOUT_MS has elapsed since creation
|
||||
bool isDeadlockLikely() const
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - creationTimestamp);
|
||||
return elapsed.count() >= DEBUG_CONFIG_QUTEX_DEADLOCK_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
private:
|
||||
SerializedAsynchronousContinuation<OriginalCbFnT>
|
||||
&serializedContinuation;
|
||||
InvocationTargetT invocationTarget;
|
||||
std::shared_ptr<ComponentThread> target;
|
||||
std::chrono::steady_clock::time_point creationTimestamp;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user