From d10217f3f57706850792773b1418dcb32989ed89 Mon Sep 17 00:00:00 2001 From: Hayodea Hakol Date: Fri, 19 Sep 2025 18:52:27 -0400 Subject: [PATCH] Docs: update qutex algo plan --- docs/design/qutexes.md | 84 ++++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 20 deletions(-) diff --git a/docs/design/qutexes.md b/docs/design/qutexes.md index b94f3c5..41722d6 100644 --- a/docs/design/qutexes.md +++ b/docs/design/qutexes.md @@ -257,6 +257,25 @@ class LockSet std::vector locks; } +bool LockerAndInvoker::operator==(const LockerAndInvoker &other) +{ + /* Compare by the address of the continuation objects. Why? + * Because there's no guarantee that the lockvoker object that was + * passed in by the io_service invocation is the same object as that + * which is in the qutexQs. Especially because we make_shared() a + * copy when registerInQutexQueues()ing. + * + * Generally when we "wake" a lockvoker by enqueuing it, boost's + * io_service::post will copy the lockvoker object. + */ + return &this->serializedContinuation == &other.serializedContinuation; +} + +bool LockerAndInvoker::operator !=(const LockerAndInvoker &other) +{ + return &this->serializedContinuation != &other.serializedContinuation; +} + class Qutex { public: @@ -280,17 +299,27 @@ public: return it; } - void unregister(LockerAndInvokerList::iterator it) + void unregister(LockerAndInvokerList::iterator it, bool shouldLock=1) { - lock.acquire(); - queue.erase(it); - lock.release(); + if (shouldLock) + { + lock.acquire(); + queue.erase(it); + lock.release(); + } + else{ + queue.erase(it); + } } - bool tryAcquire(LockerAndInvoker &tryingLockvoker, int nRequiredLocks) + bool tryAcquire(LockerAndInvoker &tryingLockvoker) { + const nRequiredLocks = tryingLockvoker.serializedContinuation + .requiredLocks.size(); + lock.acquire(); - qNItems = queue.size(); + + const qNItems = queue.size(); if (qNItems < 1) { lock.release(); @@ -304,17 +333,11 @@ public: throw; } - if (isOwned) { + if (!!currentOwner) { lock.release(); return false; } - if (nRequiredLocks == 1) { - isOwned.store(true); - lock.release(); - return true; - } - /** EXPLANATION: * From here: * if qNItems == 1 the we are the only one in the ticketQ and we have @@ -340,17 +363,31 @@ public: const int nRearItemsToScan = qNItems / nRequiredLocks; if (qNItems == 1 || nRearItemsToScan < 1) { - isOwned.store(true); + currOwner = tryingLockvoker; lock.release(); return true; } + /** EXPLANATION: + * For lockvokers that only have 1 requiredLock, they must be at the + * front of the queue to successfully acquire. + */ + if (nRequiredLocks == 1) + { + if (tryingLockvoker == &queue.front()) + { + currOwner = tryingLockvoker; + lock.release(); + return true; + } + } + auto rIt = queue.rbegin(); auto rEndIt = queue.rend(); bool foundInRear = false; for (int i=0; iserializedContinuation.requiredLocks + .getLockUsageDesc(*this).second, false); + + currOwner.release(); /** NOTE: * I am not sure whether we should only wake up the front item if @@ -514,7 +558,7 @@ public: public: SpinLock lock; - std::atomic isOwned; + std::shared_ptr currOwner; LockerAndInvokerList queue; }; ``` \ No newline at end of file