#include #include namespace smo { // Template method implementations that need full Qutex definition // These will be explicitly instantiated for the types we need template template void LockSet::registerInQutexQueues( const typename SerializedAsynchronousContinuation::template LockerAndInvoker &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::template LockerAndInvoker>(lockvoker); for (auto& lockUsageDesc : locks) { lockUsageDesc.second = lockUsageDesc.first.get().registerInQueue( sharedLockvoker); } registeredInQutexQueues = true; } template bool LockSet::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(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 void LockSet::unregisterFromQutexQueues() { // Unregister from all qutex queues for (auto& lockUsageDesc : locks) { auto it = lockUsageDesc.second; lockUsageDesc.first.get().unregisterFromQueue(it); } } template void LockSet::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