From 33c006b178f7e0afeee71f85c54731cd6c4c0fae Mon Sep 17 00:00:00 2001 From: Hayodea Hakol Date: Wed, 17 Sep 2025 16:29:58 -0400 Subject: [PATCH] Locking: Add LockSpec class which manages sets of spinlocks This class allows us to list a series of spinlocks that are all acquired and released together. It has simple, primitive detection for deadlocks and will throw if it detects one. --- include/lockSpec.h | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 include/lockSpec.h diff --git a/include/lockSpec.h b/include/lockSpec.h new file mode 100644 index 0000000..cfd5957 --- /dev/null +++ b/include/lockSpec.h @@ -0,0 +1,88 @@ +#ifndef LOCK_SPEC_H +#define LOCK_SPEC_H + +#include +#include +#include +#include +#include + +namespace smo { + +/** + * @brief LockSpec - Manages a collection of locks for acquisition/release + */ +class LockSpec +{ +public: + /** + * @brief Constructor + * @param requiredLocks Vector of lock references that must be acquired + */ + LockSpec(std::vector> requiredLocks = {}) + : requiredLocks(std::move(requiredLocks)), allLocksAcquired(false) + {} + + /** + * @brief Try to acquire all locks in order + * @return true if all locks were acquired, false otherwise + */ + bool tryAcquire() + { + if (allLocksAcquired) + { + throw std::runtime_error( + std::string(__func__) + + ": LockSpec::tryAcquire() called but allLocksAcquired is " + "already true"); + } + + // Try to acquire all required locks + int nAcquired = 0; + for (auto& lockRef : requiredLocks) + { + if (!lockRef.get().tryAcquire()) { break; } + nAcquired++; + } + + if (nAcquired < static_cast(requiredLocks.size())) + { + // Release any locks we managed to acquire + for (int i = 0; i < nAcquired; i++) { + requiredLocks[i].get().release(); + } + allLocksAcquired = false; + return false; + } + + allLocksAcquired = true; + return true; + } + + /** + * @brief Release all locks + */ + void release() + { + if (!allLocksAcquired) + { + throw std::runtime_error( + std::string(__func__) + + ": LockSpec::release() called but allLocksAcquired is false"); + } + + for (auto& lockRef : requiredLocks) { + lockRef.get().release(); + } + + allLocksAcquired = false; + } + +private: + std::vector> requiredLocks; + std::atomic allLocksAcquired; +}; + +} // namespace smo + +#endif // LOCK_SPEC_H