Qutexes: Implement them and supporting classes
Implements: LockSet, SerializedAsynchronousContinuation, LockerAndInvoker, LockerAndInvokerBase, Qutex. Very big leap in functionality here. See qutexes.md for an explanation of what we've done.
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
#include <lockSet.h>
|
||||
#include <qutex.h>
|
||||
|
||||
namespace smo {
|
||||
|
||||
// Template method implementations that need full Qutex definition
|
||||
// These will be explicitly instantiated for the types we need
|
||||
|
||||
template <class OriginalCbFnT>
|
||||
template <class InvocationTargetT>
|
||||
void LockSet<OriginalCbFnT>::registerInQutexQueues(
|
||||
const typename SerializedAsynchronousContinuation<OriginalCbFnT>::template LockerAndInvoker<InvocationTargetT> &lockvoker
|
||||
)
|
||||
{
|
||||
/** EXPLANATION:
|
||||
* Register the lockvoker with each Qutex and store the returned
|
||||
* iterator to its place within each Qutex's queue. We store the
|
||||
* iterator so that we can quickly move the lockvoker around within
|
||||
* the queue, and eventually, erase() it when we acquire all the
|
||||
* locks.
|
||||
*
|
||||
* We create a copy of the Lockvoker and then give sh_ptrs to that
|
||||
* *COPY*, to each Qutex's internal queue. This enables us to keep
|
||||
* the AsyncContinuation sh_ptr (which the Lockvoker contains within
|
||||
* itself) alive without wasting too much memory.
|
||||
*
|
||||
* This way the io_service objects can remove the lockvoker from
|
||||
* their queues and there'll be a copy of the lockvoker in each
|
||||
* Qutex's queue.
|
||||
*
|
||||
* For non-serialized, posted continuations, they won't be removed
|
||||
* from the io_service queue until they're executed, so there's no
|
||||
* need to create copies of them. Lockvokers are removed from their
|
||||
* io_service, potentially without being executed if they fail to
|
||||
* acquire all locks.
|
||||
*/
|
||||
auto sharedLockvoker = std::make_shared<
|
||||
typename SerializedAsynchronousContinuation<OriginalCbFnT>::template LockerAndInvoker<InvocationTargetT>>(lockvoker);
|
||||
|
||||
for (auto& lockUsageDesc : locks)
|
||||
{
|
||||
lockUsageDesc.second = lockUsageDesc.first.get().registerInQueue(
|
||||
sharedLockvoker);
|
||||
}
|
||||
|
||||
registeredInQutexQueues = true;
|
||||
}
|
||||
|
||||
template <class OriginalCbFnT>
|
||||
bool LockSet<OriginalCbFnT>::tryAcquireOrBackOff(LockerAndInvokerBase &lockvoker)
|
||||
{
|
||||
if (!registeredInQutexQueues)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) +
|
||||
": LockSet::tryAcquireOrBackOff() called but not registered in "
|
||||
"Qutex queues");
|
||||
}
|
||||
if (allLocksAcquired)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) +
|
||||
": LockSet::tryAcquireOrBackOff() called but allLocksAcquired "
|
||||
"is already true");
|
||||
}
|
||||
|
||||
// Try to acquire all required locks
|
||||
int nAcquired = 0;
|
||||
const int nRequiredLocks = static_cast<int>(locks.size());
|
||||
for (auto& lockUsageDesc : locks)
|
||||
{
|
||||
if (!lockUsageDesc.first.get().tryAcquire(
|
||||
lockvoker, nRequiredLocks))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
nAcquired++;
|
||||
}
|
||||
|
||||
if (nAcquired < nRequiredLocks)
|
||||
{
|
||||
// Release any locks we managed to acquire
|
||||
for (int i = 0; i < nAcquired; i++) {
|
||||
locks[i].first.get().backoff(lockvoker, nRequiredLocks);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
allLocksAcquired = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class OriginalCbFnT>
|
||||
void LockSet<OriginalCbFnT>::unregisterFromQutexQueues()
|
||||
{
|
||||
// Unregister from all qutex queues
|
||||
for (auto& lockUsageDesc : locks)
|
||||
{
|
||||
auto it = lockUsageDesc.second;
|
||||
lockUsageDesc.first.get().unregisterFromQueue(it);
|
||||
}
|
||||
}
|
||||
|
||||
template <class OriginalCbFnT>
|
||||
void LockSet<OriginalCbFnT>::release(LockerAndInvokerBase &lockvoker)
|
||||
{
|
||||
if (!registeredInQutexQueues)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) +
|
||||
": LockSet::release() called but not registered in Qutex "
|
||||
"queues");
|
||||
}
|
||||
|
||||
if (!allLocksAcquired)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) +
|
||||
": LockSet::release() called but allLocksAcquired is false");
|
||||
}
|
||||
|
||||
for (auto& lockUsageDesc : locks) {
|
||||
lockUsageDesc.first.get().release();
|
||||
}
|
||||
|
||||
allLocksAcquired = false;
|
||||
}
|
||||
|
||||
} // namespace smo
|
||||
Reference in New Issue
Block a user