From 758586bb3c26fcc6198f00ae4a835b373d39600e Mon Sep 17 00:00:00 2001 From: Hayodea Hekol Date: Tue, 30 Sep 2025 14:07:13 -0400 Subject: [PATCH] LockerAndInvoker: Move template method impls around --- include/serializedAsynchronousContinuation.h | 313 ++++++++++--------- 1 file changed, 159 insertions(+), 154 deletions(-) diff --git a/include/serializedAsynchronousContinuation.h b/include/serializedAsynchronousContinuation.h index 0b2c066..d309980 100644 --- a/include/serializedAsynchronousContinuation.h +++ b/include/serializedAsynchronousContinuation.h @@ -98,125 +98,7 @@ public: * @brief Function call operator - tries to acquire locks and either * invokes the target or returns (already registered in qutex queues) */ - void operator()() - { - if (ComponentThread::getSelf() != target) - { - throw std::runtime_error( - "LockerAndInvoker::operator(): Thread safety violation - " - "executing on wrong ComponentThread"); - } - - std::optional> firstFailedQutexRet; - bool deadlockLikely = isDeadlockLikely(); - bool gridlockLikely = isGridlockLikely(); - - if (!serializedContinuation.requiredLocks.tryAcquireOrBackOff( - *this, firstFailedQutexRet)) - { - // Just allow this lockvoker to be dropped from its io_service. - allowAwakening(); - if (!deadlockLikely && !gridlockLikely) - { return; } - -#ifdef CONFIG_ENABLE_DEBUG_LOCKS - Qutex &firstFailedQutex = firstFailedQutexRet.value().get(); - bool isDeadlock = traceContinuationHistoryForDeadlockOn( - firstFailedQutex); - - bool gridlockIsHeuristicallyLikely = false; - bool gridlockIsAlgorithmicallyLikely = false; - - if (gridlockLikely) - { - auto& tracker = QutexAcquisitionHistoryTracker - ::getInstance(); - - auto heldLocks = serializedContinuation - .getAcquiredQutexHistory(); - - // Add this continuation to the tracker - auto currentContinuationShPtr = serializedContinuation - .shared_from_this(); - - tracker.addIfNotExists( - currentContinuationShPtr, - firstFailedQutex, std::move(heldLocks)); - - gridlockIsHeuristicallyLikely = tracker - .heuristicallyTraceContinuationHistoryForGridlockOn( - firstFailedQutex, currentContinuationShPtr); - - if (gridlockIsHeuristicallyLikely) - { - gridlockIsAlgorithmicallyLikely = tracker - .completelyTraceContinuationHistoryForGridlockOn( - firstFailedQutex); - } - } - - bool isGridlock = (gridlockIsHeuristicallyLikely - || gridlockIsAlgorithmicallyLikely); - - if (!isDeadlock && !isGridlock) - { return; } - - if (isDeadlock) { handleDeadlock(firstFailedQutex); } - if (isGridlock) { handleGridlock(firstFailedQutex); } -#endif - return; - } - - /** EXPLANATION: - * Successfully acquired all locks, so unregister from qutex queues. - * We do this here so that we can free up queue slots in the qutex - * queues for other lockvokers that may be waiting to acquire the - * locks. The size of the qutex queues does matter for other - * contending lockvokers; and so also does their position in the - * queues. - * - * The alternative is to leave ourself in the queues until we - * eventually release all locks; and given that we may hold locks - * even across true async hardware bottlenecks, this could take a - * long time. - * - * Granted, the fact that we own the locks means that even though - * we've removed ourselves from the queues, other lockvokers still - * can't acquire the locks anyway. - */ - serializedContinuation.requiredLocks.unregisterFromQutexQueues(); - -#ifdef CONFIG_ENABLE_DEBUG_LOCKS - /** EXPLANATION: - * If we were being tracked for gridlock detection but successfully - * acquired all locks, it was a false positive due to timed delay, - * long-running operation, or I/O delay - */ - if (gridlockLikely) - { - std::shared_ptr - currentContinuationShPtr = - serializedContinuation.shared_from_this(); - - bool removed = QutexAcquisitionHistoryTracker::getInstance() - .remove(currentContinuationShPtr); - - if (removed) - { - std::cerr - << "LockerAndInvoker::operator(): False positive " - "gridlock detection - continuation @" - << &serializedContinuation - << " was being tracked but successfully acquired all " - "locks. This was likely due to timed delay, " - "long-running operation, or I/O delay." - << std::endl; - } - } -#endif - - invocationTarget(); - } + void operator()(); /** * @brief Get the iterator for this lockvoker in the specified Qutex's queue @@ -379,6 +261,41 @@ public: #ifdef CONFIG_ENABLE_DEBUG_LOCKS +template +std::unique_ptr>> +SerializedAsynchronousContinuation::getAcquiredQutexHistory() +const +{ + auto heldLocks = std::make_unique< + std::forward_list>>(); + + /** EXPLANATION: + * Walk through the continuation chain to collect all acquired locks + * + * We don't add the current continuation's locks because it's the one + * failing to acquire locks and backing off. So we start from the previous + * continuation. + */ + for (std::shared_ptr currContin = + this->getCallersContinuationShPtr(); + currContin != nullptr; + currContin = currContin->getCallersContinuationShPtr()) + { + auto serializedCont = std::dynamic_pointer_cast< + SerializedAsynchronousContinuation>(currContin); + + if (serializedCont == nullptr) { continue; } + + // Add this continuation's locks to the held locks list + for (size_t i = 0; i < serializedCont->requiredLocks.locks.size(); ++i) + { + heldLocks->push_front(serializedCont->requiredLocks.locks[i].first); + } + } + + return heldLocks; +} + template template bool @@ -429,41 +346,6 @@ SerializedAsynchronousContinuation return false; } -template -std::unique_ptr>> -SerializedAsynchronousContinuation::getAcquiredQutexHistory() -const -{ - auto heldLocks = std::make_unique< - std::forward_list>>(); - - /** EXPLANATION: - * Walk through the continuation chain to collect all acquired locks - * - * We don't add the current continuation's locks because it's the one - * failing to acquire locks and backing off. So we start from the previous - * continuation. - */ - for (std::shared_ptr currContin = - this->getCallersContinuationShPtr(); - currContin != nullptr; - currContin = currContin->getCallersContinuationShPtr()) - { - auto serializedCont = std::dynamic_pointer_cast< - SerializedAsynchronousContinuation>(currContin); - - if (serializedCont == nullptr) { continue; } - - // Add this continuation's locks to the held locks list - for (size_t i = 0; i < serializedCont->requiredLocks.locks.size(); ++i) - { - heldLocks->push_front(serializedCont->requiredLocks.locks[i].first); - } - } - - return heldLocks; -} - template template bool @@ -572,6 +454,129 @@ SerializedAsynchronousContinuation #endif // CONFIG_ENABLE_DEBUG_LOCKS +template +template +void SerializedAsynchronousContinuation +::LockerAndInvoker::operator()() +{ + if (ComponentThread::getSelf() != target) + { + throw std::runtime_error( + "LockerAndInvoker::operator(): Thread safety violation - " + "executing on wrong ComponentThread"); + } + + std::optional> firstFailedQutexRet; + bool deadlockLikely = isDeadlockLikely(); + bool gridlockLikely = isGridlockLikely(); + + if (!serializedContinuation.requiredLocks.tryAcquireOrBackOff( + *this, firstFailedQutexRet)) + { + // Just allow this lockvoker to be dropped from its io_service. + allowAwakening(); + if (!deadlockLikely && !gridlockLikely) + { return; } + +#ifdef CONFIG_ENABLE_DEBUG_LOCKS + Qutex &firstFailedQutex = firstFailedQutexRet.value().get(); + bool isDeadlock = traceContinuationHistoryForDeadlockOn( + firstFailedQutex); + + bool gridlockIsHeuristicallyLikely = false; + bool gridlockIsAlgorithmicallyLikely = false; + + if (gridlockLikely) + { + auto& tracker = QutexAcquisitionHistoryTracker + ::getInstance(); + + auto heldLocks = serializedContinuation + .getAcquiredQutexHistory(); + + // Add this continuation to the tracker + auto currentContinuationShPtr = serializedContinuation + .shared_from_this(); + + tracker.addIfNotExists( + currentContinuationShPtr, + firstFailedQutex, std::move(heldLocks)); + + gridlockIsHeuristicallyLikely = tracker + .heuristicallyTraceContinuationHistoryForGridlockOn( + firstFailedQutex, currentContinuationShPtr); + + if (gridlockIsHeuristicallyLikely) + { + gridlockIsAlgorithmicallyLikely = tracker + .completelyTraceContinuationHistoryForGridlockOn( + firstFailedQutex); + } + } + + bool isGridlock = (gridlockIsHeuristicallyLikely + || gridlockIsAlgorithmicallyLikely); + + if (!isDeadlock && !isGridlock) + { return; } + + if (isDeadlock) { handleDeadlock(firstFailedQutex); } + if (isGridlock) { handleGridlock(firstFailedQutex); } +#endif + return; + } + + /** EXPLANATION: + * Successfully acquired all locks, so unregister from qutex queues. + * We do this here so that we can free up queue slots in the qutex + * queues for other lockvokers that may be waiting to acquire the + * locks. The size of the qutex queues does matter for other + * contending lockvokers; and so also does their position in the + * queues. + * + * The alternative is to leave ourself in the queues until we + * eventually release all locks; and given that we may hold locks + * even across true async hardware bottlenecks, this could take a + * long time. + * + * Granted, the fact that we own the locks means that even though + * we've removed ourselves from the queues, other lockvokers still + * can't acquire the locks anyway. + */ + serializedContinuation.requiredLocks.unregisterFromQutexQueues(); + +#ifdef CONFIG_ENABLE_DEBUG_LOCKS + /** EXPLANATION: + * If we were being tracked for gridlock detection but successfully + * acquired all locks, it was a false positive due to timed delay, + * long-running operation, or I/O delay + */ + if (gridlockLikely) + { + std::shared_ptr + currentContinuationShPtr = + serializedContinuation.shared_from_this(); + + bool removed = QutexAcquisitionHistoryTracker::getInstance() + .remove(currentContinuationShPtr); + + if (removed) + { + std::cerr + << "LockerAndInvoker::operator(): False positive " + "gridlock detection - continuation @" + << &serializedContinuation + << " was being tracked but successfully acquired all " + "locks. This was likely due to timed delay, " + "long-running operation, or I/O delay." + << std::endl; + } + } +#endif + + invocationTarget(); +} + } // namespace smo #endif // SERIALIZED_ASYNCHRONOUS_CONTINUATION_H