Changeset 74804 in vbox for trunk/src/VBox/Main/src-all
- Timestamp:
- Oct 12, 2018 3:09:44 PM (6 years ago)
- svn:sync-xref-src-repo-rev:
- 125752
- Location:
- trunk/src/VBox/Main/src-all
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-all/ExtPackManagerImpl.cpp
r74219 r74804 1810 1810 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG); 1811 1811 1812 return pProgress->SetCurrentOperationProgress(uPercent); 1812 ComPtr<IInternalProgressControl> pProgressControl(pProgress); 1813 AssertReturn(!!pProgressControl, E_INVALIDARG); 1814 return pProgressControl->SetCurrentOperationProgress(uPercent); 1813 1815 } 1814 1816 … … 1828 1830 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG); 1829 1831 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); 1831 1835 } 1832 1836 1833 1837 /*static*/ DECLCALLBACK(uint32_t) 1834 1838 ExtPack::i_hlpWaitOtherProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IProgress) *pProgress, 1835 VBOXEXTPACK_IF_CS(IProgress) *pProgressOther )1839 VBOXEXTPACK_IF_CS(IProgress) *pProgressOther, uint32_t cTimeoutMS) 1836 1840 { 1837 1841 /* … … 1844 1848 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG); 1845 1849 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); 1848 1853 } 1849 1854 … … 1860 1865 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG); 1861 1866 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); 1864 1877 } 1865 1878 -
trunk/src/VBox/Main/src-all/ProgressImpl.cpp
r74759 r74804 370 370 } 371 371 372 return i_notifyCompleteEI(aResultCode, errorInfo);372 return notifyComplete(aResultCode, errorInfo); 373 373 } 374 374 … … 414 414 errorInfo->init(aResultCode, aIID, pcszComponent, text); 415 415 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 */ 430 bool Progress::i_setCancelCallback(void (*pfnCallback)(void *), void *pvUser) 431 { 435 432 AutoCaller autoCaller(this); 436 AssertComRCReturn RC(autoCaller.rc());433 AssertComRCReturn(autoCaller.rc(), false); 437 434 438 435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 439 436 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 490 HRESULT 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 498 HRESULT 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 } 505 HRESULT Progress::getInitiator(ComPtr<IUnknown> &aInitiator) 506 { 507 /* mInitiator/mParent are constant during life time, no need to lock */ 454 508 #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()); 458 518 #endif 459 519 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 523 HRESULT Progress::getCancelable(BOOL *aCancelable) 524 { 525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 526 527 *aCancelable = mCancelable; 528 529 return S_OK; 530 } 531 532 HRESULT 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 559 HRESULT 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 587 HRESULT Progress::getCompleted(BOOL *aCompleted) 588 { 589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 590 591 *aCompleted = mCompleted; 592 593 return S_OK; 594 } 595 596 HRESULT Progress::getCanceled(BOOL *aCanceled) 597 { 598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 599 600 *aCanceled = mCanceled; 601 602 return S_OK; 603 } 604 605 HRESULT 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 617 HRESULT 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 629 HRESULT 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 638 HRESULT 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 647 HRESULT 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 656 HRESULT 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 668 HRESULT 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 677 HRESULT 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 686 HRESULT 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 697 HRESULT 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 */ 714 HRESULT 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 */ 768 HRESULT 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 822 HRESULT 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 */ 853 HRESULT 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 875 HRESULT Progress::waitForOtherProgressCompletion(const ComPtr<IProgress> &aProgressOther, 876 ULONG aTimeoutMS) 470 877 { 471 878 LogFlowThisFuncEnter(); … … 483 890 rc = aProgressOther->COMGETTER(Cancelable)(&fCancelable); 484 891 if (FAILED(rc)) return rc; 892 893 uint64_t u64StopTime = UINT64_MAX; 894 if (aTimeoutMS > 0) 895 u64StopTime = RTTimeMilliTS() + aTimeoutMS; 485 896 /* Loop as long as the sync process isn't completed. */ 486 897 while (SUCCEEDED(aProgressOther->COMGETTER(Completed(&fCompleted)))) … … 541 952 break; 542 953 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 } 546 975 } 547 976 … … 562 991 563 992 /** 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 */ 1001 HRESULT 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 /** 564 1032 * Notify the progress object that we're almost at the point of no return. 565 1033 * … … 570 1038 * user believe it was rolled back. 571 1039 * 572 * @returns Success indicator.573 * @retval trueon success.574 * @retval falseif the progress object has already been canceled or is in an1040 * @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 575 1043 * invalid state 576 1044 */ 577 bool Progress::i_notifyPointOfNoReturn(void) 578 { 579 AutoCaller autoCaller(this); 580 AssertComRCReturn(autoCaller.rc(), false); 581 1045 HRESULT Progress::notifyPointOfNoReturn(void) 1046 { 582 1047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 583 1048 584 1049 if (mCanceled) 585 1050 { 586 LogThisFunc(("returns fa lse\n"));587 return false;1051 LogThisFunc(("returns failure\n")); 1052 return E_FAIL; 588 1053 } 589 1054 590 1055 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 an601 * invalid state602 * 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 */ 1068 HRESULT 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)); 610 1075 611 1076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 612 1077 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 683 1092 #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()); 693 1096 #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 else715 {716 ULONG ulPercent = (ULONG)i_calcTotalPercent();717 // do not report 100% until we're really really done with everything718 // as the Qt GUI dismisses progress dialogs in that case719 if ( ulPercent == 100720 && ( m_ulOperationPercent < 100721 || (m_ulCurrentOperation < m_cOperations -1)722 )723 )724 *aPercent = 99;725 else726 *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 else741 {742 double dPercentDone = i_calcTotalPercent();743 if (dPercentDone < 1)744 *aTimeRemaining = -1; // unreliable, or avoid division by 0 below745 else746 {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 else838 *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 methods874 /////////////////////////////////////////////////////////////////////////////875 876 /**877 * Updates the percentage value of the current operation.878 *879 * @param aPercent New percentage value of the operation in progress880 * (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 to906 * 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));931 1097 932 1098 /* wake up all waiting threads */ … … 934 1100 RTSemEventMultiSignal(mCompletedSem); 935 1101 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 1084 1107 1085 1108 // private internal helpers
Note:
See TracChangeset
for help on using the changeset viewer.