MediumLock.cpp revision 1e8d24571f924deb1319d06ec9965d334f36c265
/** @file
*
* Medium lock management helper classes
*/
/*
* Copyright (C) 2010 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
#include "MediumLock.h"
#include "MediumImpl.h"
#include "MediumAttachmentImpl.h"
MediumLock::MediumLock()
: mMedium(NULL), mMediumCaller(NULL), mLockWrite(false),
mIsLocked(false), mLockSkipped(false)
{
}
MediumLock::~MediumLock()
{
Unlock();
}
MediumLock::MediumLock(const MediumLock &aMediumLock)
: mMedium(aMediumLock.mMedium), mMediumCaller(NULL),
mLockWrite(aMediumLock.mLockWrite), mIsLocked(false), mLockSkipped(false)
{
}
MediumLock::MediumLock(const ComObjPtr<Medium> &aMedium, bool aLockWrite)
: mMedium(aMedium), mMediumCaller(NULL), mLockWrite(aLockWrite),
mIsLocked(false), mLockSkipped(false)
{
}
HRESULT MediumLock::UpdateLock(bool aLockWrite)
{
if (aLockWrite == mLockWrite)
return S_OK;
if (mIsLocked)
{
HRESULT rc = Unlock();
if (FAILED(rc))
{
Lock();
return rc;
}
mLockWrite = aLockWrite;
rc = Lock();
if (FAILED(rc))
{
mLockWrite = !mLockWrite;
Lock();
return rc;
}
return S_OK;
}
mLockWrite = aLockWrite;
return S_OK;
}
const ComObjPtr<Medium> &MediumLock::GetMedium() const
{
return mMedium;
}
bool MediumLock::GetLockRequest() const
{
return mLockWrite;
}
HRESULT MediumLock::Lock()
{
if (mIsLocked)
return S_OK;
mMediumCaller.attach(mMedium);
if (FAILED(mMediumCaller.rc()))
{
mMediumCaller.attach(NULL);
return VBOX_E_INVALID_OBJECT_STATE;
}
HRESULT rc = S_OK;
MediumState_T state;
{
AutoReadLock alock(mMedium COMMA_LOCKVAL_SRC_POS);
state = mMedium->getState();
}
switch (state)
{
case MediumState_NotCreated:
case MediumState_Creating:
case MediumState_Deleting:
mLockSkipped = true;
break;
default:
if (mLockWrite)
rc = mMedium->LockWrite(NULL);
else
rc = mMedium->LockRead(NULL);
}
if (SUCCEEDED(rc))
{
mIsLocked = true;
return S_OK;
}
else
{
mMediumCaller.attach(NULL);
return VBOX_E_INVALID_OBJECT_STATE;
}
}
HRESULT MediumLock::Unlock()
{
HRESULT rc = S_OK;
if (mIsLocked && !mLockSkipped)
{
if (mLockWrite)
rc = mMedium->UnlockWrite(NULL);
else
rc = mMedium->UnlockRead(NULL);
}
mMediumCaller.attach(NULL);
mLockSkipped = false;
mIsLocked = false;
return rc;
}
MediumLockList::MediumLockList()
{
mIsLocked = false;
}
MediumLockList::~MediumLockList()
{
Clear();
// rest is done by the list object's destructor
}
bool MediumLockList::IsEmpty()
{
return mMediumLocks.empty();
}
HRESULT MediumLockList::Append(const ComObjPtr<Medium> &aMedium, bool aLockWrite)
{
if (mIsLocked)
return VBOX_E_INVALID_OBJECT_STATE;
mMediumLocks.push_back(MediumLock(aMedium, aLockWrite));
return S_OK;
}
HRESULT MediumLockList::Prepend(const ComObjPtr<Medium> &aMedium, bool aLockWrite)
{
if (mIsLocked)
return VBOX_E_INVALID_OBJECT_STATE;
mMediumLocks.push_front(MediumLock(aMedium, aLockWrite));
return S_OK;
}
HRESULT MediumLockList::Update(const ComObjPtr<Medium> &aMedium, bool aLockWrite)
{
if (mIsLocked)
return VBOX_E_INVALID_OBJECT_STATE;
for (MediumLockList::Base::iterator it = mMediumLocks.begin();
it != mMediumLocks.end();
it++)
{
if (it->GetMedium() == aMedium)
return it->UpdateLock(aLockWrite);
}
return VBOX_E_INVALID_OBJECT_STATE;
}
HRESULT MediumLockList::RemoveByIterator(Base::iterator &aIt)
{
HRESULT rc = aIt->Unlock();
aIt = mMediumLocks.erase(aIt);
return rc;
}
HRESULT MediumLockList::Clear()
{
HRESULT rc = Unlock();
mMediumLocks.clear();
return rc;
}
MediumLockList::Base::iterator MediumLockList::GetBegin()
{
return mMediumLocks.begin();
}
MediumLockList::Base::iterator MediumLockList::GetEnd()
{
return mMediumLocks.end();
}
HRESULT MediumLockList::Lock()
{
if (mIsLocked)
return S_OK;
HRESULT rc = S_OK;
for (MediumLockList::Base::iterator it = mMediumLocks.begin();
it != mMediumLocks.end();
it++)
{
rc = it->Lock();
if (FAILED(rc))
{
for (MediumLockList::Base::iterator it2 = mMediumLocks.begin();
it2 != it;
it2++)
{
HRESULT rc2 = it2->Unlock();
AssertComRC(rc2);
}
break;
}
}
if (SUCCEEDED(rc))
mIsLocked = true;
return rc;
}
HRESULT MediumLockList::Unlock()
{
if (!mIsLocked)
return S_OK;
HRESULT rc = S_OK;
for (MediumLockList::Base::iterator it = mMediumLocks.begin();
it != mMediumLocks.end();
it++)
{
HRESULT rc2 = it->Unlock();
if (SUCCEEDED(rc) && FAILED(rc2))
rc = rc2;
}
mIsLocked = false;
return rc;
}
MediumLockListMap::MediumLockListMap()
{
mIsLocked = false;
}
MediumLockListMap::~MediumLockListMap()
{
Clear();
// rest is done by the map object's destructor
}
bool MediumLockListMap::IsEmpty()
{
return mMediumLocks.empty();
}
HRESULT MediumLockListMap::Insert(const ComObjPtr<MediumAttachment> &aMediumAttachment,
MediumLockList *aMediumLockList)
{
if (mIsLocked)
return VBOX_E_INVALID_OBJECT_STATE;
mMediumLocks[aMediumAttachment] = aMediumLockList;
return S_OK;
}
HRESULT MediumLockListMap::ReplaceKey(const ComObjPtr<MediumAttachment> &aMediumAttachmentOld,
const ComObjPtr<MediumAttachment> &aMediumAttachmentNew)
{
MediumLockListMap::Base::iterator it = mMediumLocks.find(aMediumAttachmentOld);
if (it == mMediumLocks.end())
return VBOX_E_INVALID_OBJECT_STATE;
MediumLockList *pMediumLockList = it->second;
mMediumLocks.erase(it);
mMediumLocks[aMediumAttachmentNew] = pMediumLockList;
return S_OK;
}
HRESULT MediumLockListMap::Remove(const ComObjPtr<MediumAttachment> &aMediumAttachment)
{
MediumLockListMap::Base::iterator it = mMediumLocks.find(aMediumAttachment);
if (it == mMediumLocks.end())
return VBOX_E_INVALID_OBJECT_STATE;
mMediumLocks.erase(it);
return S_OK;
}
HRESULT MediumLockListMap::Clear()
{
HRESULT rc = Unlock();
for (MediumLockListMap::Base::iterator it = mMediumLocks.begin();
it != mMediumLocks.end();
)
{
MediumLockList *pMediumLockList = it->second;
// need an incremented iterator as otherwise erasing invalidates it
mMediumLocks.erase(it++);
delete pMediumLockList;
}
return rc;
}
HRESULT MediumLockListMap::Get(const ComObjPtr<MediumAttachment> &aMediumAttachment,
MediumLockList * &aMediumLockList)
{
MediumLockListMap::Base::iterator it = mMediumLocks.find(aMediumAttachment);
if (it == mMediumLocks.end())
{
aMediumLockList = NULL;
return VBOX_E_INVALID_OBJECT_STATE;
}
aMediumLockList = it->second;
return S_OK;
}
HRESULT MediumLockListMap::Lock()
{
if (mIsLocked)
return S_OK;
HRESULT rc = S_OK;
for (MediumLockListMap::Base::const_iterator it = mMediumLocks.begin();
it != mMediumLocks.end();
it++)
{
rc = it->second->Lock();
if (FAILED(rc))
{
for (MediumLockListMap::Base::const_iterator it2 = mMediumLocks.begin();
it2 != it;
it2++)
{
HRESULT rc2 = it2->second->Unlock();
AssertComRC(rc2);
}
break;
}
}
if (SUCCEEDED(rc))
mIsLocked = true;
return rc;
}
HRESULT MediumLockListMap::Unlock()
{
if (!mIsLocked)
return S_OK;
HRESULT rc = S_OK;
for (MediumLockListMap::Base::const_iterator it = mMediumLocks.begin();
it != mMediumLocks.end();
it++)
{
MediumLockList *pMediumLockList = it->second;
HRESULT rc2 = pMediumLockList->Unlock();
if (SUCCEEDED(rc) && FAILED(rc2))
rc = rc2;
}
mIsLocked = false;
return rc;
}