ProgressImpl.cpp revision 156c3838b0c2a078d5652a6d9e3526526f779be0
/** @file
*
* VirtualBox COM class implementation
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License 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.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
#if !defined (__WIN__)
#include <nsIServiceManager.h>
#include <nsIExceptionService.h>
#include <nsCOMPtr.h>
#endif // !defined (__WIN__)
#include "ProgressImpl.h"
#include "VirtualBoxImpl.h"
#include "VirtualBoxErrorInfoImpl.h"
#include "Logging.h"
#include <iprt/semaphore.h>
////////////////////////////////////////////////////////////////////////////////
// ProgressBase class
////////////////////////////////////////////////////////////////////////////////
// constructor / destructor
////////////////////////////////////////////////////////////////////////////////
/** Subclasses must call this method from their FinalConstruct() implementations */
{
mCancelable = FALSE;
mCompleted = FALSE;
mResultCode = S_OK;
mOperationCount = 0;
mOperation = 0;
mOperationPercent = 0;
return S_OK;
}
// public initializer/uninitializer for internal purposes only
////////////////////////////////////////////////////////////////////////////////
/**
* Initializes the progress base object.
*
* Subclasses should call this or any other #init() method from their
* init() implementations.
*
* @param aParent
* Parent object (only for server-side Progress objects)
* @param aInitiator
* Initiator of the task (for server-side objects
* can be NULL which means initiator = parent, otherwise
* must not be NULL)
* @param aDescription
* Task description
* @param aID
* Address of result GUID structure (optional)
*
* @note
* This method doesn't do |isReady()| check and doesn't call
* |setReady (true)| on success!
* @note
* This method must be called from under the object's lock!
*/
#if !defined (VBOX_COM_INPROC)
#endif
{
#if !defined (VBOX_COM_INPROC)
#else
#endif
#if !defined (VBOX_COM_INPROC)
#endif
#if !defined (VBOX_COM_INPROC)
// assign (and therefore addref) initiator only if it is not VirtualBox
// (to avoid cycling); otherwise mInitiator will remain null which means
// that it is the same as the parent
#else
#endif
if (aId)
#if !defined (VBOX_COM_INPROC)
// add to the global colleciton of progess operations
mParent->addProgress (this);
// cause #uninit() to be called automatically upon VirtualBox uninit
mParent->addDependentChild (this);
#endif
return S_OK;
}
/**
* Initializes the progress base object.
* This is a special initializator for progress objects that are combined
* within a CombinedProgress instance, so it doesn't require the parent,
* initiator, description and doesn't create an ID. Note that calling respective
* getter methods on an object initialized with this constructor will hit an
* assertion.
*
* Subclasses should call this or any other #init() method from their
* init() implementations.
*
* @note
* This method doesn't do |isReady()| check and doesn't call
* |setReady (true)| on success!
* @note
* This method must be called from under the object's lock!
*/
{
return S_OK;
}
/**
* Uninitializes the instance.
* Subclasses should call this from their uninit() implementations.
* The readiness flag must be true on input and will be set to false
* on output.
*
* @param alock this object's autolock (with lock level = 1!)
*
* @note
* Using mParent member after this method returns is forbidden.
*/
{
LogFlowMember (("ProgressBase::protectedUninit()\n"));
/*
* release initiator
* (effective only if mInitiator has been assigned in init())
*/
setReady (false);
#if !defined (VBOX_COM_INPROC)
if (mParent)
{
mParent->removeDependentChild (this);
}
#endif
}
// IProgress properties
/////////////////////////////////////////////////////////////////////////////
{
if (!aId)
return E_POINTER;
CHECK_READY();
return S_OK;
}
{
if (!aDescription)
return E_POINTER;
CHECK_READY();
return S_OK;
}
{
if (!aInitiator)
return E_POINTER;
CHECK_READY();
#if !defined (VBOX_COM_INPROC)
if (mInitiator)
else
#else
#endif
return S_OK;
}
{
if (!aCancelable)
return E_POINTER;
CHECK_READY();
return S_OK;
}
{
if (!aPercent)
return E_POINTER;
CHECK_READY();
*aPercent = 100;
else
{
// global percent = (100 / mOperationCount) * mOperation +
// ((100 / mOperationCount) / 100) * mOperationPercent
}
return S_OK;
}
{
if (!aCompleted)
return E_POINTER;
CHECK_READY();
*aCompleted = mCompleted;
return S_OK;
}
{
if (!aCanceled)
return E_POINTER;
CHECK_READY();
return S_OK;
}
{
if (!aResultCode)
return E_POINTER;
CHECK_READY();
if (!mCompleted)
tr ("Result code is not available, operation is still in progress"));
return S_OK;
}
{
if (!aErrorInfo)
return E_POINTER;
CHECK_READY();
if (!mCompleted)
tr ("Error info is not available, operation is still in progress"));
return S_OK;
}
{
if (!aOperationCount)
return E_POINTER;
CHECK_READY();
return S_OK;
}
{
if (!aOperation)
return E_POINTER;
CHECK_READY();
*aOperation = mOperation;
return S_OK;
}
{
if (!aOperationDescription)
return E_POINTER;
CHECK_READY();
return S_OK;
}
{
if (!aOperationPercent)
return E_POINTER;
CHECK_READY();
*aOperationPercent = 100;
else
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////
// Progress class
////////////////////////////////////////////////////////////////////////////////
{
return rc;
mWaitersCount = 0;
return S_OK;
}
void Progress::FinalRelease()
{
uninit();
}
// public initializer/uninitializer for internal purposes only
////////////////////////////////////////////////////////////////////////////////
/**
* Initializes the progress object.
*
* @param aParent see ProgressBase::init()
* @param aInitiator see ProgressBase::init()
* @param aDescription see ProgressBase::init()
* @param aCancelable Flag whether the task maybe canceled
* @param aOperationCount Number of operations within this task (at least 1)
* @param aOperationDescription Description of the first operation
* @param aId see ProgressBase::init()
*/
#if !defined (VBOX_COM_INPROC)
#endif
{
do
{
#if !defined (VBOX_COM_INPROC)
#endif
// set ready to let protectedUninit() be called on failure
setReady (true);
mOperation = 0; // the first operation
}
while (0);
uninit();
return rc;
}
const BSTR aOperationDescription)
{
LogFlowMember(("Progress::init(): <undescriptioned>\n"));
do
{
// set ready to let protectedUninit() be called on failure
setReady (true);
mOperation = 0; // the first operation
}
while (0);
uninit();
return rc;
}
/**
* Uninitializes the instance and sets the ready flag to FALSE.
* Called either from FinalRelease() or by the parent when it gets destroyed.
*/
{
LogFlowMember (("Progress::uninit()\n"));
if (!isReady())
return;
// wake up all threads still waiting by occasion
if (mWaitersCount > 0)
{
LogFlow (("WARNING: There are still %d threads waiting for '%ls' completion!\n",
}
}
// IProgress properties
/////////////////////////////////////////////////////////////////////////////
// IProgress methods
/////////////////////////////////////////////////////////////////////////////
/**
* @note
* XPCOM: when this method is called not on the main XPCOM thread, it
* it simply blocks the thread until mCompletedSem is signalled. If the
* thread has its own event queue (hmm, what for?) that it must run, then
* calling this method will definitey freese event processing.
*/
{
CHECK_READY();
// if we're already completed, take a shortcut
if (!mCompleted)
{
int vrc = VINF_SUCCESS;
{
mWaitersCount ++;
: (unsigned) timeLeft);
mWaitersCount --;
// the progress might have been uninitialized
if (!isReady())
break;
// the last waiter resets the semaphore
if (mWaitersCount == 0)
break;
if (!forever)
{
}
}
}
LogFlowMember(("Progress::WaitForCompletion: END\n"));
return S_OK;
}
/**
* @note
* XPCOM: when this method is called not on the main XPCOM thread, it
* it simply blocks the thread until mCompletedSem is signalled. If the
* thread has its own event queue (hmm, what for?) that it must run, then
* calling this method will definitey freese event processing.
*/
{
LogFlowMember(("Progress::WaitForOperationCompletion: BEGIN: "
CHECK_READY();
if (aOperation >= mOperationCount)
// if we're already completed or if the given operation is already done,
// then take a shortcut
{
int vrc = VINF_SUCCESS;
{
mWaitersCount ++;
: (unsigned) timeLeft);
mWaitersCount --;
// the progress might have been uninitialized
if (!isReady())
break;
// the last waiter resets the semaphore
if (mWaitersCount == 0)
break;
if (!forever)
{
}
}
}
LogFlowMember(("Progress::WaitForOperationCompletion: END\n"));
return S_OK;
}
{
CHECK_READY();
if (!mCancelable)
/// @todo (dmik): implement operation cancellation!
// mCompleted = TRUE;
// mCanceled = TRUE;
// return S_OK;
ComAssertMsgFailed (("Not implemented!"));
return E_NOTIMPL;
}
// public methods only for internal purposes
/////////////////////////////////////////////////////////////////////////////
/**
* Updates the percentage value of the current operation.
*
* @param aPercent New percentage value of the operation in progress
* (in range [0, 100]).
*/
{
return S_OK;
}
/**
* Signals that the current operation is successfully completed
* and advances to the next operation. The operation percentage is reset
* to 0.
*
* @param aOperationDescription Description of the next operation
*
* @note
* The current operation must not be the last one.
*/
{
mOperation ++;
mOperationPercent = 0;
// wake up all waiting threads
if (mWaitersCount > 0)
return S_OK;
}
/**
* Marks the whole task as complete and sets the result code.
*
* If the result code indicates a failure (|FAILED (@a aResultCode)|)
* then this method will import the error info from the current
* thread and assign it to the errorInfo attribute (it will return an
* error if no info is available in such case).
*
* If the result code indicates a success (|SUCCEEDED (@a aResultCode)|)
* then the current operation is set to the last
*
* @param aResultCode Operation result code
*/
{
mCompleted = TRUE;
if (FAILED (aResultCode))
{
/* try to import error info from the current thread */
#if defined (__WIN__)
{
}
#else // !defined (__WIN__)
if (NS_SUCCEEDED (rc))
{
if (NS_SUCCEEDED (rc))
{
{
}
}
}
#endif // !defined (__WIN__)
}
else
{
mOperationPercent = 100;
}
#if !defined VBOX_COM_INPROC
/* remove from the global collection of pending progress operations */
if (mParent)
#endif
/* wake up all waiting threads */
if (mWaitersCount > 0)
return rc;
}
/**
* Marks the operation as complete and attaches full error info.
* See VirtualBoxSupportErrorInfoImpl::setError(HRESULT, const GUID &, const wchar_t *, const char *, ...)
* for more info.
*
* @param aResultCode operation result (error) code, must not be S_OK
* @param aIID IID of the intrface that defines the error
* @param aComponent name of the component that generates the error
* @param aText error message (must not be null), an RTStrPrintf-like
* format string in UTF-8 encoding
* @param ... list of arguments for the format string
*/
const Bstr &aComponent,
const char *aText, ...)
{
}
/**
* Marks the operation as complete and attaches full error info.
* See VirtualBoxSupportErrorInfoImpl::setError(HRESULT, const GUID &, const wchar_t *, const char *, ...)
* for more info.
*
* This method is preferred iy you have a ready (translated and formatted)
* Bstr string, because it omits an extra conversion Utf8Str -> Bstr.
*
* @param aResultCode operation result (error) code, must not be S_OK
* @param aIID IID of the intrface that defines the error
* @param aComponent name of the component that generates the error
* @param aText error message (must not be null)
*/
{
mCompleted = TRUE;
AssertComRC (rc);
{
}
#if !defined VBOX_COM_INPROC
/* remove from the global collection of pending progress operations */
if (mParent)
#endif
/* wake up all waiting threads */
if (mWaitersCount > 0)
return rc;
}
////////////////////////////////////////////////////////////////////////////////
// CombinedProgress class
////////////////////////////////////////////////////////////////////////////////
{
return rc;
mProgress = 0;
mCompletedOperations = 0;
return S_OK;
}
void CombinedProgress::FinalRelease()
{
uninit();
}
// public initializer/uninitializer for internal purposes only
////////////////////////////////////////////////////////////////////////////////
/**
* Initializes this object based on individual combined progresses.
* Must be called only from #init()!
*
* @param aParent see ProgressBase::init()
* @param aInitiator see ProgressBase::init()
* @param aDescription see ProgressBase::init()
* @param aId see ProgressBase::init()
*/
#if !defined (VBOX_COM_INPROC)
#endif
{
LogFlowMember (("CombinedProgress::protectedInit(): "
"aDescription={%ls} mProgresses.size()=%d\n",
do
{
#if !defined (VBOX_COM_INPROC)
#endif
// set ready to let protectedUninit() be called on failure
setReady (true);
mProgress = 0; // the first object
mCompletedOperations = 0;
mCompleted = FALSE;
mOperationCount = 0; // will be calculated later
mOperation = 0;
{
if (mCancelable)
{
return rc;
if (!cancelable)
mCancelable = FALSE;
}
{
return rc;
}
}
rc = checkProgress();
}
while (0);
uninit();
return rc;
}
/**
* Uninitializes the instance and sets the ready flag to FALSE.
* Called either from FinalRelease() or by the parent when it gets destroyed.
*/
void CombinedProgress::uninit()
{
LogFlowMember (("CombinedProgress::uninit()\n"));
if (!isReady())
return;
mProgress = 0;
mProgresses.clear();
}
// IProgress properties
////////////////////////////////////////////////////////////////////////////////
{
if (!aPercent)
return E_POINTER;
CHECK_READY();
*aPercent = 100;
else
{
return rc;
// global percent = (100 / mOperationCount) * mOperation +
// ((100 / mOperationCount) / 100) * mOperationPercent
}
return S_OK;
}
{
if (!aCompleted)
return E_POINTER;
CHECK_READY();
return rc;
}
{
if (!aCanceled)
return E_POINTER;
CHECK_READY();
return rc;
}
{
if (!aResultCode)
return E_POINTER;
CHECK_READY();
return rc;
}
{
if (!aErrorInfo)
return E_POINTER;
CHECK_READY();
return rc;
}
{
if (!aOperation)
return E_POINTER;
CHECK_READY();
return rc;
}
{
if (!aOperationDescription)
return E_POINTER;
CHECK_READY();
return rc;
}
{
if (!aOperationPercent)
return E_POINTER;
CHECK_READY();
return rc;
}
// IProgress methods
/////////////////////////////////////////////////////////////////////////////
/**
* @note
* XPCOM: when this method is called not on the main XPCOM thread, it
* it simply blocks the thread until mCompletedSem is signalled. If the
* thread has its own event queue (hmm, what for?) that it must run, then
* calling this method will definitey freese event processing.
*/
{
LogFlowMember (("CombinedProgress::WaitForCompletion: BEGIN: timeout=%d\n",
aTimeout));
CHECK_READY();
// if we're already completed, take a shortcut
if (!mCompleted)
{
{
// the progress might have been uninitialized
if (!isReady())
break;
rc = checkProgress();
break;
if (!forever)
{
}
}
return rc;
}
LogFlowMember(("CombinedProgress::WaitForCompletion: END\n"));
return S_OK;
}
/**
* @note
* XPCOM: when this method is called not on the main XPCOM thread, it
* it simply blocks the thread until mCompletedSem is signalled. If the
* thread has its own event queue (hmm, what for?) that it must run, then
* calling this method will definitey freese event processing.
*/
{
LogFlowMember(("CombinedProgress::WaitForOperationCompletion: BEGIN: "
CHECK_READY();
if (aOperation >= mOperationCount)
// if we're already completed or if the given operation is already done,
// then take a shortcut
{
// find the right progress object to wait for
do
{
return rc;
{
// found the right progress object
break;
}
completedOps += opCount;
progress ++;
}
while (1);
LogFlowMember (("CombinedProgress::WaitForOperationCompletion(): "
"will wait for mProgresses [%d] (%d)\n",
{
// wait for the appropriate progress operation completion
// the progress might have been uninitialized
if (!isReady())
break;
rc = checkProgress();
break;
if (!forever)
{
}
}
return rc;
}
LogFlowMember(("CombinedProgress::WaitForOperationCompletion: END\n"));
return S_OK;
}
{
CHECK_READY();
if (!mCancelable)
/// @todo (dmik): implement operation cancellation!
// mCompleted = TRUE;
// mCanceled = TRUE;
// return S_OK;
}
// private methods
////////////////////////////////////////////////////////////////////////////////
/**
* Fetches the properties of the current progress object and, if it is
* successfully completed, advances to the next uncompleted or unsucessfully
* completed object in the vector of combined progress objects.
*
* @note Must be called from under the object's lock!
*/
{
// do nothing if we're already marked ourselves as completed
if (mCompleted)
return S_OK;
do
{
return rc;
if (completed)
{
return rc;
return rc;
if (FAILED (mResultCode))
{
return rc;
}
{
mCompleted = TRUE;
}
else
{
return rc;
mProgress ++;
else
mCompleted = TRUE;
}
}
}
while (completed && !mCompleted);
{
{
}
}
return rc;
}