SerializedAC:analyze all locksets in contin chain;

Not only those of type <OriginCbT>.
* Fix indentation too.
This commit is contained in:
2026-03-05 21:22:50 -04:00
parent e98aae7e7e
commit 85ac715772
3 changed files with 43 additions and 42 deletions

View File

@@ -1,7 +1,11 @@
#ifndef ASYNCHRONOUS_CONTINUATION_CHAIN_LINK_H #ifndef ASYNCHRONOUS_CONTINUATION_CHAIN_LINK_H
#define ASYNCHRONOUS_CONTINUATION_CHAIN_LINK_H #define ASYNCHRONOUS_CONTINUATION_CHAIN_LINK_H
#include <functional>
#include <memory> #include <memory>
#include <optional>
#include <vector>
#include <spinscale/lockSet.h>
namespace sscl { namespace sscl {
@@ -25,6 +29,13 @@ public:
virtual std::shared_ptr<AsynchronousContinuationChainLink> virtual std::shared_ptr<AsynchronousContinuationChainLink>
getCallersContinuationShPtr() const = 0; getCallersContinuationShPtr() const = 0;
virtual std::optional<std::reference_wrapper<const LockSet>>
getLockSet() const
{ return std::nullopt; }
virtual std::optional<std::reference_wrapper<LockSet>> getLockSet()
{ return std::nullopt; }
}; };
} // namespace sscl } // namespace sscl

View File

@@ -11,15 +11,11 @@
namespace sscl { namespace sscl {
// Forward declarations
template <class OriginalCbFnT>
class SerializedAsynchronousContinuation;
class Qutex; class Qutex;
/** /**
* @brief LockSet - Manages a collection of locks for acquisition/release * @brief LockSet - Manages a collection of locks for acquisition/release
*/ */
template <class OriginalCbFnT>
class LockSet class LockSet
{ {
public: public:
@@ -44,15 +40,10 @@ public:
public: public:
/** /**
* @brief Constructor * @brief Constructor
* @param parentContinuation Reference to the parent
* SerializedAsynchronousContinuation
* @param qutexes Vector of Qutex references that must be acquired * @param qutexes Vector of Qutex references that must be acquired
*/ */
LockSet( explicit LockSet(std::vector<std::reference_wrapper<Qutex>> qutexes = {})
SerializedAsynchronousContinuation<OriginalCbFnT> &parentContinuation, : allLocksAcquired(false), registeredInQutexQueues(false)
std::vector<std::reference_wrapper<Qutex>> qutexes = {})
: parentContinuation(parentContinuation), allLocksAcquired(false),
registeredInQutexQueues(false)
{ {
/* Convert Qutex references to LockUsageDesc (iterators will be filled /* Convert Qutex references to LockUsageDesc (iterators will be filled
* in during registration) * in during registration)
@@ -131,7 +122,6 @@ public:
bool tryAcquireOrBackOff( bool tryAcquireOrBackOff(
LockerAndInvokerBase &lockvoker, LockerAndInvokerBase &lockvoker,
std::optional<std::reference_wrapper<Qutex>> &firstFailedQutex std::optional<std::reference_wrapper<Qutex>> &firstFailedQutex
= std::nullopt
) )
{ {
if (!registeredInQutexQueues) if (!registeredInQutexQueues)
@@ -251,7 +241,6 @@ public:
std::vector<LockUsageDesc> locks; std::vector<LockUsageDesc> locks;
private: private:
SerializedAsynchronousContinuation<OriginalCbFnT> &parentContinuation;
bool allLocksAcquired, registeredInQutexQueues; bool allLocksAcquired, registeredInQutexQueues;
}; };

View File

@@ -26,9 +26,16 @@ public:
Callback<OriginalCbFnT> originalCbFn, Callback<OriginalCbFnT> originalCbFn,
std::vector<std::reference_wrapper<Qutex>> requiredLocks) std::vector<std::reference_wrapper<Qutex>> requiredLocks)
: PostedAsynchronousContinuation<OriginalCbFnT>(caller, originalCbFn), : PostedAsynchronousContinuation<OriginalCbFnT>(caller, originalCbFn),
requiredLocks(*this, std::move(requiredLocks)) requiredLocks(std::move(requiredLocks))
{} {}
std::optional<std::reference_wrapper<const LockSet>>
getLockSet() const override
{ return std::cref(requiredLocks); }
std::optional<std::reference_wrapper<LockSet>> getLockSet() override
{ return std::ref(requiredLocks); }
template<typename... Args> template<typename... Args>
void callOriginalCb(Args&&... args) void callOriginalCb(Args&&... args)
{ {
@@ -50,7 +57,7 @@ public:
{ requiredLocks.releaseQutexEarly(qutex); } { requiredLocks.releaseQutexEarly(qutex); }
public: public:
LockSet<OriginalCbFnT> requiredLocks; LockSet requiredLocks;
std::atomic<bool> isAwakeOrBeingAwakened{false}; std::atomic<bool> isAwakeOrBeingAwakened{false};
/** /**
@@ -287,15 +294,13 @@ const
currContin != nullptr; currContin != nullptr;
currContin = currContin->getCallersContinuationShPtr()) currContin = currContin->getCallersContinuationShPtr())
{ {
auto serializedCont = std::dynamic_pointer_cast< auto heldLockSet = currContin->getLockSet();
SerializedAsynchronousContinuation<OriginalCbFnT>>(currContin); if (!heldLockSet.has_value()) { continue; }
if (serializedCont == nullptr) { continue; }
// Add this continuation's locks to the held locks list // Add this continuation's locks to the held locks list
for (size_t i = 0; i < serializedCont->requiredLocks.locks.size(); ++i) for (size_t i = 0; i < heldLockSet->get().locks.size(); ++i)
{ {
heldLocks->push_front(serializedCont->requiredLocks.locks[i].qutex); heldLocks->push_front(heldLockSet->get().locks[i].qutex);
} }
} }
@@ -327,14 +332,12 @@ SerializedAsynchronousContinuation<OriginalCbFnT>
currContin != nullptr; currContin != nullptr;
currContin = currContin->getCallersContinuationShPtr()) currContin = currContin->getCallersContinuationShPtr())
{ {
auto serializedCont = std::dynamic_pointer_cast< auto heldLockSet = currContin->getLockSet();
SerializedAsynchronousContinuation<OriginalCbFnT>>(currContin); if (!heldLockSet.has_value()) { continue; }
if (serializedCont == nullptr) { continue; }
// Check if the firstFailedQutex is in this continuation's LockSet // Check if the firstFailedQutex is in this continuation's LockSet
try { try {
serializedCont->requiredLocks.getLockUsageDesc(firstFailedQutex); heldLockSet->get().getLockUsageDesc(firstFailedQutex);
} catch (const std::runtime_error& e) { } catch (const std::runtime_error& e) {
std::cerr << __func__ << ": " << e.what() << std::endl; std::cerr << __func__ << ": " << e.what() << std::endl;
continue; continue;
@@ -344,7 +347,7 @@ SerializedAsynchronousContinuation<OriginalCbFnT>
<< "firstFailedQutex @" << &firstFailedQutex << "firstFailedQutex @" << &firstFailedQutex
<< " (" << firstFailedQutex.name << ") in LockSet of " << " (" << firstFailedQutex.name << ") in LockSet of "
<< "SerializedAsynchronousContinuation @" << "SerializedAsynchronousContinuation @"
<< serializedCont.get() << std::endl; << currContin.get() << std::endl;
return true; return true;
} }
@@ -428,20 +431,18 @@ SerializedAsynchronousContinuation<OriginalCbFnT>
currContin != nullptr; currContin != nullptr;
currContin = currContin->getCallersContinuationShPtr()) currContin = currContin->getCallersContinuationShPtr())
{ {
auto serializedCont = std::dynamic_pointer_cast< auto heldLockSet = currContin->getLockSet();
SerializedAsynchronousContinuation<OriginalCbFnT>>(currContin); if (!heldLockSet.has_value()) { continue; }
if (serializedCont == nullptr) { continue; }
// Check if this continuation holds the foreign lock // Check if this continuation holds the foreign lock
try { try {
const auto& lockUsageDesc = serializedCont->requiredLocks const auto& lockUsageDesc = heldLockSet
.getLockUsageDesc(foreignLock); ->get().getLockUsageDesc(foreignLock);
// Matched! We hold a lock that the foreign owner is waiting for // Matched! We hold a lock that the foreign owner is waiting for
std::cout << __func__ << ": Gridlock detected: We hold lock @" std::cout << __func__ << ": Gridlock detected: We hold lock @"
<< &foreignLock << " (" << foreignLock.name << ") in " << &foreignLock << " (" << foreignLock.name << ") in "
"continuation @" << serializedCont.get() "continuation @" << currContin.get()
<< ", while foreign owner @" << &foreignOwner << ", while foreign owner @" << &foreignOwner
<< " holds lock @" << &firstFailedQutex << " (" << " holds lock @" << &firstFailedQutex << " ("
<< firstFailedQutex.name << ") that we're waiting for" << firstFailedQutex.name << ") that we're waiting for"