mirror of
https://github.com/latentPrion/libspinscale.git
synced 2026-04-17 22:44:25 +00:00
Compare commits
5 Commits
e98aae7e7e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b6eb502e56 | |||
| 596ad367e2 | |||
| e4332323f9 | |||
| 7eff7a6a9c | |||
| 85ac715772 |
@@ -81,6 +81,11 @@ add_library(spinscale SHARED
|
|||||||
src/callableTracer.cpp
|
src/callableTracer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set_target_properties(spinscale PROPERTIES
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||||
|
)
|
||||||
|
|
||||||
# Conditionally add qutexAcquisitionHistoryTracker.cpp only when debug locks
|
# Conditionally add qutexAcquisitionHistoryTracker.cpp only when debug locks
|
||||||
# are enabled, since the tracker is only referenced under CONFIG_ENABLE_DEBUG_LOCKS.
|
# are enabled, since the tracker is only referenced under CONFIG_ENABLE_DEBUG_LOCKS.
|
||||||
if(ENABLE_DEBUG_LOCKS)
|
if(ENABLE_DEBUG_LOCKS)
|
||||||
@@ -127,29 +132,29 @@ endif()
|
|||||||
# Install rules
|
# Install rules
|
||||||
install(TARGETS spinscale
|
install(TARGETS spinscale
|
||||||
EXPORT spinscaleTargets
|
EXPORT spinscaleTargets
|
||||||
LIBRARY DESTINATION lib
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} NAMELINK_SKIP
|
||||||
ARCHIVE DESTINATION lib
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
RUNTIME DESTINATION bin
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
install(DIRECTORY include/spinscale
|
install(DIRECTORY include/spinscale
|
||||||
DESTINATION include
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
FILES_MATCHING PATTERN "*.h"
|
FILES_MATCHING PATTERN "*.h"
|
||||||
)
|
)
|
||||||
|
|
||||||
install(FILES include/boostAsioLinkageFix.h
|
install(FILES include/boostAsioLinkageFix.h
|
||||||
DESTINATION include
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/config.h
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/config.h
|
||||||
DESTINATION include
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Install CMake config files for find_package() support
|
# Install CMake config files for find_package() support
|
||||||
install(EXPORT spinscaleTargets
|
install(EXPORT spinscaleTargets
|
||||||
FILE spinscaleTargets.cmake
|
FILE spinscaleTargets.cmake
|
||||||
NAMESPACE spinscale::
|
NAMESPACE spinscale::
|
||||||
DESTINATION lib/cmake/spinscale
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/spinscale
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create config file for find_package()
|
# Create config file for find_package()
|
||||||
@@ -158,7 +163,7 @@ include(CMakePackageConfigHelpers)
|
|||||||
configure_package_config_file(
|
configure_package_config_file(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/spinscaleConfig.cmake.in
|
${CMAKE_CURRENT_SOURCE_DIR}/cmake/spinscaleConfig.cmake.in
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/spinscaleConfig.cmake
|
${CMAKE_CURRENT_BINARY_DIR}/spinscaleConfig.cmake
|
||||||
INSTALL_DESTINATION lib/cmake/spinscale
|
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/spinscale
|
||||||
)
|
)
|
||||||
|
|
||||||
write_basic_package_version_file(
|
write_basic_package_version_file(
|
||||||
@@ -170,5 +175,5 @@ write_basic_package_version_file(
|
|||||||
install(FILES
|
install(FILES
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/spinscaleConfig.cmake
|
${CMAKE_CURRENT_BINARY_DIR}/spinscaleConfig.cmake
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/spinscaleConfigVersion.cmake
|
${CMAKE_CURRENT_BINARY_DIR}/spinscaleConfigVersion.cmake
|
||||||
DESTINATION lib/cmake/spinscale
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/spinscale
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class ComponentThread
|
|||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
ComponentThread(ThreadId _id, std::string _name)
|
ComponentThread(ThreadId _id, std::string _name)
|
||||||
: id(_id), name(std::move(_name)), work(io_service)
|
: id(_id), name(std::move(_name)), work(io_service), keepLooping(true)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -207,15 +197,52 @@ public:
|
|||||||
allLocksAcquired = false;
|
allLocksAcquired = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LockUsageDesc &getLockUsageDesc(const Qutex &criterionLock) const
|
std::optional<std::reference_wrapper<LockUsageDesc>>
|
||||||
|
findLockUsageDesc(const Qutex &criterionLock)
|
||||||
{
|
{
|
||||||
for (auto& lockUsageDesc : locks)
|
for (auto& lockUsageDesc : locks)
|
||||||
{
|
{
|
||||||
if (&lockUsageDesc.qutex.get() == &criterionLock) {
|
if (&lockUsageDesc.qutex.get() == &criterionLock) {
|
||||||
return lockUsageDesc;
|
return std::ref(lockUsageDesc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::reference_wrapper<const LockUsageDesc>>
|
||||||
|
findLockUsageDesc(const Qutex &criterionLock) const
|
||||||
|
{
|
||||||
|
for (const auto& lockUsageDesc : locks)
|
||||||
|
{
|
||||||
|
if (&lockUsageDesc.qutex.get() == &criterionLock) {
|
||||||
|
return std::cref(lockUsageDesc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
LockUsageDesc &getLockUsageDesc(const Qutex &criterionLock)
|
||||||
|
{
|
||||||
|
auto lockUsageDesc = findLockUsageDesc(criterionLock);
|
||||||
|
if (lockUsageDesc.has_value()) {
|
||||||
|
return lockUsageDesc->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should never happen if the LockSet is properly constructed
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::string(__func__) +
|
||||||
|
": Qutex not found in this LockSet");
|
||||||
|
}
|
||||||
|
|
||||||
|
const LockUsageDesc &getLockUsageDesc(const Qutex &criterionLock) const
|
||||||
|
{
|
||||||
|
auto lockUsageDesc = findLockUsageDesc(criterionLock);
|
||||||
|
if (lockUsageDesc.has_value()) {
|
||||||
|
return lockUsageDesc->get();
|
||||||
|
}
|
||||||
|
|
||||||
// Should never happen if the LockSet is properly constructed
|
// Should never happen if the LockSet is properly constructed
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
std::string(__func__) +
|
std::string(__func__) +
|
||||||
@@ -235,8 +262,7 @@ public:
|
|||||||
": LockSet::releaseQutexEarly() called but allLocksAcquired is false");
|
": LockSet::releaseQutexEarly() called but allLocksAcquired is false");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& lockUsageDesc = const_cast<LockUsageDesc&>(
|
auto& lockUsageDesc = getLockUsageDesc(qutex);
|
||||||
getLockUsageDesc(qutex));
|
|
||||||
|
|
||||||
if (!lockUsageDesc.hasBeenReleased)
|
if (!lockUsageDesc.hasBeenReleased)
|
||||||
{
|
{
|
||||||
@@ -251,7 +277,6 @@ public:
|
|||||||
std::vector<LockUsageDesc> locks;
|
std::vector<LockUsageDesc> locks;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SerializedAsynchronousContinuation<OriginalCbFnT> &parentContinuation;
|
|
||||||
bool allLocksAcquired, registeredInQutexQueues;
|
bool allLocksAcquired, registeredInQutexQueues;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
@@ -40,7 +47,8 @@ public:
|
|||||||
// Return list of all qutexes in predecessors' LockSets; excludes self.
|
// Return list of all qutexes in predecessors' LockSets; excludes self.
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
std::unique_ptr<std::forward_list<std::reference_wrapper<Qutex>>>
|
std::unique_ptr<std::forward_list<std::reference_wrapper<Qutex>>>
|
||||||
getAcquiredQutexHistory() const;
|
getAcquiredQutexHistory(
|
||||||
|
bool includeLocksWhichHaveBeenReleased = false) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Release a specific qutex early
|
* @brief Release a specific qutex early
|
||||||
@@ -50,7 +58,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};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -269,8 +277,8 @@ public:
|
|||||||
|
|
||||||
template <class OriginalCbFnT>
|
template <class OriginalCbFnT>
|
||||||
std::unique_ptr<std::forward_list<std::reference_wrapper<Qutex>>>
|
std::unique_ptr<std::forward_list<std::reference_wrapper<Qutex>>>
|
||||||
SerializedAsynchronousContinuation<OriginalCbFnT>::getAcquiredQutexHistory()
|
SerializedAsynchronousContinuation<OriginalCbFnT>::getAcquiredQutexHistory(
|
||||||
const
|
bool includeLocksWhichHaveBeenReleased) const
|
||||||
{
|
{
|
||||||
auto heldLocks = std::make_unique<
|
auto heldLocks = std::make_unique<
|
||||||
std::forward_list<std::reference_wrapper<Qutex>>>();
|
std::forward_list<std::reference_wrapper<Qutex>>>();
|
||||||
@@ -287,15 +295,19 @@ 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);
|
if (!includeLocksWhichHaveBeenReleased
|
||||||
|
&& heldLockSet->get().locks[i].hasBeenReleased)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
heldLocks->push_front(heldLockSet->get().locks[i].qutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,16 +339,15 @@ 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; }
|
// A miss is expected here; only a hit indicates a potential deadlock.
|
||||||
|
auto lockUsageDesc = heldLockSet->get().findLockUsageDesc(
|
||||||
// Check if the firstFailedQutex is in this continuation's LockSet
|
firstFailedQutex);
|
||||||
try {
|
if (!lockUsageDesc.has_value()
|
||||||
serializedCont->requiredLocks.getLockUsageDesc(firstFailedQutex);
|
|| lockUsageDesc->get().hasBeenReleased)
|
||||||
} catch (const std::runtime_error& e) {
|
{
|
||||||
std::cerr << __func__ << ": " << e.what() << std::endl;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,7 +355,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,30 +439,24 @@ 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; }
|
// A miss is expected here; a hit indicates a potential gridlock.
|
||||||
|
auto lockUsageDesc = heldLockSet->get().findLockUsageDesc(
|
||||||
// Check if this continuation holds the foreign lock
|
foreignLock);
|
||||||
try {
|
if (!lockUsageDesc.has_value()) { continue; }
|
||||||
const auto& lockUsageDesc = serializedCont->requiredLocks
|
|
||||||
.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"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (const std::runtime_error& e) {
|
|
||||||
// This continuation doesn't hold the foreign lock. Continue.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user