VirtualBox

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


Ignore:
Timestamp:
Oct 12, 2018 3:09:44 PM (6 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
125752
Message:

Main/Progress: Split into safe public interface and a private one which is used purely by API implementation code (for updating). Needed quite a few minor adjustments elsewhere.

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

Legend:

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

    r74219 r74804  
    18101810    AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
    18111811
    1812     return pProgress->SetCurrentOperationProgress(uPercent);
     1812    ComPtr<IInternalProgressControl> pProgressControl(pProgress);
     1813    AssertReturn(!!pProgressControl, E_INVALIDARG);
     1814    return pProgressControl->SetCurrentOperationProgress(uPercent);
    18131815}
    18141816
     
    18281830    AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
    18291831
    1830     return pProgress->SetNextOperation(Bstr(pcszNextOperationDescription).raw(), uNextOperationWeight);
     1832    ComPtr<IInternalProgressControl> pProgressControl(pProgress);
     1833    AssertReturn(!!pProgressControl, E_INVALIDARG);
     1834    return pProgressControl->SetNextOperation(Bstr(pcszNextOperationDescription).raw(), uNextOperationWeight);
    18311835}
    18321836
    18331837/*static*/ DECLCALLBACK(uint32_t)
    18341838ExtPack::i_hlpWaitOtherProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IProgress) *pProgress,
    1835                                 VBOXEXTPACK_IF_CS(IProgress) *pProgressOther)
     1839                                VBOXEXTPACK_IF_CS(IProgress) *pProgressOther, uint32_t cTimeoutMS)
    18361840{
    18371841    /*
     
    18441848    AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
    18451849
    1846     Progress *pProgressInt = static_cast<Progress *>(pProgress);
    1847     return pProgressInt->i_waitForOtherProgressCompletion(pProgressOther);
     1850    ComPtr<IInternalProgressControl> pProgressControl(pProgress);
     1851    AssertReturn(!!pProgressControl, E_INVALIDARG);
     1852    return pProgressControl->WaitForOtherProgressCompletion(pProgressOther, cTimeoutMS);
    18481853}
    18491854
     
    18601865    AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
    18611866
    1862     Progress *pProgressInt = static_cast<Progress *>(pProgress);
    1863     return pProgressInt->i_notifyComplete(uResultCode);
     1867    ComPtr<IInternalProgressControl> pProgressControl(pProgress);
     1868    AssertReturn(!!pProgressControl, E_INVALIDARG);
     1869
     1870    ComPtr<IVirtualBoxErrorInfo> errorInfo;
     1871    if (FAILED((HRESULT)uResultCode))
     1872    {
     1873        ErrorInfoKeeper eik;
     1874        eik.getVirtualBoxErrorInfo(errorInfo);
     1875    }
     1876    return pProgressControl->NotifyComplete(uResultCode, errorInfo);
    18641877}
    18651878
  • trunk/src/VBox/Main/src-all/ProgressImpl.cpp

    r74759 r74804  
    370370    }
    371371
    372     return i_notifyCompleteEI(aResultCode, errorInfo);
     372    return notifyComplete(aResultCode, errorInfo);
    373373}
    374374
     
    414414    errorInfo->init(aResultCode, aIID, pcszComponent, text);
    415415
    416     return i_notifyCompleteEI(aResultCode, errorInfo);
    417 }
    418 
    419 /**
    420  * Marks the operation as complete and attaches full error info.
    421  *
    422  * This is where the actual work is done, the related methods all end up here.
    423  *
    424  * @param aResultCode   Operation result (error) code, must not be S_OK.
    425  * @param aErrorInfo            List of arguments for the format string.
    426  */
    427 HRESULT Progress::i_notifyCompleteEI(HRESULT aResultCode, const ComPtr<IVirtualBoxErrorInfo> &aErrorInfo)
    428 {
    429     LogThisFunc(("aResultCode=%d\n", aResultCode));
    430     /* on failure we expect error info, on success there must be none */
    431     AssertMsg(FAILED(aResultCode) ^ aErrorInfo.isNull(),
    432               ("No error info but trying to set a failed result (%08X)!\n",
    433                aResultCode));
    434 
     416    return notifyComplete(aResultCode, errorInfo);
     417}
     418
     419/**
     420 * Sets the cancelation callback, checking for cancelation first.
     421 *
     422 * @returns Success indicator.
     423 * @retval  true on success.
     424 * @retval  false if the progress object has already been canceled or is in an
     425 *          invalid state
     426 *
     427 * @param   pfnCallback     The function to be called upon cancelation.
     428 * @param   pvUser          The callback argument.
     429 */
     430bool Progress::i_setCancelCallback(void (*pfnCallback)(void *), void *pvUser)
     431{
    435432    AutoCaller autoCaller(this);
    436     AssertComRCReturnRC(autoCaller.rc());
     433    AssertComRCReturn(autoCaller.rc(), false);
    437434
    438435    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    439436
    440     AssertReturn(mCompleted == FALSE, E_FAIL);
    441 
    442     if (mCanceled && SUCCEEDED(aResultCode))
    443         aResultCode = E_FAIL;
    444 
    445     mCompleted = TRUE;
    446     mResultCode = aResultCode;
    447     if (SUCCEEDED(aResultCode))
    448     {
    449         m_ulCurrentOperation = m_cOperations - 1; /* last operation */
    450         m_ulOperationPercent = 100;
    451     }
    452     mErrorInfo = aErrorInfo;
    453 
     437    i_checkForAutomaticTimeout();
     438    if (mCanceled)
     439        return false;
     440
     441    m_pvCancelUserArg   = pvUser;
     442    m_pfnCancelCallback = pfnCallback;
     443    return true;
     444}
     445
     446/**
     447 * @callback_method_impl{FNRTPROGRESS,
     448 *      Works the progress of the current operation.}
     449 */
     450/*static*/ DECLCALLBACK(int) Progress::i_iprtProgressCallback(unsigned uPercentage, void *pvUser)
     451{
     452    Progress *pThis = (Progress *)pvUser;
     453
     454    /*
     455     * Same as setCurrentOperationProgress, except we don't fail on mCompleted.
     456     */
     457    AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
     458    int vrc = VINF_SUCCESS;
     459    if (!pThis->mCompleted)
     460    {
     461        pThis->i_checkForAutomaticTimeout();
     462        if (!pThis->mCanceled)
     463        {
     464            if (uPercentage > pThis->m_ulOperationPercent)
     465                pThis->setCurrentOperationProgress(uPercentage);
     466        }
     467        else
     468        {
     469            Assert(pThis->mCancelable);
     470            vrc = VERR_CANCELLED;
     471        }
     472    }
     473    /* else ignored */
     474    return vrc;
     475}
     476
     477/**
     478 * @callback_method_impl{FNVDPROGRESS,
     479 *      Progress::i_iprtProgressCallback with parameters switched around.}
     480 */
     481/*static*/ DECLCALLBACK(int) Progress::i_vdProgressCallback(void *pvUser, unsigned uPercentage)
     482{
     483    return i_iprtProgressCallback(uPercentage, pvUser);
     484}
     485
     486
     487// IProgress properties
     488/////////////////////////////////////////////////////////////////////////////
     489
     490HRESULT Progress::getId(com::Guid &aId)
     491{
     492    /* mId is constant during life time, no need to lock */
     493    aId = mId;
     494
     495    return S_OK;
     496}
     497
     498HRESULT Progress::getDescription(com::Utf8Str &aDescription)
     499{
     500    /* mDescription is constant during life time, no need to lock */
     501    aDescription = mDescription;
     502
     503    return S_OK;
     504}
     505HRESULT Progress::getInitiator(ComPtr<IUnknown> &aInitiator)
     506{
     507    /* mInitiator/mParent are constant during life time, no need to lock */
    454508#if !defined(VBOX_COM_INPROC)
    455     /* remove from the global collection of pending progress operations */
    456     if (mParent)
    457         mParent->i_removeProgress(mId.ref());
     509    if (mInitiator)
     510        mInitiator.queryInterfaceTo(aInitiator.asOutParam());
     511    else
     512    {
     513        ComObjPtr<VirtualBox> pVirtualBox(mParent);
     514        pVirtualBox.queryInterfaceTo(aInitiator.asOutParam());
     515    }
     516#else
     517    mInitiator.queryInterfaceTo(aInitiator.asOutParam());
    458518#endif
    459519
    460     /* wake up all waiting threads */
    461     if (mWaitersCount > 0)
    462         RTSemEventMultiSignal(mCompletedSem);
    463 
    464     fireProgressTaskCompletedEvent(pEventSource, mId.toUtf16().raw());
    465 
    466     return S_OK;
    467 }
    468 
    469 HRESULT Progress::i_waitForOtherProgressCompletion(const ComPtr<IProgress> &aProgressOther)
     520    return S_OK;
     521}
     522
     523HRESULT Progress::getCancelable(BOOL *aCancelable)
     524{
     525    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     526
     527    *aCancelable = mCancelable;
     528
     529    return S_OK;
     530}
     531
     532HRESULT Progress::getPercent(ULONG *aPercent)
     533{
     534    /* i_checkForAutomaticTimeout requires a write lock. */
     535    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     536
     537    if (mCompleted && SUCCEEDED(mResultCode))
     538        *aPercent = 100;
     539    else
     540    {
     541        ULONG ulPercent = (ULONG)i_calcTotalPercent();
     542        // do not report 100% until we're really really done with everything
     543        // as the Qt GUI dismisses progress dialogs in that case
     544        if (    ulPercent == 100
     545             && (    m_ulOperationPercent < 100
     546                  || (m_ulCurrentOperation < m_cOperations -1)
     547                )
     548           )
     549            *aPercent = 99;
     550        else
     551            *aPercent = ulPercent;
     552    }
     553
     554    i_checkForAutomaticTimeout();
     555
     556    return S_OK;
     557}
     558
     559HRESULT Progress::getTimeRemaining(LONG *aTimeRemaining)
     560{
     561    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     562
     563    if (mCompleted)
     564        *aTimeRemaining = 0;
     565    else
     566    {
     567        double dPercentDone = i_calcTotalPercent();
     568        if (dPercentDone < 1)
     569            *aTimeRemaining = -1;       // unreliable, or avoid division by 0 below
     570        else
     571        {
     572            uint64_t ullTimeNow = RTTimeMilliTS();
     573            uint64_t ullTimeElapsed = ullTimeNow - m_ullTimestamp;
     574            uint64_t ullTimeTotal = (uint64_t)((double)ullTimeElapsed * 100 / dPercentDone);
     575            uint64_t ullTimeRemaining = ullTimeTotal - ullTimeElapsed;
     576
     577//          LogFunc(("dPercentDone = %RI32, ullTimeNow = %RI64, ullTimeElapsed = %RI64, ullTimeTotal = %RI64, ullTimeRemaining = %RI64\n",
     578//                   (uint32_t)dPercentDone, ullTimeNow, ullTimeElapsed, ullTimeTotal, ullTimeRemaining));
     579
     580            *aTimeRemaining = (LONG)(ullTimeRemaining / 1000);
     581        }
     582    }
     583
     584    return S_OK;
     585}
     586
     587HRESULT Progress::getCompleted(BOOL *aCompleted)
     588{
     589    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     590
     591    *aCompleted = mCompleted;
     592
     593    return S_OK;
     594}
     595
     596HRESULT Progress::getCanceled(BOOL *aCanceled)
     597{
     598    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     599
     600    *aCanceled = mCanceled;
     601
     602    return S_OK;
     603}
     604
     605HRESULT Progress::getResultCode(LONG *aResultCode)
     606{
     607    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     608
     609    if (!mCompleted)
     610        return setError(E_FAIL, tr("Result code is not available, operation is still in progress"));
     611
     612    *aResultCode = mResultCode;
     613
     614    return S_OK;
     615}
     616
     617HRESULT Progress::getErrorInfo(ComPtr<IVirtualBoxErrorInfo> &aErrorInfo)
     618{
     619    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     620
     621    if (!mCompleted)
     622        return setError(E_FAIL, tr("Error info is not available, operation is still in progress"));
     623
     624    mErrorInfo.queryInterfaceTo(aErrorInfo.asOutParam());
     625
     626    return S_OK;
     627}
     628
     629HRESULT Progress::getOperationCount(ULONG *aOperationCount)
     630{
     631    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     632
     633    *aOperationCount = m_cOperations;
     634
     635    return S_OK;
     636}
     637
     638HRESULT Progress::getOperation(ULONG *aOperation)
     639{
     640    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     641
     642    *aOperation = m_ulCurrentOperation;
     643
     644    return S_OK;
     645}
     646
     647HRESULT Progress::getOperationDescription(com::Utf8Str &aOperationDescription)
     648{
     649    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     650
     651    aOperationDescription = m_operationDescription;
     652
     653    return S_OK;
     654}
     655
     656HRESULT Progress::getOperationPercent(ULONG *aOperationPercent)
     657{
     658    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     659
     660    if (mCompleted && SUCCEEDED(mResultCode))
     661        *aOperationPercent = 100;
     662    else
     663        *aOperationPercent = m_ulOperationPercent;
     664
     665    return S_OK;
     666}
     667
     668HRESULT Progress::getOperationWeight(ULONG *aOperationWeight)
     669{
     670    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     671
     672    *aOperationWeight = m_ulCurrentOperationWeight;
     673
     674    return S_OK;
     675}
     676
     677HRESULT Progress::getTimeout(ULONG *aTimeout)
     678{
     679    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     680
     681    *aTimeout = m_cMsTimeout;
     682
     683    return S_OK;
     684}
     685
     686HRESULT Progress::setTimeout(ULONG aTimeout)
     687{
     688    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     689
     690    if (!mCancelable)
     691        return setError(VBOX_E_INVALID_OBJECT_STATE, tr("Operation cannot be canceled"));
     692    m_cMsTimeout = aTimeout;
     693
     694    return S_OK;
     695}
     696
     697HRESULT Progress::getEventSource(ComPtr<IEventSource> &aEventSource)
     698{
     699    /* event source is const, no need to lock */
     700    pEventSource.queryInterfaceTo(aEventSource.asOutParam());
     701    return S_OK;
     702}
     703
     704
     705// IProgress methods
     706/////////////////////////////////////////////////////////////////////////////
     707
     708/**
     709 * @note XPCOM: when this method is not called on the main XPCOM thread, it
     710 *       simply blocks the thread until mCompletedSem is signalled. If the
     711 *       thread has its own event queue (hmm, what for?) that it must run, then
     712 *       calling this method will definitely freeze event processing.
     713 */
     714HRESULT Progress::waitForCompletion(LONG aTimeout)
     715{
     716    LogFlowThisFuncEnter();
     717    LogFlowThisFunc(("aTimeout=%d\n", aTimeout));
     718
     719    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     720
     721    /* if we're already completed, take a shortcut */
     722    if (!mCompleted)
     723    {
     724        int vrc = VINF_SUCCESS;
     725        bool fForever = aTimeout < 0;
     726        int64_t timeLeft = aTimeout;
     727        int64_t lastTime = RTTimeMilliTS();
     728
     729        while (!mCompleted && (fForever || timeLeft > 0))
     730        {
     731            mWaitersCount++;
     732            alock.release();
     733            vrc = RTSemEventMultiWait(mCompletedSem,
     734                                      fForever ? RT_INDEFINITE_WAIT : (RTMSINTERVAL)timeLeft);
     735            alock.acquire();
     736            mWaitersCount--;
     737
     738            /* the last waiter resets the semaphore */
     739            if (mWaitersCount == 0)
     740                RTSemEventMultiReset(mCompletedSem);
     741
     742            if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
     743                break;
     744
     745            if (!fForever)
     746            {
     747                int64_t now = RTTimeMilliTS();
     748                timeLeft -= now - lastTime;
     749                lastTime = now;
     750            }
     751        }
     752
     753        if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
     754            return setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to wait for the task completion (%Rrc)"), vrc);
     755    }
     756
     757    LogFlowThisFuncLeave();
     758
     759    return S_OK;
     760}
     761
     762/**
     763 * @note XPCOM: when this method is not called on the main XPCOM thread, it
     764 *       simply blocks the thread until mCompletedSem is signalled. If the
     765 *       thread has its own event queue (hmm, what for?) that it must run, then
     766 *       calling this method will definitely freeze event processing.
     767 */
     768HRESULT Progress::waitForOperationCompletion(ULONG aOperation, LONG aTimeout)
     769
     770{
     771    LogFlowThisFuncEnter();
     772    LogFlowThisFunc(("aOperation=%d, aTimeout=%d\n", aOperation, aTimeout));
     773
     774    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     775
     776    CheckComArgExpr(aOperation, aOperation < m_cOperations);
     777
     778    /* if we're already completed or if the given operation is already done,
     779     * then take a shortcut */
     780    if (    !mCompleted
     781         && aOperation >= m_ulCurrentOperation)
     782    {
     783        int vrc = VINF_SUCCESS;
     784        bool fForever = aTimeout < 0;
     785        int64_t timeLeft = aTimeout;
     786        int64_t lastTime = RTTimeMilliTS();
     787
     788        while (    !mCompleted && aOperation >= m_ulCurrentOperation
     789                && (fForever || timeLeft > 0))
     790        {
     791            mWaitersCount ++;
     792            alock.release();
     793            vrc = RTSemEventMultiWait(mCompletedSem,
     794                                      fForever ? RT_INDEFINITE_WAIT : (unsigned) timeLeft);
     795            alock.acquire();
     796            mWaitersCount--;
     797
     798            /* the last waiter resets the semaphore */
     799            if (mWaitersCount == 0)
     800                RTSemEventMultiReset(mCompletedSem);
     801
     802            if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
     803                break;
     804
     805            if (!fForever)
     806            {
     807                int64_t now = RTTimeMilliTS();
     808                timeLeft -= now - lastTime;
     809                lastTime = now;
     810            }
     811        }
     812
     813        if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
     814            return setErrorBoth(E_FAIL, vrc, tr("Failed to wait for the operation completion (%Rrc)"), vrc);
     815    }
     816
     817    LogFlowThisFuncLeave();
     818
     819    return S_OK;
     820}
     821
     822HRESULT Progress::cancel()
     823{
     824    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     825
     826    if (!mCancelable)
     827        return setError(VBOX_E_INVALID_OBJECT_STATE, tr("Operation cannot be canceled"));
     828
     829    if (!mCanceled)
     830    {
     831        LogThisFunc(("Canceling\n"));
     832        mCanceled = TRUE;
     833        if (m_pfnCancelCallback)
     834            m_pfnCancelCallback(m_pvCancelUserArg);
     835
     836    }
     837    else
     838        LogThisFunc(("Already canceled\n"));
     839
     840    return S_OK;
     841}
     842
     843
     844// IInternalProgressControl methods
     845/////////////////////////////////////////////////////////////////////////////
     846
     847/**
     848 * Updates the percentage value of the current operation.
     849 *
     850 * @param aPercent  New percentage value of the operation in progress
     851 *                  (in range [0, 100]).
     852 */
     853HRESULT Progress::setCurrentOperationProgress(ULONG aPercent)
     854{
     855    AssertMsgReturn(aPercent <= 100, ("%u\n", aPercent), E_INVALIDARG);
     856
     857    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     858
     859    i_checkForAutomaticTimeout();
     860    if (mCancelable && mCanceled)
     861        AssertReturn(!mCompleted, E_FAIL);
     862    AssertReturn(!mCompleted && !mCanceled, E_FAIL);
     863
     864    if (m_ulOperationPercent != aPercent)
     865    {
     866        m_ulOperationPercent = aPercent;
     867        ULONG actualPercent = 0;
     868        getPercent(&actualPercent);
     869        fireProgressPercentageChangedEvent(pEventSource, mId.toUtf16().raw(), actualPercent);
     870    }
     871
     872    return S_OK;
     873}
     874
     875HRESULT Progress::waitForOtherProgressCompletion(const ComPtr<IProgress> &aProgressOther,
     876                                                 ULONG aTimeoutMS)
    470877{
    471878    LogFlowThisFuncEnter();
     
    483890    rc = aProgressOther->COMGETTER(Cancelable)(&fCancelable);
    484891    if (FAILED(rc)) return rc;
     892
     893    uint64_t u64StopTime = UINT64_MAX;
     894    if (aTimeoutMS > 0)
     895        u64StopTime = RTTimeMilliTS() + aTimeoutMS;
    485896    /* Loop as long as the sync process isn't completed. */
    486897    while (SUCCEEDED(aProgressOther->COMGETTER(Completed(&fCompleted))))
     
    541952            break;
    542953
    543         /* Make sure the loop is not too tight */
    544         rc = aProgressOther->WaitForCompletion(100);
    545         if (FAILED(rc)) return rc;
     954        if (aTimeoutMS != 0)
     955        {
     956            /* Make sure the loop is not too tight */
     957            uint64_t u64Now = RTTimeMilliTS();
     958            uint64_t u64RemainingMS = u64StopTime - u64Now;
     959            if (u64RemainingMS < 10)
     960                u64RemainingMS = 10;
     961            else if (u64RemainingMS > 200)
     962                u64RemainingMS = 200;
     963            rc = aProgressOther->WaitForCompletion((LONG)u64RemainingMS);
     964            if (FAILED(rc)) return rc;
     965
     966            if (RTTimeMilliTS() >= u64StopTime)
     967                return VBOX_E_TIMEOUT;
     968        }
     969        else
     970        {
     971            /* Make sure the loop is not too tight */
     972            rc = aProgressOther->WaitForCompletion(200);
     973            if (FAILED(rc)) return rc;
     974        }
    546975    }
    547976
     
    562991
    563992/**
     993 * Signals that the current operation is successfully completed and advances to
     994 * the next operation. The operation percentage is reset to 0.
     995 *
     996 * @param aNextOperationDescription  Description of the next operation.
     997 * @param aNextOperationsWeight     Weight of the next operation.
     998 *
     999 * @note The current operation must not be the last one.
     1000 */
     1001HRESULT Progress::setNextOperation(const com::Utf8Str &aNextOperationDescription, ULONG aNextOperationsWeight)
     1002{
     1003    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     1004
     1005    if (mCanceled)
     1006        return E_FAIL;
     1007    AssertReturn(!mCompleted, E_FAIL);
     1008    AssertReturn(m_ulCurrentOperation + 1 < m_cOperations, E_FAIL);
     1009
     1010    ++m_ulCurrentOperation;
     1011    m_ulOperationsCompletedWeight += m_ulCurrentOperationWeight;
     1012
     1013    m_operationDescription = aNextOperationDescription;
     1014    m_ulCurrentOperationWeight = aNextOperationsWeight;
     1015    m_ulOperationPercent = 0;
     1016
     1017    LogThisFunc(("%s: aNextOperationsWeight = %d; m_ulCurrentOperation is now %d, m_ulOperationsCompletedWeight is now %d\n",
     1018                 m_operationDescription.c_str(), aNextOperationsWeight, m_ulCurrentOperation, m_ulOperationsCompletedWeight));
     1019
     1020    /* wake up all waiting threads */
     1021    if (mWaitersCount > 0)
     1022        RTSemEventMultiSignal(mCompletedSem);
     1023
     1024    ULONG actualPercent = 0;
     1025    getPercent(&actualPercent);
     1026    fireProgressPercentageChangedEvent(pEventSource, mId.toUtf16().raw(), actualPercent);
     1027
     1028    return S_OK;
     1029}
     1030
     1031/**
    5641032 * Notify the progress object that we're almost at the point of no return.
    5651033 *
     
    5701038 * user believe it was rolled back.
    5711039 *
    572  * @returns Success indicator.
    573  * @retval  true on success.
    574  * @retval  false if the progress object has already been canceled or is in an
     1040 * @returns COM error status.
     1041 * @retval  S_OK on success.
     1042 * @retval  E_FAIL if the progress object has already been canceled or is in an
    5751043 *          invalid state
    5761044 */
    577 bool Progress::i_notifyPointOfNoReturn(void)
    578 {
    579     AutoCaller autoCaller(this);
    580     AssertComRCReturn(autoCaller.rc(), false);
    581 
     1045HRESULT Progress::notifyPointOfNoReturn(void)
     1046{
    5821047    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    5831048
    5841049    if (mCanceled)
    5851050    {
    586         LogThisFunc(("returns false\n"));
    587         return false;
     1051        LogThisFunc(("returns failure\n"));
     1052        return E_FAIL;
    5881053    }
    5891054
    5901055    mCancelable = FALSE;
    591     LogThisFunc(("returns true\n"));
    592     return true;
    593 }
    594 
    595 /**
    596  * Sets the cancelation callback, checking for cancelation first.
    597  *
    598  * @returns Success indicator.
    599  * @retval  true on success.
    600  * @retval  false if the progress object has already been canceled or is in an
    601  *          invalid state
    602  *
    603  * @param   pfnCallback     The function to be called upon cancelation.
    604  * @param   pvUser          The callback argument.
    605  */
    606 bool Progress::i_setCancelCallback(void (*pfnCallback)(void *), void *pvUser)
    607 {
    608     AutoCaller autoCaller(this);
    609     AssertComRCReturn(autoCaller.rc(), false);
     1056    LogThisFunc(("returns success\n"));
     1057    return S_OK;
     1058}
     1059
     1060/**
     1061 * Marks the operation as complete and attaches full error info.
     1062 *
     1063 * This is where the actual work is done, the related methods all end up here.
     1064 *
     1065 * @param aResultCode   Operation result (error) code, must not be S_OK.
     1066 * @param aErrorInfo            List of arguments for the format string.
     1067 */
     1068HRESULT Progress::notifyComplete(LONG aResultCode, const ComPtr<IVirtualBoxErrorInfo> &aErrorInfo)
     1069{
     1070    LogThisFunc(("aResultCode=%d\n", aResultCode));
     1071    /* on failure we expect error info, on success there must be none */
     1072    AssertMsg(FAILED(aResultCode) ^ aErrorInfo.isNull(),
     1073              ("No error info but trying to set a failed result (%08X)!\n",
     1074               aResultCode));
    6101075
    6111076    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    6121077
    613     i_checkForAutomaticTimeout();
    614     if (mCanceled)
    615         return false;
    616 
    617     m_pvCancelUserArg   = pvUser;
    618     m_pfnCancelCallback = pfnCallback;
    619     return true;
    620 }
    621 
    622 /**
    623  * @callback_method_impl{FNRTPROGRESS,
    624  *      Works the progress of the current operation.}
    625  */
    626 /*static*/ DECLCALLBACK(int) Progress::i_iprtProgressCallback(unsigned uPercentage, void *pvUser)
    627 {
    628     Progress *pThis = (Progress *)pvUser;
    629 
    630     /*
    631      * Same as setCurrentOperationProgress, except we don't fail on mCompleted.
    632      */
    633     AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
    634     int vrc = VINF_SUCCESS;
    635     if (!pThis->mCompleted)
    636     {
    637         pThis->i_checkForAutomaticTimeout();
    638         if (!pThis->mCanceled)
    639         {
    640             if (uPercentage > pThis->m_ulOperationPercent)
    641                 pThis->setCurrentOperationProgress(uPercentage);
    642         }
    643         else
    644         {
    645             Assert(pThis->mCancelable);
    646             vrc = VERR_CANCELLED;
    647         }
    648     }
    649     /* else ignored */
    650     return vrc;
    651 }
    652 
    653 /**
    654  * @callback_method_impl{FNVDPROGRESS,
    655  *      Progress::i_iprtProgressCallback with parameters switched around.}
    656  */
    657 /*static*/ DECLCALLBACK(int) Progress::i_vdProgressCallback(void *pvUser, unsigned uPercentage)
    658 {
    659     return i_iprtProgressCallback(uPercentage, pvUser);
    660 }
    661 
    662 // IProgress properties
    663 /////////////////////////////////////////////////////////////////////////////
    664 
    665 HRESULT Progress::getId(com::Guid &aId)
    666 {
    667     /* mId is constant during life time, no need to lock */
    668     aId = mId;
    669 
    670     return S_OK;
    671 }
    672 
    673 HRESULT Progress::getDescription(com::Utf8Str &aDescription)
    674 {
    675     /* mDescription is constant during life time, no need to lock */
    676     aDescription = mDescription;
    677 
    678     return S_OK;
    679 }
    680 HRESULT Progress::getInitiator(ComPtr<IUnknown> &aInitiator)
    681 {
    682     /* mInitiator/mParent are constant during life time, no need to lock */
     1078    AssertReturn(mCompleted == FALSE, E_FAIL);
     1079
     1080    if (mCanceled && SUCCEEDED(aResultCode))
     1081        aResultCode = E_FAIL;
     1082
     1083    mCompleted = TRUE;
     1084    mResultCode = aResultCode;
     1085    if (SUCCEEDED(aResultCode))
     1086    {
     1087        m_ulCurrentOperation = m_cOperations - 1; /* last operation */
     1088        m_ulOperationPercent = 100;
     1089    }
     1090    mErrorInfo = aErrorInfo;
     1091
    6831092#if !defined(VBOX_COM_INPROC)
    684     if (mInitiator)
    685         mInitiator.queryInterfaceTo(aInitiator.asOutParam());
    686     else
    687     {
    688         ComObjPtr<VirtualBox> pVirtualBox(mParent);
    689         pVirtualBox.queryInterfaceTo(aInitiator.asOutParam());
    690     }
    691 #else
    692     mInitiator.queryInterfaceTo(aInitiator.asOutParam());
     1093    /* remove from the global collection of pending progress operations */
     1094    if (mParent)
     1095        mParent->i_removeProgress(mId.ref());
    6931096#endif
    694 
    695     return S_OK;
    696 }
    697 
    698 HRESULT Progress::getCancelable(BOOL *aCancelable)
    699 {
    700     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    701 
    702     *aCancelable = mCancelable;
    703 
    704     return S_OK;
    705 }
    706 
    707 HRESULT Progress::getPercent(ULONG *aPercent)
    708 {
    709     /* i_checkForAutomaticTimeout requires a write lock. */
    710     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    711 
    712     if (mCompleted && SUCCEEDED(mResultCode))
    713         *aPercent = 100;
    714     else
    715     {
    716         ULONG ulPercent = (ULONG)i_calcTotalPercent();
    717         // do not report 100% until we're really really done with everything
    718         // as the Qt GUI dismisses progress dialogs in that case
    719         if (    ulPercent == 100
    720              && (    m_ulOperationPercent < 100
    721                   || (m_ulCurrentOperation < m_cOperations -1)
    722                 )
    723            )
    724             *aPercent = 99;
    725         else
    726             *aPercent = ulPercent;
    727     }
    728 
    729     i_checkForAutomaticTimeout();
    730 
    731     return S_OK;
    732 }
    733 
    734 HRESULT Progress::getTimeRemaining(LONG *aTimeRemaining)
    735 {
    736     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    737 
    738     if (mCompleted)
    739         *aTimeRemaining = 0;
    740     else
    741     {
    742         double dPercentDone = i_calcTotalPercent();
    743         if (dPercentDone < 1)
    744             *aTimeRemaining = -1;       // unreliable, or avoid division by 0 below
    745         else
    746         {
    747             uint64_t ullTimeNow = RTTimeMilliTS();
    748             uint64_t ullTimeElapsed = ullTimeNow - m_ullTimestamp;
    749             uint64_t ullTimeTotal = (uint64_t)((double)ullTimeElapsed * 100 / dPercentDone);
    750             uint64_t ullTimeRemaining = ullTimeTotal - ullTimeElapsed;
    751 
    752 //          LogFunc(("dPercentDone = %RI32, ullTimeNow = %RI64, ullTimeElapsed = %RI64, ullTimeTotal = %RI64, ullTimeRemaining = %RI64\n",
    753 //                   (uint32_t)dPercentDone, ullTimeNow, ullTimeElapsed, ullTimeTotal, ullTimeRemaining));
    754 
    755             *aTimeRemaining = (LONG)(ullTimeRemaining / 1000);
    756         }
    757     }
    758 
    759     return S_OK;
    760 }
    761 
    762 HRESULT Progress::getCompleted(BOOL *aCompleted)
    763 {
    764     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    765 
    766     *aCompleted = mCompleted;
    767 
    768     return S_OK;
    769 }
    770 
    771 HRESULT Progress::getCanceled(BOOL *aCanceled)
    772 {
    773     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    774 
    775     *aCanceled = mCanceled;
    776 
    777     return S_OK;
    778 }
    779 
    780 HRESULT Progress::getResultCode(LONG *aResultCode)
    781 {
    782     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    783 
    784     if (!mCompleted)
    785         return setError(E_FAIL, tr("Result code is not available, operation is still in progress"));
    786 
    787     *aResultCode = mResultCode;
    788 
    789     return S_OK;
    790 }
    791 
    792 HRESULT Progress::getErrorInfo(ComPtr<IVirtualBoxErrorInfo> &aErrorInfo)
    793 {
    794     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    795 
    796     if (!mCompleted)
    797         return setError(E_FAIL, tr("Error info is not available, operation is still in progress"));
    798 
    799     mErrorInfo.queryInterfaceTo(aErrorInfo.asOutParam());
    800 
    801     return S_OK;
    802 }
    803 
    804 HRESULT Progress::getOperationCount(ULONG *aOperationCount)
    805 {
    806     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    807 
    808     *aOperationCount = m_cOperations;
    809 
    810     return S_OK;
    811 }
    812 
    813 HRESULT Progress::getOperation(ULONG *aOperation)
    814 {
    815     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    816 
    817     *aOperation = m_ulCurrentOperation;
    818 
    819     return S_OK;
    820 }
    821 
    822 HRESULT Progress::getOperationDescription(com::Utf8Str &aOperationDescription)
    823 {
    824     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    825 
    826     aOperationDescription = m_operationDescription;
    827 
    828     return S_OK;
    829 }
    830 
    831 HRESULT Progress::getOperationPercent(ULONG *aOperationPercent)
    832 {
    833     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    834 
    835     if (mCompleted && SUCCEEDED(mResultCode))
    836         *aOperationPercent = 100;
    837     else
    838         *aOperationPercent = m_ulOperationPercent;
    839 
    840     return S_OK;
    841 }
    842 
    843 HRESULT Progress::getOperationWeight(ULONG *aOperationWeight)
    844 {
    845     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    846 
    847     *aOperationWeight = m_ulCurrentOperationWeight;
    848 
    849     return S_OK;
    850 }
    851 
    852 HRESULT Progress::getTimeout(ULONG *aTimeout)
    853 {
    854     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    855 
    856     *aTimeout = m_cMsTimeout;
    857 
    858     return S_OK;
    859 }
    860 
    861 HRESULT Progress::setTimeout(ULONG aTimeout)
    862 {
    863     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    864 
    865     if (!mCancelable)
    866         return setError(VBOX_E_INVALID_OBJECT_STATE, tr("Operation cannot be canceled"));
    867     m_cMsTimeout = aTimeout;
    868 
    869     return S_OK;
    870 }
    871 
    872 
    873 // IProgress methods
    874 /////////////////////////////////////////////////////////////////////////////
    875 
    876 /**
    877  * Updates the percentage value of the current operation.
    878  *
    879  * @param aPercent  New percentage value of the operation in progress
    880  *                  (in range [0, 100]).
    881  */
    882 HRESULT Progress::setCurrentOperationProgress(ULONG aPercent)
    883 {
    884     AssertMsgReturn(aPercent <= 100, ("%u\n", aPercent), E_INVALIDARG);
    885 
    886     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    887 
    888     i_checkForAutomaticTimeout();
    889     if (mCancelable && mCanceled)
    890         AssertReturn(!mCompleted, E_FAIL);
    891     AssertReturn(!mCompleted && !mCanceled, E_FAIL);
    892 
    893     if (m_ulOperationPercent != aPercent)
    894     {
    895         m_ulOperationPercent = aPercent;
    896         ULONG actualPercent = 0;
    897         getPercent(&actualPercent);
    898         fireProgressPercentageChangedEvent(pEventSource, mId.toUtf16().raw(), actualPercent);
    899     }
    900 
    901     return S_OK;
    902 }
    903 
    904 /**
    905  * Signals that the current operation is successfully completed and advances to
    906  * the next operation. The operation percentage is reset to 0.
    907  *
    908  * @param aNextOperationDescription  Description of the next operation.
    909  * @param aNextOperationsWeight     Weight of the next operation.
    910  *
    911  * @note The current operation must not be the last one.
    912  */
    913 HRESULT Progress::setNextOperation(const com::Utf8Str &aNextOperationDescription, ULONG aNextOperationsWeight)
    914 {
    915     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    916 
    917     if (mCanceled)
    918         return E_FAIL;
    919     AssertReturn(!mCompleted, E_FAIL);
    920     AssertReturn(m_ulCurrentOperation + 1 < m_cOperations, E_FAIL);
    921 
    922     ++m_ulCurrentOperation;
    923     m_ulOperationsCompletedWeight += m_ulCurrentOperationWeight;
    924 
    925     m_operationDescription = aNextOperationDescription;
    926     m_ulCurrentOperationWeight = aNextOperationsWeight;
    927     m_ulOperationPercent = 0;
    928 
    929     LogThisFunc(("%s: aNextOperationsWeight = %d; m_ulCurrentOperation is now %d, m_ulOperationsCompletedWeight is now %d\n",
    930                  m_operationDescription.c_str(), aNextOperationsWeight, m_ulCurrentOperation, m_ulOperationsCompletedWeight));
    9311097
    9321098    /* wake up all waiting threads */
     
    9341100        RTSemEventMultiSignal(mCompletedSem);
    9351101
    936     ULONG actualPercent = 0;
    937     getPercent(&actualPercent);
    938     fireProgressPercentageChangedEvent(pEventSource, mId.toUtf16().raw(), actualPercent);
    939 
    940     return S_OK;
    941 }
    942 
    943 /**
    944  * @note XPCOM: when this method is not called on the main XPCOM thread, it
    945  *       simply blocks the thread until mCompletedSem is signalled. If the
    946  *       thread has its own event queue (hmm, what for?) that it must run, then
    947  *       calling this method will definitely freeze event processing.
    948  */
    949 HRESULT Progress::waitForCompletion(LONG aTimeout)
    950 {
    951     LogFlowThisFuncEnter();
    952     LogFlowThisFunc(("aTimeout=%d\n", aTimeout));
    953 
    954     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    955 
    956     /* if we're already completed, take a shortcut */
    957     if (!mCompleted)
    958     {
    959         int vrc = VINF_SUCCESS;
    960         bool fForever = aTimeout < 0;
    961         int64_t timeLeft = aTimeout;
    962         int64_t lastTime = RTTimeMilliTS();
    963 
    964         while (!mCompleted && (fForever || timeLeft > 0))
    965         {
    966             mWaitersCount++;
    967             alock.release();
    968             vrc = RTSemEventMultiWait(mCompletedSem,
    969                                       fForever ? RT_INDEFINITE_WAIT : (RTMSINTERVAL)timeLeft);
    970             alock.acquire();
    971             mWaitersCount--;
    972 
    973             /* the last waiter resets the semaphore */
    974             if (mWaitersCount == 0)
    975                 RTSemEventMultiReset(mCompletedSem);
    976 
    977             if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
    978                 break;
    979 
    980             if (!fForever)
    981             {
    982                 int64_t now = RTTimeMilliTS();
    983                 timeLeft -= now - lastTime;
    984                 lastTime = now;
    985             }
    986         }
    987 
    988         if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
    989             return setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to wait for the task completion (%Rrc)"), vrc);
    990     }
    991 
    992     LogFlowThisFuncLeave();
    993 
    994     return S_OK;
    995 }
    996 
    997 /**
    998  * @note XPCOM: when this method is not called on the main XPCOM thread, it
    999  *       simply blocks the thread until mCompletedSem is signalled. If the
    1000  *       thread has its own event queue (hmm, what for?) that it must run, then
    1001  *       calling this method will definitely freeze event processing.
    1002  */
    1003 HRESULT Progress::waitForOperationCompletion(ULONG aOperation, LONG aTimeout)
    1004 
    1005 {
    1006     LogFlowThisFuncEnter();
    1007     LogFlowThisFunc(("aOperation=%d, aTimeout=%d\n", aOperation, aTimeout));
    1008 
    1009     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1010 
    1011     CheckComArgExpr(aOperation, aOperation < m_cOperations);
    1012 
    1013     /* if we're already completed or if the given operation is already done,
    1014      * then take a shortcut */
    1015     if (    !mCompleted
    1016          && aOperation >= m_ulCurrentOperation)
    1017     {
    1018         int vrc = VINF_SUCCESS;
    1019         bool fForever = aTimeout < 0;
    1020         int64_t timeLeft = aTimeout;
    1021         int64_t lastTime = RTTimeMilliTS();
    1022 
    1023         while (    !mCompleted && aOperation >= m_ulCurrentOperation
    1024                 && (fForever || timeLeft > 0))
    1025         {
    1026             mWaitersCount ++;
    1027             alock.release();
    1028             vrc = RTSemEventMultiWait(mCompletedSem,
    1029                                       fForever ? RT_INDEFINITE_WAIT : (unsigned) timeLeft);
    1030             alock.acquire();
    1031             mWaitersCount--;
    1032 
    1033             /* the last waiter resets the semaphore */
    1034             if (mWaitersCount == 0)
    1035                 RTSemEventMultiReset(mCompletedSem);
    1036 
    1037             if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
    1038                 break;
    1039 
    1040             if (!fForever)
    1041             {
    1042                 int64_t now = RTTimeMilliTS();
    1043                 timeLeft -= now - lastTime;
    1044                 lastTime = now;
    1045             }
    1046         }
    1047 
    1048         if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
    1049             return setErrorBoth(E_FAIL, vrc, tr("Failed to wait for the operation completion (%Rrc)"), vrc);
    1050     }
    1051 
    1052     LogFlowThisFuncLeave();
    1053 
    1054     return S_OK;
    1055 }
    1056 
    1057 HRESULT Progress::cancel()
    1058 {
    1059     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1060 
    1061     if (!mCancelable)
    1062         return setError(VBOX_E_INVALID_OBJECT_STATE, tr("Operation cannot be canceled"));
    1063 
    1064     if (!mCanceled)
    1065     {
    1066         LogThisFunc(("Canceling\n"));
    1067         mCanceled = TRUE;
    1068         if (m_pfnCancelCallback)
    1069             m_pfnCancelCallback(m_pvCancelUserArg);
    1070 
    1071     }
    1072     else
    1073         LogThisFunc(("Already canceled\n"));
    1074 
    1075     return S_OK;
    1076 }
    1077 
    1078 HRESULT Progress::getEventSource(ComPtr<IEventSource> &aEventSource)
    1079 {
    1080     /* event source is const, no need to lock */
    1081     pEventSource.queryInterfaceTo(aEventSource.asOutParam());
    1082     return S_OK;
    1083 }
     1102    fireProgressTaskCompletedEvent(pEventSource, mId.toUtf16().raw());
     1103
     1104    return S_OK;
     1105}
     1106
    10841107
    10851108// private internal helpers
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