Docs: update qutex algo plan

This commit is contained in:
2025-09-19 18:52:27 -04:00
parent 4b0e832e27
commit d10217f3f5
+64 -20
View File
@@ -257,6 +257,25 @@ class LockSet
std::vector<LockUsageDesc> 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; i<nRearItemsToScan && rIt != rEndIt; rIt++, i++)
{
if (&(*rIt) != &tryingLockvoker) { continue; }
if (*rIt != tryingLockvoker) { continue; }
foundInRear == true;
break;
@@ -364,7 +401,7 @@ public:
/* Not found in rear: this means the item is in the top X%. That means
* it should be allowed to claim the lock.
*/
isOwned.store(true);
currOwner = tryingLockvoker;
lock.release();
return true;
}
@@ -445,6 +482,8 @@ public:
*/
}
currOwner.release();
LockerAndInvoker &newFront = queue.front();
lock.release();
@@ -452,7 +491,7 @@ public:
wakeUp(newFront);
}
void release(LockerAndInvoker &prevOwner)
void release()
{
lock.acquire();
@@ -460,8 +499,13 @@ public:
LockerAndInvoker &oldFront = queue.front();
#endif
unregister(prevOwner.serializedContinuation.requiredLocks.getDesc(
*this).second);
/* Get the saved iterator and use it to unregister.
* Don't acquire lock because we already acquired it in this function.
*/
unregister(currOwner->serializedContinuation.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<bool> isOwned;
std::shared_ptr<LockerAndInvoker> currOwner;
LockerAndInvokerList queue;
};
```