Files
salmanoff/smocore/lockSet.cpp
T

146 lines
3.8 KiB
C++

#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, Qutex *firstFailedQutex
)
{
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))
{
// Set the first failed qutex for debugging
if (firstFailedQutex) {
*firstFailedQutex = &lockUsageDesc.first.get();
}
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()
{
if (!registeredInQutexQueues)
{
throw std::runtime_error(
std::string(__func__) +
": LockSet::unregisterFromQutexQueues() called but not "
"registered in Qutex queues");
}
// 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