VirtualBox

Changeset 54992 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Mar 27, 2015 3:45:03 PM (10 years ago)
Author:
vboxsync
Message:

Main/Progress: big cleanup eliminating inconsistencies and redundant code

Location:
trunk/src/VBox/Main
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/include/ProgressImpl.h

    r51687 r54992  
    66
    77/*
    8  * Copyright (C) 2006-2014 Oracle Corporation
     8 * Copyright (C) 2006-2015 Oracle Corporation
    99 *
    1010 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    3333    public ProgressWrap
    3434{
    35 protected:
    36 
    37     DECLARE_EMPTY_CTOR_DTOR (Progress)
    38 
    39     void i_checkForAutomaticTimeout(void);
    40 
    41 #if !defined (VBOX_COM_INPROC)
    42     /** Weak parent. */
    43     VirtualBox * const      mParent;
    44 #endif
    45 
    46     const ComPtr<IUnknown>  mInitiator;
    47 
    48     const Guid mId;
    49     const com::Utf8Str mDescription;
    50 
    51     uint64_t m_ullTimestamp;                        // progress object creation timestamp, for ETA computation
    52 
    53     void (*m_pfnCancelCallback)(void *);
    54     void *m_pvCancelUserArg;
    55 
    56     /* The fields below are to be properly initialized by subclasses */
    57 
    58     BOOL mCompleted;
    59     BOOL mCancelable;
    60     BOOL mCanceled;
    61     HRESULT mResultCode;
    62     ComPtr<IVirtualBoxErrorInfo> mErrorInfo;
    63 
    64     ULONG m_cOperations;                            // number of operations (so that progress dialog can
    65                                                     // display something like 1/3)
    66     ULONG m_ulTotalOperationsWeight;                // sum of weights of all operations, given to constructor
    67 
    68     ULONG m_ulOperationsCompletedWeight;            // summed-up weight of operations that have been completed; initially 0
    69 
    70     ULONG m_ulCurrentOperation;                     // operations counter, incremented with
    71                                                     // each setNextOperation()
    72     com::Utf8Str m_operationDescription;            // name of current operation; initially
    73                                                     // from constructor, changed with setNextOperation()
    74     ULONG m_ulCurrentOperationWeight;               // weight of current operation, given to setNextOperation()
    75     ULONG m_ulOperationPercent;                     // percentage of current operation, set with setCurrentOperationProgress()
    76     ULONG m_cMsTimeout;                             /**< Automatic timeout value. 0 means none. */
    77 
    7835public:
    79     DECLARE_NOT_AGGREGATABLE (Progress)
     36    DECLARE_NOT_AGGREGATABLE(Progress)
    8037
    8138    HRESULT FinalConstruct();
     
    9451     */
    9552    HRESULT init(
    96 #if !defined (VBOX_COM_INPROC)
     53#if !defined(VBOX_COM_INPROC)
    9754                  VirtualBox *aParent,
    9855#endif
     
    10259    {
    10360        return init(
    104 #if !defined (VBOX_COM_INPROC)
     61#if !defined(VBOX_COM_INPROC)
    10562            aParent,
    10663#endif
     
    12683     */
    12784    HRESULT init(
    128 #if !defined (VBOX_COM_INPROC)
     85#if !defined(VBOX_COM_INPROC)
    12986                  VirtualBox *aParent,
    13087#endif
     
    13592    {
    13693        return init(
    137 #if !defined (VBOX_COM_INPROC)
     94#if !defined(VBOX_COM_INPROC)
    13895            aParent,
    13996#endif
     
    148105
    149106    HRESULT init(
    150 #if !defined (VBOX_COM_INPROC)
     107#if !defined(VBOX_COM_INPROC)
    151108                  VirtualBox *aParent,
    152109#endif
     
    167124
    168125    // public methods only for internal purposes
    169     HRESULT i_setResultCode(HRESULT aResultCode);
    170 
    171126    HRESULT i_notifyComplete(HRESULT aResultCode);
    172127    HRESULT i_notifyComplete(HRESULT aResultCode,
     
    180135                              const char *aText,
    181136                              va_list va);
     137    HRESULT i_notifyCompleteEI(HRESULT aResultCode,
     138                               const ComPtr<IVirtualBoxErrorInfo> &aErrorInfo);
     139
    182140    bool i_notifyPointOfNoReturn(void);
    183 
    184     // public methods only for internal purposes
    185141    bool i_setCancelCallback(void (*pfnCallback)(void *), void *pvUser);
    186142
    187     // unsafe inline public methods for internal purposes only (ensure there is
    188     // a caller and a read lock before calling them!)
    189     BOOL i_getCompleted() const { return mCompleted; }
    190     HRESULT i_getResultCode() const { return mResultCode; }
    191     double i_calcTotalPercent();
     143protected:
     144    DECLARE_EMPTY_CTOR_DTOR(Progress)
     145
     146#if !defined(VBOX_COM_INPROC)
     147    /** Weak parent. */
     148    VirtualBox * const      mParent;
     149#endif
     150
     151    const ComPtr<IUnknown>  mInitiator;
     152
     153    const Guid mId;
     154    const com::Utf8Str mDescription;
     155
     156    uint64_t m_ullTimestamp;                        // progress object creation timestamp, for ETA computation
     157
     158    void (*m_pfnCancelCallback)(void *);
     159    void *m_pvCancelUserArg;
     160
     161    /* The fields below are to be properly initialized by subclasses */
     162
     163    BOOL mCompleted;
     164    BOOL mCancelable;
     165    BOOL mCanceled;
     166    HRESULT mResultCode;
     167    ComPtr<IVirtualBoxErrorInfo> mErrorInfo;
     168
     169    ULONG m_cOperations;                            // number of operations (so that progress dialog can
     170                                                    // display something like 1/3)
     171    ULONG m_ulTotalOperationsWeight;                // sum of weights of all operations, given to constructor
     172
     173    ULONG m_ulOperationsCompletedWeight;            // summed-up weight of operations that have been completed; initially 0
     174
     175    ULONG m_ulCurrentOperation;                     // operations counter, incremented with
     176                                                    // each setNextOperation()
     177    com::Utf8Str m_operationDescription;            // name of current operation; initially
     178                                                    // from constructor, changed with setNextOperation()
     179    ULONG m_ulCurrentOperationWeight;               // weight of current operation, given to setNextOperation()
     180    ULONG m_ulOperationPercent;                     // percentage of current operation, set with setCurrentOperationProgress()
     181    ULONG m_cMsTimeout;                             /**< Automatic timeout value. 0 means none. */
    192182
    193183private:
    194 
    195     // Wrapped IProgress Data
     184    // wrapped IProgress properties
    196185    HRESULT getId(com::Guid &aId);
    197186    HRESULT getDescription(com::Utf8Str &aDescription);
     
    211200    HRESULT getTimeout(ULONG *aTimeout);
    212201    HRESULT setTimeout(ULONG aTimeout);
     202
     203    // wrapped IProgress methods
    213204    HRESULT setCurrentOperationProgress(ULONG aPercent);
    214205    HRESULT setNextOperation(const com::Utf8Str &aNextOperationDescription,
    215206                             ULONG aNextOperationsWeight);
    216 
    217     // Wrapped Iprogress methods
    218207    HRESULT waitForCompletion(LONG aTimeout);
    219208    HRESULT waitForOperationCompletion(ULONG aOperation,
     
    222211    HRESULT cancel();
    223212
     213    // internal helper methods
     214    double i_calcTotalPercent();
     215    void i_checkForAutomaticTimeout(void);
    224216
    225217    RTSEMEVENTMULTI mCompletedSem;
  • trunk/src/VBox/Main/src-all/ProgressImpl.cpp

    r51687 r54992  
    66
    77/*
    8  * Copyright (C) 2006-2014 Oracle Corporation
     8 * Copyright (C) 2006-2015 Oracle Corporation
    99 *
    1010 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    5454}
    5555
    56 
    57 // IProgress properties
    58 /////////////////////////////////////////////////////////////////////////////
    59 
    60 HRESULT Progress::getId(com::Guid &aId)
    61 {
    62     /* mId is constant during life time, no need to lock */
    63     aId = mId;
    64 
    65     return S_OK;
    66 }
    67 
    68 HRESULT Progress::getDescription(com::Utf8Str &aDescription)
    69 {
    70     /* mDescription is constant during life time, no need to lock */
    71     aDescription = mDescription;
    72 
    73     return S_OK;
    74 }
    75 HRESULT Progress::getInitiator(ComPtr<IUnknown> &aInitiator)
    76 {
    77 
    78     /* mInitiator/mParent are constant during life time, no need to lock */
    79 
    80 #if !defined(VBOX_COM_INPROC)
    81     if (mInitiator)
    82         mInitiator.queryInterfaceTo(aInitiator.asOutParam());
    83     else
    84     {
    85         ComObjPtr<VirtualBox> pVirtualBox(mParent);
    86         pVirtualBox.queryInterfaceTo(aInitiator.asOutParam());
    87     }
    88 #else
    89     mInitiator.queryInterfaceTo(aInitiator.asOutParam());
    90 #endif
    91 
    92     return S_OK;
    93 }
    94 
    95 HRESULT Progress::getCancelable(BOOL *aCancelable)
    96 {
    97 
    98     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    99 
    100     *aCancelable = mCancelable;
    101 
    102     return S_OK;
    103 }
    104 
    105 /**
    106  * Internal helper to compute the total percent value based on the member values and
    107  * returns it as a "double". This is used both by GetPercent (which returns it as a
    108  * rounded ULONG) and GetTimeRemaining().
    109  *
    110  * Requires locking by the caller!
    111  *
    112  * @return fractional percentage as a double value.
    113  */
    114 double Progress::i_calcTotalPercent()
    115 {
    116     // avoid division by zero
    117     if (m_ulTotalOperationsWeight == 0)
    118         return 0;
    119 
    120     double dPercent = (    (double)m_ulOperationsCompletedWeight  // weight of operations that have been completed
    121                          + ((double)m_ulOperationPercent *
    122                             (double)m_ulCurrentOperationWeight / (double)100)  // plus partial weight of the current operation
    123                       ) * (double)100 / (double)m_ulTotalOperationsWeight;
    124 
    125     return dPercent;
    126 }
    127 
    128 /**
    129  * Internal helper for automatically timing out the operation.
    130  *
    131  * The caller should hold the object write lock.
    132  */
    133 void Progress::i_checkForAutomaticTimeout(void)
    134 {
    135     if (   m_cMsTimeout
    136         && mCancelable
    137         && !mCanceled
    138         && RTTimeMilliTS() - m_ullTimestamp > m_cMsTimeout
    139        )
    140         Cancel();
    141 }
    142 
    143 HRESULT Progress::getTimeRemaining(LONG *aTimeRemaining)
    144 {
    145     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    146 
    147     if (mCompleted)
    148         *aTimeRemaining = 0;
    149     else
    150     {
    151         double dPercentDone = i_calcTotalPercent();
    152         if (dPercentDone < 1)
    153             *aTimeRemaining = -1;       // unreliable, or avoid division by 0 below
    154         else
    155         {
    156             uint64_t ullTimeNow = RTTimeMilliTS();
    157             uint64_t ullTimeElapsed = ullTimeNow - m_ullTimestamp;
    158             uint64_t ullTimeTotal = (uint64_t)(ullTimeElapsed * 100 / dPercentDone);
    159             uint64_t ullTimeRemaining = ullTimeTotal - ullTimeElapsed;
    160 
    161 //          LogFunc(("dPercentDone = %RI32, ullTimeNow = %RI64, ullTimeElapsed = %RI64, ullTimeTotal = %RI64, ullTimeRemaining = %RI64\n",
    162 //                   (uint32_t)dPercentDone, ullTimeNow, ullTimeElapsed, ullTimeTotal, ullTimeRemaining));
    163 
    164             *aTimeRemaining = (LONG)(ullTimeRemaining / 1000);
    165         }
    166     }
    167 
    168     return S_OK;
    169 }
    170 
    171 HRESULT Progress::getPercent(ULONG *aPercent)
    172 {
    173     i_checkForAutomaticTimeout();
    174 
    175     /* checkForAutomaticTimeout requires a write lock. */
    176     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    177 
    178     if (mCompleted && SUCCEEDED(mResultCode))
    179         *aPercent = 100;
    180     else
    181     {
    182         ULONG ulPercent = (ULONG)i_calcTotalPercent();
    183         // do not report 100% until we're really really done with everything as the Qt GUI dismisses progress dialogs in that case
    184         if (    ulPercent == 100
    185              && (    m_ulOperationPercent < 100
    186                   || (m_ulCurrentOperation < m_cOperations -1)
    187                 )
    188            )
    189             *aPercent = 99;
    190         else
    191             *aPercent = ulPercent;
    192     }
    193 
    194     i_checkForAutomaticTimeout();
    195 
    196     return S_OK;
    197 }
    198 
    199 HRESULT Progress::getCompleted(BOOL *aCompleted)
    200 {
    201     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    202 
    203     *aCompleted = mCompleted;
    204 
    205     return S_OK;
    206 }
    207 
    208 HRESULT Progress::getCanceled(BOOL *aCanceled)
    209 {
    210     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    211 
    212     *aCanceled = mCanceled;
    213 
    214     return S_OK;
    215 }
    216 
    217 HRESULT Progress::getResultCode(LONG *aResultCode)
    218 {
    219     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    220 
    221     if (!mCompleted)
    222         return setError(E_FAIL,
    223                         tr("Result code is not available, operation is still in progress"));
    224 
    225     *aResultCode = mResultCode;
    226 
    227     return S_OK;
    228 }
    229 
    230 HRESULT Progress::getErrorInfo(ComPtr<IVirtualBoxErrorInfo> &aErrorInfo)
    231 {
    232     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    233 
    234     if (!mCompleted)
    235         return setError(E_FAIL,
    236                         tr("Error info is not available, operation is still in progress"));
    237 
    238     mErrorInfo.queryInterfaceTo(aErrorInfo.asOutParam());
    239 
    240     return S_OK;
    241 }
    242 
    243 HRESULT Progress::getOperationCount(ULONG *aOperationCount)
    244 {
    245     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    246 
    247     *aOperationCount = m_cOperations;
    248 
    249     return S_OK;
    250 }
    251 
    252 HRESULT Progress::getOperation(ULONG *aOperation)
    253 {
    254     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    255 
    256     *aOperation = m_ulCurrentOperation;
    257 
    258     return S_OK;
    259 }
    260 
    261 HRESULT Progress::getOperationDescription(com::Utf8Str &aOperationDescription)
    262 {
    263 
    264     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    265 
    266     aOperationDescription = m_operationDescription;
    267 
    268     return S_OK;
    269 }
    270 
    271 HRESULT Progress::getOperationPercent(ULONG *aOperationPercent)
    272 {
    273     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    274 
    275     if (mCompleted && SUCCEEDED(mResultCode))
    276         *aOperationPercent = 100;
    277     else
    278         *aOperationPercent = m_ulOperationPercent;
    279 
    280     return S_OK;
    281 }
    282 
    283 HRESULT Progress::getOperationWeight(ULONG *aOperationWeight)
    284 {
    285     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    286 
    287     *aOperationWeight = m_ulCurrentOperationWeight;
    288 
    289     return S_OK;
    290 }
    291 
    292 HRESULT Progress::setTimeout(ULONG aTimeout)
    293 {
    294     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    295 
    296     if (!mCancelable)
    297         return setError(VBOX_E_INVALID_OBJECT_STATE,
    298                         tr("Operation cannot be canceled"));
    299 
    300     LogThisFunc(("%#x => %#x\n", m_cMsTimeout, aTimeout));
    301     m_cMsTimeout = aTimeout;
    302     return S_OK;
    303 }
    304 
    305 HRESULT Progress::getTimeout(ULONG *aTimeout)
    306 {
    307     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    308 
    309     *aTimeout = m_cMsTimeout;
    310     return S_OK;
    311 }
    312 
    313 // public methods only for internal purposes
    314 ////////////////////////////////////////////////////////////////////////////////
    315 
    316 /**
    317  * Sets the cancelation callback, checking for cancelation first.
    318  *
    319  * @returns Success indicator.
    320  * @retval  true on success.
    321  * @retval  false if the progress object has already been canceled or is in an
    322  *          invalid state
    323  *
    324  * @param   pfnCallback     The function to be called upon cancelation.
    325  * @param   pvUser          The callback argument.
    326  */
    327 bool Progress::i_setCancelCallback(void (*pfnCallback)(void *), void *pvUser)
    328 {
    329     AutoCaller autoCaller(this);
    330     AssertComRCReturn(autoCaller.rc(), false);
    331 
    332     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    333 
    334     i_checkForAutomaticTimeout();
    335     if (mCanceled)
    336         return false;
    337 
    338     m_pvCancelUserArg   = pvUser;
    339     m_pfnCancelCallback = pfnCallback;
    340     return true;
    341 }
    34256
    34357HRESULT Progress::FinalConstruct()
     
    36377    m_pvCancelUserArg = NULL;
    36478
    365     HRESULT rc = Progress::BaseFinalConstruct();
    366     if (FAILED(rc)) return rc;
    367 
    36879    mCompletedSem = NIL_RTSEMEVENTMULTI;
    36980    mWaitersCount = 0;
    37081
    371     return S_OK;
     82    return Progress::BaseFinalConstruct();
    37283}
    37384
     
    607318    RTSemEventMultiDestroy(mCompletedSem);
    608319
    609     /* release initiator (effective only if mInitiator has been assigned in
    610  *      * init()) */
     320    /* release initiator (effective only if mInitiator has been assigned in init()) */
    611321    unconst(mInitiator).setNull();
    612322
     
    624334
    625335
     336// public methods only for internal purposes
     337////////////////////////////////////////////////////////////////////////////////
     338
     339/**
     340 * Marks the whole task as complete and sets the result code.
     341 *
     342 * If the result code indicates a failure (|FAILED(@a aResultCode)|) then this
     343 * method will import the error info from the current thread and assign it to
     344 * the errorInfo attribute (it will return an error if no info is available in
     345 * such case).
     346 *
     347 * If the result code indicates a success (|SUCCEEDED(@a aResultCode)|) then
     348 * the current operation is set to the last.
     349 *
     350 * Note that this method may be called only once for the given Progress object.
     351 * Subsequent calls will assert.
     352 *
     353 * @param aResultCode   Operation result code.
     354 */
     355HRESULT Progress::i_notifyComplete(HRESULT aResultCode)
     356{
     357    HRESULT rc;
     358    ComPtr<VirtualBoxErrorInfo> errorInfo;
     359    if (FAILED(aResultCode))
     360    {
     361        /* try to import error info from the current thread */
     362#if !defined(VBOX_WITH_XPCOM)
     363        ComPtr<IErrorInfo> err;
     364        rc = ::GetErrorInfo(0, err.asOutParam());
     365        if (rc == S_OK && err)
     366            rc = err.queryInterfaceTo(mErrorInfo.asOutParam());
     367#else /* !defined(VBOX_WITH_XPCOM) */
     368        nsCOMPtr<nsIExceptionService> es;
     369        es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
     370        if (NS_SUCCEEDED(rc))
     371        {
     372            nsCOMPtr <nsIExceptionManager> em;
     373            rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
     374            if (NS_SUCCEEDED(rc))
     375            {
     376                ComPtr<nsIException> ex;
     377                rc = em->GetCurrentException(ex.asOutParam());
     378                if (NS_SUCCEEDED(rc) && ex)
     379                    rc = ex.queryInterfaceTo(errorInfo.asOutParam());
     380            }
     381        }
     382#endif /* !defined(VBOX_WITH_XPCOM) */
     383    }
     384
     385    return i_notifyCompleteEI(aResultCode, errorInfo);
     386}
     387
     388/**
     389 * Wrapper around Progress:notifyCompleteV.
     390 */
     391HRESULT Progress::i_notifyComplete(HRESULT aResultCode,
     392                                   const GUID &aIID,
     393                                   const char *pcszComponent,
     394                                   const char *aText,
     395                                   ...)
     396{
     397    va_list va;
     398    va_start(va, aText);
     399    HRESULT hrc = i_notifyCompleteV(aResultCode, aIID, pcszComponent, aText, va);
     400    va_end(va);
     401    return hrc;
     402}
     403
     404/**
     405 * Marks the operation as complete and attaches full error info.
     406 *
     407 * @param aResultCode   Operation result (error) code, must not be S_OK.
     408 * @param aIID          IID of the interface that defines the error.
     409 * @param aComponent    Name of the component that generates the error.
     410 * @param aText         Error message (must not be null), an RTStrPrintf-like
     411 *                      format string in UTF-8 encoding.
     412 * @param va            List of arguments for the format string.
     413 */
     414HRESULT Progress::i_notifyCompleteV(HRESULT aResultCode,
     415                                    const GUID &aIID,
     416                                    const char *pcszComponent,
     417                                    const char *aText,
     418                                    va_list va)
     419{
     420    /* expected to be used only in case of error */
     421    Assert(FAILED(aResultCode));
     422
     423    Utf8Str text(aText, va);
     424    ComObjPtr<VirtualBoxErrorInfo> errorInfo;
     425    HRESULT rc = errorInfo.createObject();
     426    AssertComRCReturnRC(rc);
     427    errorInfo->init(aResultCode, aIID, pcszComponent, text);
     428
     429    return i_notifyCompleteEI(aResultCode, errorInfo);
     430}
     431
     432/**
     433 * Marks the operation as complete and attaches full error info.
     434 *
     435 * This is where the actual work is done, the related methods all end up here.
     436 *
     437 * @param aResultCode   Operation result (error) code, must not be S_OK.
     438 * @param aErrorInfo            List of arguments for the format string.
     439 */
     440HRESULT Progress::i_notifyCompleteEI(HRESULT aResultCode, const ComPtr<IVirtualBoxErrorInfo> &aErrorInfo)
     441{
     442    LogThisFunc(("aResultCode=%d\n", aResultCode));
     443    /* on failure we expect error info, on success there must be none */
     444    AssertMsg(FAILED(aResultCode) ^ aErrorInfo.isNull(),
     445              ("No error info but trying to set a failed result (%08X)!\n",
     446               aResultCode));
     447
     448    AutoCaller autoCaller(this);
     449    AssertComRCReturnRC(autoCaller.rc());
     450
     451    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     452
     453    AssertReturn(mCompleted == FALSE, E_FAIL);
     454
     455    if (mCanceled && SUCCEEDED(aResultCode))
     456        aResultCode = E_FAIL;
     457
     458    mCompleted = TRUE;
     459    mResultCode = aResultCode;
     460    if (SUCCEEDED(aResultCode))
     461    {
     462        m_ulCurrentOperation = m_cOperations - 1; /* last operation */
     463        m_ulOperationPercent = 100;
     464    }
     465    mErrorInfo = aErrorInfo;
     466
     467#if !defined VBOX_COM_INPROC
     468    /* remove from the global collection of pending progress operations */
     469    if (mParent)
     470        mParent->i_removeProgress(mId.ref());
     471#endif
     472
     473    /* wake up all waiting threads */
     474    if (mWaitersCount > 0)
     475        RTSemEventMultiSignal(mCompletedSem);
     476
     477    return S_OK;
     478}
     479
     480/**
     481 * Notify the progress object that we're almost at the point of no return.
     482 *
     483 * This atomically checks for and disables cancelation.  Calls to
     484 * IProgress::Cancel() made after a successful call to this method will fail
     485 * and the user can be told.  While this isn't entirely clean behavior, it
     486 * prevents issues with an irreversible actually operation succeeding while the
     487 * user believe it was rolled back.
     488 *
     489 * @returns Success indicator.
     490 * @retval  true on success.
     491 * @retval  false if the progress object has already been canceled or is in an
     492 *          invalid state
     493 */
     494bool Progress::i_notifyPointOfNoReturn(void)
     495{
     496    AutoCaller autoCaller(this);
     497    AssertComRCReturn(autoCaller.rc(), false);
     498
     499    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     500
     501    if (mCanceled)
     502    {
     503        LogThisFunc(("returns false\n"));
     504        return false;
     505    }
     506
     507    mCancelable = FALSE;
     508    LogThisFunc(("returns true\n"));
     509    return true;
     510}
     511
     512/**
     513 * Sets the cancelation callback, checking for cancelation first.
     514 *
     515 * @returns Success indicator.
     516 * @retval  true on success.
     517 * @retval  false if the progress object has already been canceled or is in an
     518 *          invalid state
     519 *
     520 * @param   pfnCallback     The function to be called upon cancelation.
     521 * @param   pvUser          The callback argument.
     522 */
     523bool Progress::i_setCancelCallback(void (*pfnCallback)(void *), void *pvUser)
     524{
     525    AutoCaller autoCaller(this);
     526    AssertComRCReturn(autoCaller.rc(), false);
     527
     528    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     529
     530    i_checkForAutomaticTimeout();
     531    if (mCanceled)
     532        return false;
     533
     534    m_pvCancelUserArg   = pvUser;
     535    m_pfnCancelCallback = pfnCallback;
     536    return true;
     537}
     538
     539
    626540// IProgress properties
    627541/////////////////////////////////////////////////////////////////////////////
    628542
     543HRESULT Progress::getId(com::Guid &aId)
     544{
     545    /* mId is constant during life time, no need to lock */
     546    aId = mId;
     547
     548    return S_OK;
     549}
     550
     551HRESULT Progress::getDescription(com::Utf8Str &aDescription)
     552{
     553    /* mDescription is constant during life time, no need to lock */
     554    aDescription = mDescription;
     555
     556    return S_OK;
     557}
     558HRESULT Progress::getInitiator(ComPtr<IUnknown> &aInitiator)
     559{
     560    /* mInitiator/mParent are constant during life time, no need to lock */
     561#if !defined(VBOX_COM_INPROC)
     562    if (mInitiator)
     563        mInitiator.queryInterfaceTo(aInitiator.asOutParam());
     564    else
     565    {
     566        ComObjPtr<VirtualBox> pVirtualBox(mParent);
     567        pVirtualBox.queryInterfaceTo(aInitiator.asOutParam());
     568    }
     569#else
     570    mInitiator.queryInterfaceTo(aInitiator.asOutParam());
     571#endif
     572
     573    return S_OK;
     574}
     575
     576HRESULT Progress::getCancelable(BOOL *aCancelable)
     577{
     578    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     579
     580    *aCancelable = mCancelable;
     581
     582    return S_OK;
     583}
     584
     585HRESULT Progress::getPercent(ULONG *aPercent)
     586{
     587    /* i_checkForAutomaticTimeout requires a write lock. */
     588    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     589
     590    if (mCompleted && SUCCEEDED(mResultCode))
     591        *aPercent = 100;
     592    else
     593    {
     594        ULONG ulPercent = (ULONG)i_calcTotalPercent();
     595        // do not report 100% until we're really really done with everything
     596        // as the Qt GUI dismisses progress dialogs in that case
     597        if (    ulPercent == 100
     598             && (    m_ulOperationPercent < 100
     599                  || (m_ulCurrentOperation < m_cOperations -1)
     600                )
     601           )
     602            *aPercent = 99;
     603        else
     604            *aPercent = ulPercent;
     605    }
     606
     607    i_checkForAutomaticTimeout();
     608
     609    return S_OK;
     610}
     611
     612HRESULT Progress::getTimeRemaining(LONG *aTimeRemaining)
     613{
     614    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     615
     616    if (mCompleted)
     617        *aTimeRemaining = 0;
     618    else
     619    {
     620        double dPercentDone = i_calcTotalPercent();
     621        if (dPercentDone < 1)
     622            *aTimeRemaining = -1;       // unreliable, or avoid division by 0 below
     623        else
     624        {
     625            uint64_t ullTimeNow = RTTimeMilliTS();
     626            uint64_t ullTimeElapsed = ullTimeNow - m_ullTimestamp;
     627            uint64_t ullTimeTotal = (uint64_t)(ullTimeElapsed * 100 / dPercentDone);
     628            uint64_t ullTimeRemaining = ullTimeTotal - ullTimeElapsed;
     629
     630//          LogFunc(("dPercentDone = %RI32, ullTimeNow = %RI64, ullTimeElapsed = %RI64, ullTimeTotal = %RI64, ullTimeRemaining = %RI64\n",
     631//                   (uint32_t)dPercentDone, ullTimeNow, ullTimeElapsed, ullTimeTotal, ullTimeRemaining));
     632
     633            *aTimeRemaining = (LONG)(ullTimeRemaining / 1000);
     634        }
     635    }
     636
     637    return S_OK;
     638}
     639
     640HRESULT Progress::getCompleted(BOOL *aCompleted)
     641{
     642    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     643
     644    *aCompleted = mCompleted;
     645
     646    return S_OK;
     647}
     648
     649HRESULT Progress::getCanceled(BOOL *aCanceled)
     650{
     651    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     652
     653    *aCanceled = mCanceled;
     654
     655    return S_OK;
     656}
     657
     658HRESULT Progress::getResultCode(LONG *aResultCode)
     659{
     660    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     661
     662    if (!mCompleted)
     663        return setError(E_FAIL,
     664                        tr("Result code is not available, operation is still in progress"));
     665
     666    *aResultCode = mResultCode;
     667
     668    return S_OK;
     669}
     670
     671HRESULT Progress::getErrorInfo(ComPtr<IVirtualBoxErrorInfo> &aErrorInfo)
     672{
     673    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     674
     675    if (!mCompleted)
     676        return setError(E_FAIL,
     677                        tr("Error info is not available, operation is still in progress"));
     678
     679    mErrorInfo.queryInterfaceTo(aErrorInfo.asOutParam());
     680
     681    return S_OK;
     682}
     683
     684HRESULT Progress::getOperationCount(ULONG *aOperationCount)
     685{
     686    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     687
     688    *aOperationCount = m_cOperations;
     689
     690    return S_OK;
     691}
     692
     693HRESULT Progress::getOperation(ULONG *aOperation)
     694{
     695    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     696
     697    *aOperation = m_ulCurrentOperation;
     698
     699    return S_OK;
     700}
     701
     702HRESULT Progress::getOperationDescription(com::Utf8Str &aOperationDescription)
     703{
     704    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     705
     706    aOperationDescription = m_operationDescription;
     707
     708    return S_OK;
     709}
     710
     711HRESULT Progress::getOperationPercent(ULONG *aOperationPercent)
     712{
     713    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     714
     715    if (mCompleted && SUCCEEDED(mResultCode))
     716        *aOperationPercent = 100;
     717    else
     718        *aOperationPercent = m_ulOperationPercent;
     719
     720    return S_OK;
     721}
     722
     723HRESULT Progress::getOperationWeight(ULONG *aOperationWeight)
     724{
     725    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     726
     727    *aOperationWeight = m_ulCurrentOperationWeight;
     728
     729    return S_OK;
     730}
     731
     732HRESULT Progress::getTimeout(ULONG *aTimeout)
     733{
     734    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     735
     736    *aTimeout = m_cMsTimeout;
     737
     738    return S_OK;
     739}
     740
     741HRESULT Progress::setTimeout(ULONG aTimeout)
     742{
     743    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     744
     745    if (!mCancelable)
     746        return setError(VBOX_E_INVALID_OBJECT_STATE,
     747                        tr("Operation cannot be canceled"));
     748    m_cMsTimeout = aTimeout;
     749
     750    return S_OK;
     751}
     752
     753
    629754// IProgress methods
    630755/////////////////////////////////////////////////////////////////////////////
     756
     757/**
     758 * Updates the percentage value of the current operation.
     759 *
     760 * @param aPercent  New percentage value of the operation in progress
     761 *                  (in range [0, 100]).
     762 */
     763HRESULT Progress::setCurrentOperationProgress(ULONG aPercent)
     764{
     765    AssertMsgReturn(aPercent <= 100, ("%u\n", aPercent), E_INVALIDARG);
     766
     767    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     768
     769    i_checkForAutomaticTimeout();
     770    if (mCancelable && mCanceled)
     771        AssertReturn(!mCompleted, E_FAIL);
     772    AssertReturn(!mCompleted && !mCanceled, E_FAIL);
     773
     774    m_ulOperationPercent = aPercent;
     775
     776    return S_OK;
     777}
     778
     779/**
     780 * Signals that the current operation is successfully completed and advances to
     781 * the next operation. The operation percentage is reset to 0.
     782 *
     783 * @param aOperationDescription     Description of the next operation.
     784 *
     785 * @note The current operation must not be the last one.
     786 */
     787HRESULT Progress::setNextOperation(const com::Utf8Str &aNextOperationDescription, ULONG aNextOperationsWeight)
     788{
     789    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     790
     791    if (mCanceled)
     792        return E_FAIL;
     793    AssertReturn(!mCompleted, E_FAIL);
     794    AssertReturn(m_ulCurrentOperation + 1 < m_cOperations, E_FAIL);
     795
     796    ++m_ulCurrentOperation;
     797    m_ulOperationsCompletedWeight += m_ulCurrentOperationWeight;
     798
     799    m_operationDescription = aNextOperationDescription;
     800    m_ulCurrentOperationWeight = aNextOperationsWeight;
     801    m_ulOperationPercent = 0;
     802
     803    LogThisFunc(("%s: aNextOperationsWeight = %d; m_ulCurrentOperation is now %d, m_ulOperationsCompletedWeight is now %d\n",
     804                 m_operationDescription.c_str(), aNextOperationsWeight, m_ulCurrentOperation, m_ulOperationsCompletedWeight));
     805
     806    /* wake up all waiting threads */
     807    if (mWaitersCount > 0)
     808        RTSemEventMultiSignal(mCompletedSem);
     809
     810    return S_OK;
     811}
    631812
    632813/**
     
    8541035}
    8551036
    856 /**
    857  * Updates the percentage value of the current operation.
    858  *
    859  * @param aPercent  New percentage value of the operation in progress
    860  *                  (in range [0, 100]).
    861  */
    862 HRESULT Progress::setCurrentOperationProgress(ULONG aPercent)
    863 {
    864     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    865 
    866     AssertMsgReturn(aPercent <= 100, ("%u\n", aPercent), E_INVALIDARG);
    867 
    868     i_checkForAutomaticTimeout();
    869     if (mCancelable && mCanceled)
    870     {
    871         Assert(!mCompleted);
    872         return E_FAIL;
    873     }
    874     AssertReturn(!mCompleted && !mCanceled, E_FAIL);
    875 
    876     m_ulOperationPercent = aPercent;
    877 
    878     return S_OK;
    879 }
    880 
    881 /**
    882  * Signals that the current operation is successfully completed and advances to
    883  * the next operation. The operation percentage is reset to 0.
    884  *
    885  * @param aOperationDescription     Description of the next operation.
    886  *
    887  * @note The current operation must not be the last one.
    888  */
    889 HRESULT Progress::setNextOperation(const com::Utf8Str &aNextOperationDescription, ULONG aNextOperationsWeight)
    890 
    891 {
    892     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    893 
    894     if (mCanceled)
    895         return E_FAIL;
    896     AssertReturn(!mCompleted, E_FAIL);
    897     AssertReturn(m_ulCurrentOperation + 1 < m_cOperations, E_FAIL);
    898 
    899     ++m_ulCurrentOperation;
    900     m_ulOperationsCompletedWeight += m_ulCurrentOperationWeight;
    901 
    902     m_operationDescription = aNextOperationDescription;
    903     m_ulCurrentOperationWeight = aNextOperationsWeight;
    904     m_ulOperationPercent = 0;
    905 
    906     Log(("Progress::setNextOperation(%s): aNextOperationsWeight = %d; m_ulCurrentOperation is now %d, m_ulOperationsCompletedWeight is now %d\n",
    907          m_operationDescription.c_str(), aNextOperationsWeight, m_ulCurrentOperation, m_ulOperationsCompletedWeight));
    908 
    909     /* wake up all waiting threads */
    910     if (mWaitersCount > 0)
    911         RTSemEventMultiSignal(mCompletedSem);
    912 
    913     return S_OK;
    914 }
    915 
    916 // public methods only for internal purposes
     1037
     1038// private internal helpers
    9171039/////////////////////////////////////////////////////////////////////////////
    9181040
    9191041/**
    920  * Sets the internal result code and attempts to retrieve additional error
    921  * info from the current thread. Gets called from Progress::notifyComplete(),
    922  * but can be called again to override a previous result set with
    923  * notifyComplete().
    924  *
    925  * @param aResultCode
    926  */
    927 HRESULT Progress::i_setResultCode(HRESULT aResultCode)
    928 {
    929     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    930 
    931     mResultCode = aResultCode;
    932 
    933     HRESULT rc = S_OK;
    934 
    935     if (FAILED(aResultCode))
    936     {
    937         /* try to import error info from the current thread */
    938 
    939 #if !defined(VBOX_WITH_XPCOM)
    940 
    941         ComPtr<IErrorInfo> err;
    942         rc = ::GetErrorInfo(0, err.asOutParam());
    943         if (rc == S_OK && err)
    944         {
    945             rc = err.queryInterfaceTo(mErrorInfo.asOutParam());
    946             if (SUCCEEDED(rc) && !mErrorInfo)
    947                 rc = E_FAIL;
    948         }
    949 
    950 #else /* !defined(VBOX_WITH_XPCOM) */
    951 
    952         nsCOMPtr<nsIExceptionService> es;
    953         es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
    954         if (NS_SUCCEEDED(rc))
    955         {
    956             nsCOMPtr <nsIExceptionManager> em;
    957             rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
    958             if (NS_SUCCEEDED(rc))
    959             {
    960                 ComPtr<nsIException> ex;
    961                 rc = em->GetCurrentException(ex.asOutParam());
    962                 if (NS_SUCCEEDED(rc) && ex)
    963                 {
    964                     rc = ex.queryInterfaceTo(mErrorInfo.asOutParam());
    965                     if (NS_SUCCEEDED(rc) && !mErrorInfo)
    966                         rc = E_FAIL;
    967                 }
    968             }
    969         }
    970 #endif /* !defined(VBOX_WITH_XPCOM) */
    971 
    972         AssertMsg(rc == S_OK, ("Couldn't get error info (rc=%08X) while trying to set a failed result (%08X)!\n",
    973                                rc, aResultCode));
    974     }
    975 
    976     return rc;
    977 }
    978 
    979 /**
    980  * Marks the whole task as complete and sets the result code.
    981  *
    982  * If the result code indicates a failure (|FAILED(@a aResultCode)|) then this
    983  * method will import the error info from the current thread and assign it to
    984  * the errorInfo attribute (it will return an error if no info is available in
    985  * such case).
    986  *
    987  * If the result code indicates a success (|SUCCEEDED(@a aResultCode)|) then
    988  * the current operation is set to the last.
    989  *
    990  * Note that this method may be called only once for the given Progress object.
    991  * Subsequent calls will assert.
    992  *
    993  * @param aResultCode   Operation result code.
    994  */
    995 HRESULT Progress::i_notifyComplete(HRESULT aResultCode)
    996 {
    997     AutoCaller autoCaller(this);
    998     AssertComRCReturnRC(autoCaller.rc());
    999 
    1000     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1001 
    1002     AssertReturn(mCompleted == FALSE, E_FAIL);
    1003 
    1004     LogFunc(("aResultCode=%d\n", aResultCode));
    1005 
    1006     if (mCanceled && SUCCEEDED(aResultCode))
    1007         aResultCode = E_FAIL;
    1008 
    1009     HRESULT rc = i_setResultCode(aResultCode);
    1010 
    1011     mCompleted = TRUE;
    1012 
    1013     if (!FAILED(aResultCode))
    1014     {
    1015         m_ulCurrentOperation = m_cOperations - 1; /* last operation */
    1016         m_ulOperationPercent = 100;
    1017     }
    1018 
    1019 #if !defined VBOX_COM_INPROC
    1020     /* remove from the global collection of pending progress operations */
    1021     if (mParent)
    1022         mParent->i_removeProgress(mId.ref());
    1023 #endif
    1024 
    1025     /* wake up all waiting threads */
    1026     if (mWaitersCount > 0)
    1027         RTSemEventMultiSignal(mCompletedSem);
    1028 
    1029     return rc;
    1030 }
    1031 
    1032 /**
    1033  * Wrapper around Progress:notifyCompleteV.
    1034  */
    1035 HRESULT Progress::i_notifyComplete(HRESULT aResultCode,
    1036                                    const GUID &aIID,
    1037                                    const char *pcszComponent,
    1038                                    const char *aText,
    1039                                    ...)
    1040 {
    1041     va_list va;
    1042     va_start(va, aText);
    1043     HRESULT hrc = i_notifyCompleteV(aResultCode, aIID, pcszComponent, aText, va);
    1044     va_end(va);
    1045     return hrc;
    1046 }
    1047 
    1048 /**
    1049  * Marks the operation as complete and attaches full error info.
    1050  *
    1051  * See VirtualBoxBase::setError(HRESULT, const GUID &, const wchar_t
    1052  * *, const char *, ...) for more info.
    1053  *
    1054  * @param aResultCode   Operation result (error) code, must not be S_OK.
    1055  * @param aIID          IID of the interface that defines the error.
    1056  * @param aComponent    Name of the component that generates the error.
    1057  * @param aText         Error message (must not be null), an RTStrPrintf-like
    1058  *                      format string in UTF-8 encoding.
    1059  * @param va            List of arguments for the format string.
    1060  */
    1061 HRESULT Progress::i_notifyCompleteV(HRESULT aResultCode,
    1062                                     const GUID &aIID,
    1063                                     const char *pcszComponent,
    1064                                     const char *aText,
    1065                                     va_list va)
    1066 {
    1067     Utf8Str text(aText, va);
    1068 
    1069     AutoCaller autoCaller(this);
    1070     AssertComRCReturnRC(autoCaller.rc());
    1071 
    1072     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1073 
    1074     AssertReturn(mCompleted == FALSE, E_FAIL);
    1075 
    1076     if (mCanceled && SUCCEEDED(aResultCode))
    1077         aResultCode = E_FAIL;
    1078 
    1079     mCompleted = TRUE;
    1080     mResultCode = aResultCode;
    1081 
    1082     AssertReturn(FAILED(aResultCode), E_FAIL);
    1083 
    1084     ComObjPtr<VirtualBoxErrorInfo> errorInfo;
    1085     HRESULT rc = errorInfo.createObject();
    1086     AssertComRC(rc);
    1087     if (SUCCEEDED(rc))
    1088     {
    1089         errorInfo->init(aResultCode, aIID, pcszComponent, text);
    1090         errorInfo.queryInterfaceTo(mErrorInfo.asOutParam());
    1091     }
    1092 
    1093 #if !defined VBOX_COM_INPROC
    1094     /* remove from the global collection of pending progress operations */
    1095     if (mParent)
    1096         mParent->i_removeProgress(mId.ref());
    1097 #endif
    1098 
    1099     /* wake up all waiting threads */
    1100     if (mWaitersCount > 0)
    1101         RTSemEventMultiSignal(mCompletedSem);
    1102 
    1103     return rc;
    1104 }
    1105 
    1106 /**
    1107  * Notify the progress object that we're almost at the point of no return.
    1108  *
    1109  * This atomically checks for and disables cancelation.  Calls to
    1110  * IProgress::Cancel() made after a successful call to this method will fail
    1111  * and the user can be told.  While this isn't entirely clean behavior, it
    1112  * prevents issues with an irreversible actually operation succeeding while the
    1113  * user believe it was rolled back.
    1114  *
    1115  * @returns Success indicator.
    1116  * @retval  true on success.
    1117  * @retval  false if the progress object has already been canceled or is in an
    1118  *          invalid state
    1119  */
    1120 bool Progress::i_notifyPointOfNoReturn(void)
    1121 {
    1122     AutoCaller autoCaller(this);
    1123     AssertComRCReturn(autoCaller.rc(), false);
    1124 
    1125     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1126 
    1127     if (mCanceled)
    1128     {
    1129         LogThisFunc(("returns false\n"));
    1130         return false;
    1131     }
    1132 
    1133     mCancelable = FALSE;
    1134     LogThisFunc(("returns true\n"));
    1135     return true;
    1136 }
    1137 
     1042 * Internal helper to compute the total percent value based on the member values and
     1043 * returns it as a "double". This is used both by GetPercent (which returns it as a
     1044 * rounded ULONG) and GetTimeRemaining().
     1045 *
     1046 * Requires locking by the caller!
     1047 *
     1048 * @return fractional percentage as a double value.
     1049 */
     1050double Progress::i_calcTotalPercent()
     1051{
     1052    // avoid division by zero
     1053    if (m_ulTotalOperationsWeight == 0)
     1054        return 0.0;
     1055
     1056    double dPercent = (    (double)m_ulOperationsCompletedWeight  // weight of operations that have been completed
     1057                         + ((double)m_ulOperationPercent *
     1058                            (double)m_ulCurrentOperationWeight / 100.0)  // plus partial weight of the current operation
     1059                      ) * 100.0 / (double)m_ulTotalOperationsWeight;
     1060
     1061    return dPercent;
     1062}
     1063
     1064/**
     1065 * Internal helper for automatically timing out the operation.
     1066 *
     1067 * The caller must hold the object write lock.
     1068 */
     1069void Progress::i_checkForAutomaticTimeout(void)
     1070{
     1071    AssertReturnVoid(isWriteLockOnCurrentThread());
     1072
     1073    if (   m_cMsTimeout
     1074        && mCancelable
     1075        && !mCanceled
     1076        && RTTimeMilliTS() - m_ullTimestamp > m_cMsTimeout)
     1077        Cancel();
     1078}
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette