VirtualBox

Changeset 73743 in vbox for trunk/src/VBox/Main/src-all


Ignore:
Timestamp:
Aug 17, 2018 5:56:34 PM (7 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
124424
Message:

Main/Progress+Appliance+Machine: Turn IProgress::waitForAsyncProgressCompletion into an internal method. It is not needed by any client code outside the API, it's simply is too special. Also include the error propagation which it originally skipped (leding to reduntant code in the calling code). Remove a replicated version of it from the Appliance code which seems to be the half-forgotten ancestor of the method in IProgress. Adapt the users of the method to the new internal method, which made the code easier to read.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/src-all/ProgressImpl.cpp

    r73716 r73743  
    486486}
    487487
    488 /**
    489  * Notify the progress object that we're almost at the point of no return.
    490  *
    491  * This atomically checks for and disables cancelation.  Calls to
    492  * IProgress::Cancel() made after a successful call to this method will fail
    493  * and the user can be told.  While this isn't entirely clean behavior, it
    494  * prevents issues with an irreversible actually operation succeeding while the
    495  * user believe it was rolled back.
    496  *
    497  * @returns Success indicator.
    498  * @retval  true on success.
    499  * @retval  false if the progress object has already been canceled or is in an
    500  *          invalid state
    501  */
    502 bool Progress::i_notifyPointOfNoReturn(void)
    503 {
    504     AutoCaller autoCaller(this);
    505     AssertComRCReturn(autoCaller.rc(), false);
    506 
    507     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    508 
    509     if (mCanceled)
    510     {
    511         LogThisFunc(("returns false\n"));
    512         return false;
    513     }
    514 
    515     mCancelable = FALSE;
    516     LogThisFunc(("returns true\n"));
    517     return true;
    518 }
    519 
    520 /**
    521  * Sets the cancelation callback, checking for cancelation first.
    522  *
    523  * @returns Success indicator.
    524  * @retval  true on success.
    525  * @retval  false if the progress object has already been canceled or is in an
    526  *          invalid state
    527  *
    528  * @param   pfnCallback     The function to be called upon cancelation.
    529  * @param   pvUser          The callback argument.
    530  */
    531 bool Progress::i_setCancelCallback(void (*pfnCallback)(void *), void *pvUser)
    532 {
    533     AutoCaller autoCaller(this);
    534     AssertComRCReturn(autoCaller.rc(), false);
    535 
    536     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    537 
    538     i_checkForAutomaticTimeout();
    539     if (mCanceled)
    540         return false;
    541 
    542     m_pvCancelUserArg   = pvUser;
    543     m_pfnCancelCallback = pfnCallback;
    544     return true;
    545 }
    546 
    547 /**
    548  * @callback_method_impl{FNRTPROGRESS,
    549  *      Works the progress of the current operation.}
    550  */
    551 /*static*/ DECLCALLBACK(int) Progress::i_iprtProgressCallback(unsigned uPercentage, void *pvUser)
    552 {
    553     Progress *pThis = (Progress *)pvUser;
    554 
    555     /*
    556      * Same as setCurrentOperationProgress, except we don't fail on mCompleted.
    557      */
    558     AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
    559     int vrc = VINF_SUCCESS;
    560     if (!pThis->mCompleted)
    561     {
    562         pThis->i_checkForAutomaticTimeout();
    563         if (!pThis->mCanceled)
    564         {
    565             if (uPercentage > pThis->m_ulOperationPercent)
    566                 pThis->setCurrentOperationProgress(uPercentage);
    567         }
    568         else
    569         {
    570             Assert(pThis->mCancelable);
    571             vrc = VERR_CANCELLED;
    572         }
    573     }
    574     /* else ignored */
    575     return vrc;
    576 }
    577 
    578 /**
    579  * @callback_method_impl{FNVDPROGRESS,
    580  *      Progress::i_iprtProgressCallback with parameters switched around.}
    581  */
    582 /*static*/ DECLCALLBACK(int) Progress::i_vdProgressCallback(void *pvUser, unsigned uPercentage)
    583 {
    584     return i_iprtProgressCallback(uPercentage, pvUser);
    585 }
    586 
    587 // IProgress properties
    588 /////////////////////////////////////////////////////////////////////////////
    589 
    590 HRESULT Progress::getId(com::Guid &aId)
    591 {
    592     /* mId is constant during life time, no need to lock */
    593     aId = mId;
    594 
    595     return S_OK;
    596 }
    597 
    598 HRESULT Progress::getDescription(com::Utf8Str &aDescription)
    599 {
    600     /* mDescription is constant during life time, no need to lock */
    601     aDescription = mDescription;
    602 
    603     return S_OK;
    604 }
    605 HRESULT Progress::getInitiator(ComPtr<IUnknown> &aInitiator)
    606 {
    607     /* mInitiator/mParent are constant during life time, no need to lock */
    608 #if !defined(VBOX_COM_INPROC)
    609     if (mInitiator)
    610         mInitiator.queryInterfaceTo(aInitiator.asOutParam());
    611     else
    612     {
    613         ComObjPtr<VirtualBox> pVirtualBox(mParent);
    614         pVirtualBox.queryInterfaceTo(aInitiator.asOutParam());
    615     }
    616 #else
    617     mInitiator.queryInterfaceTo(aInitiator.asOutParam());
    618 #endif
    619 
    620     return S_OK;
    621 }
    622 
    623 HRESULT Progress::getCancelable(BOOL *aCancelable)
    624 {
    625     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    626 
    627     *aCancelable = mCancelable;
    628 
    629     return S_OK;
    630 }
    631 
    632 HRESULT Progress::getPercent(ULONG *aPercent)
    633 {
    634     /* i_checkForAutomaticTimeout requires a write lock. */
    635     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    636 
    637     if (mCompleted && SUCCEEDED(mResultCode))
    638         *aPercent = 100;
    639     else
    640     {
    641         ULONG ulPercent = (ULONG)i_calcTotalPercent();
    642         // do not report 100% until we're really really done with everything
    643         // as the Qt GUI dismisses progress dialogs in that case
    644         if (    ulPercent == 100
    645              && (    m_ulOperationPercent < 100
    646                   || (m_ulCurrentOperation < m_cOperations -1)
    647                 )
    648            )
    649             *aPercent = 99;
    650         else
    651             *aPercent = ulPercent;
    652     }
    653 
    654     i_checkForAutomaticTimeout();
    655 
    656     return S_OK;
    657 }
    658 
    659 HRESULT Progress::getTimeRemaining(LONG *aTimeRemaining)
    660 {
    661     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    662 
    663     if (mCompleted)
    664         *aTimeRemaining = 0;
    665     else
    666     {
    667         double dPercentDone = i_calcTotalPercent();
    668         if (dPercentDone < 1)
    669             *aTimeRemaining = -1;       // unreliable, or avoid division by 0 below
    670         else
    671         {
    672             uint64_t ullTimeNow = RTTimeMilliTS();
    673             uint64_t ullTimeElapsed = ullTimeNow - m_ullTimestamp;
    674             uint64_t ullTimeTotal = (uint64_t)((double)ullTimeElapsed * 100 / dPercentDone);
    675             uint64_t ullTimeRemaining = ullTimeTotal - ullTimeElapsed;
    676 
    677 //          LogFunc(("dPercentDone = %RI32, ullTimeNow = %RI64, ullTimeElapsed = %RI64, ullTimeTotal = %RI64, ullTimeRemaining = %RI64\n",
    678 //                   (uint32_t)dPercentDone, ullTimeNow, ullTimeElapsed, ullTimeTotal, ullTimeRemaining));
    679 
    680             *aTimeRemaining = (LONG)(ullTimeRemaining / 1000);
    681         }
    682     }
    683 
    684     return S_OK;
    685 }
    686 
    687 HRESULT Progress::getCompleted(BOOL *aCompleted)
    688 {
    689     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    690 
    691     *aCompleted = mCompleted;
    692 
    693     return S_OK;
    694 }
    695 
    696 HRESULT Progress::getCanceled(BOOL *aCanceled)
    697 {
    698     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    699 
    700     *aCanceled = mCanceled;
    701 
    702     return S_OK;
    703 }
    704 
    705 HRESULT Progress::getResultCode(LONG *aResultCode)
    706 {
    707     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    708 
    709     if (!mCompleted)
    710         return setError(E_FAIL, tr("Result code is not available, operation is still in progress"));
    711 
    712     *aResultCode = mResultCode;
    713 
    714     return S_OK;
    715 }
    716 
    717 HRESULT Progress::getErrorInfo(ComPtr<IVirtualBoxErrorInfo> &aErrorInfo)
    718 {
    719     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    720 
    721     if (!mCompleted)
    722         return setError(E_FAIL, tr("Error info is not available, operation is still in progress"));
    723 
    724     mErrorInfo.queryInterfaceTo(aErrorInfo.asOutParam());
    725 
    726     return S_OK;
    727 }
    728 
    729 HRESULT Progress::getOperationCount(ULONG *aOperationCount)
    730 {
    731     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    732 
    733     *aOperationCount = m_cOperations;
    734 
    735     return S_OK;
    736 }
    737 
    738 HRESULT Progress::getOperation(ULONG *aOperation)
    739 {
    740     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    741 
    742     *aOperation = m_ulCurrentOperation;
    743 
    744     return S_OK;
    745 }
    746 
    747 HRESULT Progress::getOperationDescription(com::Utf8Str &aOperationDescription)
    748 {
    749     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    750 
    751     aOperationDescription = m_operationDescription;
    752 
    753     return S_OK;
    754 }
    755 
    756 HRESULT Progress::getOperationPercent(ULONG *aOperationPercent)
    757 {
    758     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    759 
    760     if (mCompleted && SUCCEEDED(mResultCode))
    761         *aOperationPercent = 100;
    762     else
    763         *aOperationPercent = m_ulOperationPercent;
    764 
    765     return S_OK;
    766 }
    767 
    768 HRESULT Progress::getOperationWeight(ULONG *aOperationWeight)
    769 {
    770     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    771 
    772     *aOperationWeight = m_ulCurrentOperationWeight;
    773 
    774     return S_OK;
    775 }
    776 
    777 HRESULT Progress::getTimeout(ULONG *aTimeout)
    778 {
    779     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    780 
    781     *aTimeout = m_cMsTimeout;
    782 
    783     return S_OK;
    784 }
    785 
    786 HRESULT Progress::setTimeout(ULONG aTimeout)
    787 {
    788     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    789 
    790     if (!mCancelable)
    791         return setError(VBOX_E_INVALID_OBJECT_STATE, tr("Operation cannot be canceled"));
    792     m_cMsTimeout = aTimeout;
    793 
    794     return S_OK;
    795 }
    796 
    797 
    798 // IProgress methods
    799 /////////////////////////////////////////////////////////////////////////////
    800 
    801 /**
    802  * Updates the percentage value of the current operation.
    803  *
    804  * @param aPercent  New percentage value of the operation in progress
    805  *                  (in range [0, 100]).
    806  */
    807 HRESULT Progress::setCurrentOperationProgress(ULONG aPercent)
    808 {
    809     AssertMsgReturn(aPercent <= 100, ("%u\n", aPercent), E_INVALIDARG);
    810 
    811     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    812 
    813     i_checkForAutomaticTimeout();
    814     if (mCancelable && mCanceled)
    815         AssertReturn(!mCompleted, E_FAIL);
    816     AssertReturn(!mCompleted && !mCanceled, E_FAIL);
    817 
    818     if (m_ulOperationPercent != aPercent)
    819     {
    820         m_ulOperationPercent = aPercent;
    821         ULONG actualPercent = 0;
    822         getPercent(&actualPercent);
    823         fireProgressPercentageChangedEvent(pEventSource, mId.toUtf16().raw(), actualPercent);
    824     }
    825 
    826     return S_OK;
    827 }
    828 
    829 /**
    830  * Signals that the current operation is successfully completed and advances to
    831  * the next operation. The operation percentage is reset to 0.
    832  *
    833  * @param aNextOperationDescription  Description of the next operation.
    834  * @param aNextOperationsWeight     Weight of the next operation.
    835  *
    836  * @note The current operation must not be the last one.
    837  */
    838 HRESULT Progress::setNextOperation(const com::Utf8Str &aNextOperationDescription, ULONG aNextOperationsWeight)
    839 {
    840     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    841 
    842     if (mCanceled)
    843         return E_FAIL;
    844     AssertReturn(!mCompleted, E_FAIL);
    845     AssertReturn(m_ulCurrentOperation + 1 < m_cOperations, E_FAIL);
    846 
    847     ++m_ulCurrentOperation;
    848     m_ulOperationsCompletedWeight += m_ulCurrentOperationWeight;
    849 
    850     m_operationDescription = aNextOperationDescription;
    851     m_ulCurrentOperationWeight = aNextOperationsWeight;
    852     m_ulOperationPercent = 0;
    853 
    854     LogThisFunc(("%s: aNextOperationsWeight = %d; m_ulCurrentOperation is now %d, m_ulOperationsCompletedWeight is now %d\n",
    855                  m_operationDescription.c_str(), aNextOperationsWeight, m_ulCurrentOperation, m_ulOperationsCompletedWeight));
    856 
    857     /* wake up all waiting threads */
    858     if (mWaitersCount > 0)
    859         RTSemEventMultiSignal(mCompletedSem);
    860 
    861     ULONG actualPercent = 0;
    862     getPercent(&actualPercent);
    863     fireProgressPercentageChangedEvent(pEventSource, mId.toUtf16().raw(), actualPercent);
    864 
    865     return S_OK;
    866 }
    867 
    868 /**
    869  * @note XPCOM: when this method is not called on the main XPCOM thread, it
    870  *       simply blocks the thread until mCompletedSem is signalled. If the
    871  *       thread has its own event queue (hmm, what for?) that it must run, then
    872  *       calling this method will definitely freeze event processing.
    873  */
    874 HRESULT Progress::waitForCompletion(LONG aTimeout)
     488HRESULT Progress::i_waitForOtherProgressCompletion(const ComPtr<IProgress> &aProgressOther)
    875489{
    876490    LogFlowThisFuncEnter();
    877     LogFlowThisFunc(("aTimeout=%d\n", aTimeout));
    878 
    879     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    880 
    881     /* if we're already completed, take a shortcut */
    882     if (!mCompleted)
    883     {
    884         int vrc = VINF_SUCCESS;
    885         bool fForever = aTimeout < 0;
    886         int64_t timeLeft = aTimeout;
    887         int64_t lastTime = RTTimeMilliTS();
    888 
    889         while (!mCompleted && (fForever || timeLeft > 0))
    890         {
    891             mWaitersCount++;
    892             alock.release();
    893             vrc = RTSemEventMultiWait(mCompletedSem,
    894                                       fForever ? RT_INDEFINITE_WAIT : (RTMSINTERVAL)timeLeft);
    895             alock.acquire();
    896             mWaitersCount--;
    897 
    898             /* the last waiter resets the semaphore */
    899             if (mWaitersCount == 0)
    900                 RTSemEventMultiReset(mCompletedSem);
    901 
    902             if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
    903                 break;
    904 
    905             if (!fForever)
    906             {
    907                 int64_t now = RTTimeMilliTS();
    908                 timeLeft -= now - lastTime;
    909                 lastTime = now;
    910             }
    911         }
    912 
    913         if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
    914             return setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to wait for the task completion (%Rrc)"), vrc);
    915     }
    916 
    917     LogFlowThisFuncLeave();
    918 
    919     return S_OK;
    920 }
    921 
    922 /**
    923  * @note XPCOM: when this method is not called on the main XPCOM thread, it
    924  *       simply blocks the thread until mCompletedSem is signalled. If the
    925  *       thread has its own event queue (hmm, what for?) that it must run, then
    926  *       calling this method will definitely freeze event processing.
    927  */
    928 HRESULT Progress::waitForOperationCompletion(ULONG aOperation, LONG aTimeout)
    929 
    930 {
    931     LogFlowThisFuncEnter();
    932     LogFlowThisFunc(("aOperation=%d, aTimeout=%d\n", aOperation, aTimeout));
    933 
    934     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    935 
    936     CheckComArgExpr(aOperation, aOperation < m_cOperations);
    937 
    938     /* if we're already completed or if the given operation is already done,
    939      * then take a shortcut */
    940     if (    !mCompleted
    941          && aOperation >= m_ulCurrentOperation)
    942     {
    943         int vrc = VINF_SUCCESS;
    944         bool fForever = aTimeout < 0;
    945         int64_t timeLeft = aTimeout;
    946         int64_t lastTime = RTTimeMilliTS();
    947 
    948         while (    !mCompleted && aOperation >= m_ulCurrentOperation
    949                 && (fForever || timeLeft > 0))
    950         {
    951             mWaitersCount ++;
    952             alock.release();
    953             vrc = RTSemEventMultiWait(mCompletedSem,
    954                                       fForever ? RT_INDEFINITE_WAIT : (unsigned) timeLeft);
    955             alock.acquire();
    956             mWaitersCount--;
    957 
    958             /* the last waiter resets the semaphore */
    959             if (mWaitersCount == 0)
    960                 RTSemEventMultiReset(mCompletedSem);
    961 
    962             if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
    963                 break;
    964 
    965             if (!fForever)
    966             {
    967                 int64_t now = RTTimeMilliTS();
    968                 timeLeft -= now - lastTime;
    969                 lastTime = now;
    970             }
    971         }
    972 
    973         if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
    974             return setErrorBoth(E_FAIL, vrc, tr("Failed to wait for the operation completion (%Rrc)"), vrc);
    975     }
    976 
    977     LogFlowThisFuncLeave();
    978 
    979     return S_OK;
    980 }
    981 
    982 HRESULT Progress::waitForAsyncProgressCompletion(const ComPtr<IProgress> &aPProgressAsync)
    983 {
    984     LogFlowThisFuncEnter();
    985 
    986     /* Note: we don't lock here, cause we just using public methods. */
     491
     492    /* Note: no locking needed, because we just use public methods. */
    987493
    988494    HRESULT rc           = S_OK;
     
    994500    ULONG cOp            = 0;
    995501    /* Is the async process cancelable? */
    996     rc = aPProgressAsync->COMGETTER(Cancelable)(&fCancelable);
     502    rc = aProgressOther->COMGETTER(Cancelable)(&fCancelable);
    997503    if (FAILED(rc)) return rc;
    998504    /* Loop as long as the sync process isn't completed. */
    999     while (SUCCEEDED(aPProgressAsync->COMGETTER(Completed(&fCompleted))))
     505    while (SUCCEEDED(aProgressOther->COMGETTER(Completed(&fCompleted))))
    1000506    {
    1001507        /* We can forward any cancel request to the async process only when
     
    1007513            if (fCanceled)
    1008514            {
    1009                 rc = aPProgressAsync->Cancel();
     515                rc = aProgressOther->Cancel();
    1010516                if (FAILED(rc)) return rc;
    1011517            }
     
    1024530            for (;;)
    1025531            {
    1026                 rc = aPProgressAsync->COMGETTER(Operation(&curOp));
     532                rc = aProgressOther->COMGETTER(Operation(&curOp));
    1027533                if (FAILED(rc)) return rc;
    1028534                if (cOp != curOp)
     
    1030536                    Bstr bstr;
    1031537                    ULONG currentWeight;
    1032                     rc = aPProgressAsync->COMGETTER(OperationDescription(bstr.asOutParam()));
     538                    rc = aProgressOther->COMGETTER(OperationDescription(bstr.asOutParam()));
    1033539                    if (FAILED(rc)) return rc;
    1034                     rc = aPProgressAsync->COMGETTER(OperationWeight(&currentWeight));
     540                    rc = aProgressOther->COMGETTER(OperationWeight(&currentWeight));
    1035541                    if (FAILED(rc)) return rc;
    1036542                    rc = SetNextOperation(bstr.raw(), currentWeight);
     
    1042548            }
    1043549
    1044             rc = aPProgressAsync->COMGETTER(OperationPercent(&currentPercent));
     550            rc = aProgressOther->COMGETTER(OperationPercent(&currentPercent));
    1045551            if (FAILED(rc)) return rc;
    1046552            if (currentPercent != prevPercent)
     
    1055561
    1056562        /* Make sure the loop is not too tight */
    1057         rc = aPProgressAsync->WaitForCompletion(100);
     563        rc = aProgressOther->WaitForCompletion(100);
    1058564        if (FAILED(rc)) return rc;
    1059565    }
    1060566
     567    /* Transfer error information if applicable and report the error status
     568     * back to the caller to make this as easy as possible. */
     569    LONG iRc;
     570    rc = aProgressOther->COMGETTER(ResultCode)(&iRc);
     571    if (FAILED(rc)) return rc;
     572    if (FAILED(iRc))
     573    {
     574        setError(ProgressErrorInfo(aProgressOther));
     575        rc = iRc;
     576    }
     577
    1061578    LogFlowThisFuncLeave();
    1062 
    1063579    return rc;
     580}
     581
     582/**
     583 * Notify the progress object that we're almost at the point of no return.
     584 *
     585 * This atomically checks for and disables cancelation.  Calls to
     586 * IProgress::Cancel() made after a successful call to this method will fail
     587 * and the user can be told.  While this isn't entirely clean behavior, it
     588 * prevents issues with an irreversible actually operation succeeding while the
     589 * user believe it was rolled back.
     590 *
     591 * @returns Success indicator.
     592 * @retval  true on success.
     593 * @retval  false if the progress object has already been canceled or is in an
     594 *          invalid state
     595 */
     596bool Progress::i_notifyPointOfNoReturn(void)
     597{
     598    AutoCaller autoCaller(this);
     599    AssertComRCReturn(autoCaller.rc(), false);
     600
     601    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     602
     603    if (mCanceled)
     604    {
     605        LogThisFunc(("returns false\n"));
     606        return false;
     607    }
     608
     609    mCancelable = FALSE;
     610    LogThisFunc(("returns true\n"));
     611    return true;
     612}
     613
     614/**
     615 * Sets the cancelation callback, checking for cancelation first.
     616 *
     617 * @returns Success indicator.
     618 * @retval  true on success.
     619 * @retval  false if the progress object has already been canceled or is in an
     620 *          invalid state
     621 *
     622 * @param   pfnCallback     The function to be called upon cancelation.
     623 * @param   pvUser          The callback argument.
     624 */
     625bool Progress::i_setCancelCallback(void (*pfnCallback)(void *), void *pvUser)
     626{
     627    AutoCaller autoCaller(this);
     628    AssertComRCReturn(autoCaller.rc(), false);
     629
     630    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     631
     632    i_checkForAutomaticTimeout();
     633    if (mCanceled)
     634        return false;
     635
     636    m_pvCancelUserArg   = pvUser;
     637    m_pfnCancelCallback = pfnCallback;
     638    return true;
     639}
     640
     641/**
     642 * @callback_method_impl{FNRTPROGRESS,
     643 *      Works the progress of the current operation.}
     644 */
     645/*static*/ DECLCALLBACK(int) Progress::i_iprtProgressCallback(unsigned uPercentage, void *pvUser)
     646{
     647    Progress *pThis = (Progress *)pvUser;
     648
     649    /*
     650     * Same as setCurrentOperationProgress, except we don't fail on mCompleted.
     651     */
     652    AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
     653    int vrc = VINF_SUCCESS;
     654    if (!pThis->mCompleted)
     655    {
     656        pThis->i_checkForAutomaticTimeout();
     657        if (!pThis->mCanceled)
     658        {
     659            if (uPercentage > pThis->m_ulOperationPercent)
     660                pThis->setCurrentOperationProgress(uPercentage);
     661        }
     662        else
     663        {
     664            Assert(pThis->mCancelable);
     665            vrc = VERR_CANCELLED;
     666        }
     667    }
     668    /* else ignored */
     669    return vrc;
     670}
     671
     672/**
     673 * @callback_method_impl{FNVDPROGRESS,
     674 *      Progress::i_iprtProgressCallback with parameters switched around.}
     675 */
     676/*static*/ DECLCALLBACK(int) Progress::i_vdProgressCallback(void *pvUser, unsigned uPercentage)
     677{
     678    return i_iprtProgressCallback(uPercentage, pvUser);
     679}
     680
     681// IProgress properties
     682/////////////////////////////////////////////////////////////////////////////
     683
     684HRESULT Progress::getId(com::Guid &aId)
     685{
     686    /* mId is constant during life time, no need to lock */
     687    aId = mId;
     688
     689    return S_OK;
     690}
     691
     692HRESULT Progress::getDescription(com::Utf8Str &aDescription)
     693{
     694    /* mDescription is constant during life time, no need to lock */
     695    aDescription = mDescription;
     696
     697    return S_OK;
     698}
     699HRESULT Progress::getInitiator(ComPtr<IUnknown> &aInitiator)
     700{
     701    /* mInitiator/mParent are constant during life time, no need to lock */
     702#if !defined(VBOX_COM_INPROC)
     703    if (mInitiator)
     704        mInitiator.queryInterfaceTo(aInitiator.asOutParam());
     705    else
     706    {
     707        ComObjPtr<VirtualBox> pVirtualBox(mParent);
     708        pVirtualBox.queryInterfaceTo(aInitiator.asOutParam());
     709    }
     710#else
     711    mInitiator.queryInterfaceTo(aInitiator.asOutParam());
     712#endif
     713
     714    return S_OK;
     715}
     716
     717HRESULT Progress::getCancelable(BOOL *aCancelable)
     718{
     719    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     720
     721    *aCancelable = mCancelable;
     722
     723    return S_OK;
     724}
     725
     726HRESULT Progress::getPercent(ULONG *aPercent)
     727{
     728    /* i_checkForAutomaticTimeout requires a write lock. */
     729    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     730
     731    if (mCompleted && SUCCEEDED(mResultCode))
     732        *aPercent = 100;
     733    else
     734    {
     735        ULONG ulPercent = (ULONG)i_calcTotalPercent();
     736        // do not report 100% until we're really really done with everything
     737        // as the Qt GUI dismisses progress dialogs in that case
     738        if (    ulPercent == 100
     739             && (    m_ulOperationPercent < 100
     740                  || (m_ulCurrentOperation < m_cOperations -1)
     741                )
     742           )
     743            *aPercent = 99;
     744        else
     745            *aPercent = ulPercent;
     746    }
     747
     748    i_checkForAutomaticTimeout();
     749
     750    return S_OK;
     751}
     752
     753HRESULT Progress::getTimeRemaining(LONG *aTimeRemaining)
     754{
     755    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     756
     757    if (mCompleted)
     758        *aTimeRemaining = 0;
     759    else
     760    {
     761        double dPercentDone = i_calcTotalPercent();
     762        if (dPercentDone < 1)
     763            *aTimeRemaining = -1;       // unreliable, or avoid division by 0 below
     764        else
     765        {
     766            uint64_t ullTimeNow = RTTimeMilliTS();
     767            uint64_t ullTimeElapsed = ullTimeNow - m_ullTimestamp;
     768            uint64_t ullTimeTotal = (uint64_t)((double)ullTimeElapsed * 100 / dPercentDone);
     769            uint64_t ullTimeRemaining = ullTimeTotal - ullTimeElapsed;
     770
     771//          LogFunc(("dPercentDone = %RI32, ullTimeNow = %RI64, ullTimeElapsed = %RI64, ullTimeTotal = %RI64, ullTimeRemaining = %RI64\n",
     772//                   (uint32_t)dPercentDone, ullTimeNow, ullTimeElapsed, ullTimeTotal, ullTimeRemaining));
     773
     774            *aTimeRemaining = (LONG)(ullTimeRemaining / 1000);
     775        }
     776    }
     777
     778    return S_OK;
     779}
     780
     781HRESULT Progress::getCompleted(BOOL *aCompleted)
     782{
     783    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     784
     785    *aCompleted = mCompleted;
     786
     787    return S_OK;
     788}
     789
     790HRESULT Progress::getCanceled(BOOL *aCanceled)
     791{
     792    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     793
     794    *aCanceled = mCanceled;
     795
     796    return S_OK;
     797}
     798
     799HRESULT Progress::getResultCode(LONG *aResultCode)
     800{
     801    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     802
     803    if (!mCompleted)
     804        return setError(E_FAIL, tr("Result code is not available, operation is still in progress"));
     805
     806    *aResultCode = mResultCode;
     807
     808    return S_OK;
     809}
     810
     811HRESULT Progress::getErrorInfo(ComPtr<IVirtualBoxErrorInfo> &aErrorInfo)
     812{
     813    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     814
     815    if (!mCompleted)
     816        return setError(E_FAIL, tr("Error info is not available, operation is still in progress"));
     817
     818    mErrorInfo.queryInterfaceTo(aErrorInfo.asOutParam());
     819
     820    return S_OK;
     821}
     822
     823HRESULT Progress::getOperationCount(ULONG *aOperationCount)
     824{
     825    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     826
     827    *aOperationCount = m_cOperations;
     828
     829    return S_OK;
     830}
     831
     832HRESULT Progress::getOperation(ULONG *aOperation)
     833{
     834    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     835
     836    *aOperation = m_ulCurrentOperation;
     837
     838    return S_OK;
     839}
     840
     841HRESULT Progress::getOperationDescription(com::Utf8Str &aOperationDescription)
     842{
     843    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     844
     845    aOperationDescription = m_operationDescription;
     846
     847    return S_OK;
     848}
     849
     850HRESULT Progress::getOperationPercent(ULONG *aOperationPercent)
     851{
     852    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     853
     854    if (mCompleted && SUCCEEDED(mResultCode))
     855        *aOperationPercent = 100;
     856    else
     857        *aOperationPercent = m_ulOperationPercent;
     858
     859    return S_OK;
     860}
     861
     862HRESULT Progress::getOperationWeight(ULONG *aOperationWeight)
     863{
     864    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     865
     866    *aOperationWeight = m_ulCurrentOperationWeight;
     867
     868    return S_OK;
     869}
     870
     871HRESULT Progress::getTimeout(ULONG *aTimeout)
     872{
     873    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     874
     875    *aTimeout = m_cMsTimeout;
     876
     877    return S_OK;
     878}
     879
     880HRESULT Progress::setTimeout(ULONG aTimeout)
     881{
     882    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     883
     884    if (!mCancelable)
     885        return setError(VBOX_E_INVALID_OBJECT_STATE, tr("Operation cannot be canceled"));
     886    m_cMsTimeout = aTimeout;
     887
     888    return S_OK;
     889}
     890
     891
     892// IProgress methods
     893/////////////////////////////////////////////////////////////////////////////
     894
     895/**
     896 * Updates the percentage value of the current operation.
     897 *
     898 * @param aPercent  New percentage value of the operation in progress
     899 *                  (in range [0, 100]).
     900 */
     901HRESULT Progress::setCurrentOperationProgress(ULONG aPercent)
     902{
     903    AssertMsgReturn(aPercent <= 100, ("%u\n", aPercent), E_INVALIDARG);
     904
     905    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     906
     907    i_checkForAutomaticTimeout();
     908    if (mCancelable && mCanceled)
     909        AssertReturn(!mCompleted, E_FAIL);
     910    AssertReturn(!mCompleted && !mCanceled, E_FAIL);
     911
     912    if (m_ulOperationPercent != aPercent)
     913    {
     914        m_ulOperationPercent = aPercent;
     915        ULONG actualPercent = 0;
     916        getPercent(&actualPercent);
     917        fireProgressPercentageChangedEvent(pEventSource, mId.toUtf16().raw(), actualPercent);
     918    }
     919
     920    return S_OK;
     921}
     922
     923/**
     924 * Signals that the current operation is successfully completed and advances to
     925 * the next operation. The operation percentage is reset to 0.
     926 *
     927 * @param aNextOperationDescription  Description of the next operation.
     928 * @param aNextOperationsWeight     Weight of the next operation.
     929 *
     930 * @note The current operation must not be the last one.
     931 */
     932HRESULT Progress::setNextOperation(const com::Utf8Str &aNextOperationDescription, ULONG aNextOperationsWeight)
     933{
     934    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     935
     936    if (mCanceled)
     937        return E_FAIL;
     938    AssertReturn(!mCompleted, E_FAIL);
     939    AssertReturn(m_ulCurrentOperation + 1 < m_cOperations, E_FAIL);
     940
     941    ++m_ulCurrentOperation;
     942    m_ulOperationsCompletedWeight += m_ulCurrentOperationWeight;
     943
     944    m_operationDescription = aNextOperationDescription;
     945    m_ulCurrentOperationWeight = aNextOperationsWeight;
     946    m_ulOperationPercent = 0;
     947
     948    LogThisFunc(("%s: aNextOperationsWeight = %d; m_ulCurrentOperation is now %d, m_ulOperationsCompletedWeight is now %d\n",
     949                 m_operationDescription.c_str(), aNextOperationsWeight, m_ulCurrentOperation, m_ulOperationsCompletedWeight));
     950
     951    /* wake up all waiting threads */
     952    if (mWaitersCount > 0)
     953        RTSemEventMultiSignal(mCompletedSem);
     954
     955    ULONG actualPercent = 0;
     956    getPercent(&actualPercent);
     957    fireProgressPercentageChangedEvent(pEventSource, mId.toUtf16().raw(), actualPercent);
     958
     959    return S_OK;
     960}
     961
     962/**
     963 * @note XPCOM: when this method is not called on the main XPCOM thread, it
     964 *       simply blocks the thread until mCompletedSem is signalled. If the
     965 *       thread has its own event queue (hmm, what for?) that it must run, then
     966 *       calling this method will definitely freeze event processing.
     967 */
     968HRESULT Progress::waitForCompletion(LONG aTimeout)
     969{
     970    LogFlowThisFuncEnter();
     971    LogFlowThisFunc(("aTimeout=%d\n", aTimeout));
     972
     973    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     974
     975    /* if we're already completed, take a shortcut */
     976    if (!mCompleted)
     977    {
     978        int vrc = VINF_SUCCESS;
     979        bool fForever = aTimeout < 0;
     980        int64_t timeLeft = aTimeout;
     981        int64_t lastTime = RTTimeMilliTS();
     982
     983        while (!mCompleted && (fForever || timeLeft > 0))
     984        {
     985            mWaitersCount++;
     986            alock.release();
     987            vrc = RTSemEventMultiWait(mCompletedSem,
     988                                      fForever ? RT_INDEFINITE_WAIT : (RTMSINTERVAL)timeLeft);
     989            alock.acquire();
     990            mWaitersCount--;
     991
     992            /* the last waiter resets the semaphore */
     993            if (mWaitersCount == 0)
     994                RTSemEventMultiReset(mCompletedSem);
     995
     996            if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
     997                break;
     998
     999            if (!fForever)
     1000            {
     1001                int64_t now = RTTimeMilliTS();
     1002                timeLeft -= now - lastTime;
     1003                lastTime = now;
     1004            }
     1005        }
     1006
     1007        if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
     1008            return setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to wait for the task completion (%Rrc)"), vrc);
     1009    }
     1010
     1011    LogFlowThisFuncLeave();
     1012
     1013    return S_OK;
     1014}
     1015
     1016/**
     1017 * @note XPCOM: when this method is not called on the main XPCOM thread, it
     1018 *       simply blocks the thread until mCompletedSem is signalled. If the
     1019 *       thread has its own event queue (hmm, what for?) that it must run, then
     1020 *       calling this method will definitely freeze event processing.
     1021 */
     1022HRESULT Progress::waitForOperationCompletion(ULONG aOperation, LONG aTimeout)
     1023
     1024{
     1025    LogFlowThisFuncEnter();
     1026    LogFlowThisFunc(("aOperation=%d, aTimeout=%d\n", aOperation, aTimeout));
     1027
     1028    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     1029
     1030    CheckComArgExpr(aOperation, aOperation < m_cOperations);
     1031
     1032    /* if we're already completed or if the given operation is already done,
     1033     * then take a shortcut */
     1034    if (    !mCompleted
     1035         && aOperation >= m_ulCurrentOperation)
     1036    {
     1037        int vrc = VINF_SUCCESS;
     1038        bool fForever = aTimeout < 0;
     1039        int64_t timeLeft = aTimeout;
     1040        int64_t lastTime = RTTimeMilliTS();
     1041
     1042        while (    !mCompleted && aOperation >= m_ulCurrentOperation
     1043                && (fForever || timeLeft > 0))
     1044        {
     1045            mWaitersCount ++;
     1046            alock.release();
     1047            vrc = RTSemEventMultiWait(mCompletedSem,
     1048                                      fForever ? RT_INDEFINITE_WAIT : (unsigned) timeLeft);
     1049            alock.acquire();
     1050            mWaitersCount--;
     1051
     1052            /* the last waiter resets the semaphore */
     1053            if (mWaitersCount == 0)
     1054                RTSemEventMultiReset(mCompletedSem);
     1055
     1056            if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
     1057                break;
     1058
     1059            if (!fForever)
     1060            {
     1061                int64_t now = RTTimeMilliTS();
     1062                timeLeft -= now - lastTime;
     1063                lastTime = now;
     1064            }
     1065        }
     1066
     1067        if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
     1068            return setErrorBoth(E_FAIL, vrc, tr("Failed to wait for the operation completion (%Rrc)"), vrc);
     1069    }
     1070
     1071    LogFlowThisFuncLeave();
     1072
     1073    return S_OK;
    10641074}
    10651075
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