Changeset 73743 in vbox for trunk/src/VBox/Main/src-all
- Timestamp:
- Aug 17, 2018 5:56:34 PM (7 years ago)
- svn:sync-xref-src-repo-rev:
- 124424
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-all/ProgressImpl.cpp
r73716 r73743 486 486 } 487 487 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) 488 HRESULT Progress::i_waitForOtherProgressCompletion(const ComPtr<IProgress> &aProgressOther) 875 489 { 876 490 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. */ 987 493 988 494 HRESULT rc = S_OK; … … 994 500 ULONG cOp = 0; 995 501 /* Is the async process cancelable? */ 996 rc = aP ProgressAsync->COMGETTER(Cancelable)(&fCancelable);502 rc = aProgressOther->COMGETTER(Cancelable)(&fCancelable); 997 503 if (FAILED(rc)) return rc; 998 504 /* Loop as long as the sync process isn't completed. */ 999 while (SUCCEEDED(aP ProgressAsync->COMGETTER(Completed(&fCompleted))))505 while (SUCCEEDED(aProgressOther->COMGETTER(Completed(&fCompleted)))) 1000 506 { 1001 507 /* We can forward any cancel request to the async process only when … … 1007 513 if (fCanceled) 1008 514 { 1009 rc = aP ProgressAsync->Cancel();515 rc = aProgressOther->Cancel(); 1010 516 if (FAILED(rc)) return rc; 1011 517 } … … 1024 530 for (;;) 1025 531 { 1026 rc = aP ProgressAsync->COMGETTER(Operation(&curOp));532 rc = aProgressOther->COMGETTER(Operation(&curOp)); 1027 533 if (FAILED(rc)) return rc; 1028 534 if (cOp != curOp) … … 1030 536 Bstr bstr; 1031 537 ULONG currentWeight; 1032 rc = aP ProgressAsync->COMGETTER(OperationDescription(bstr.asOutParam()));538 rc = aProgressOther->COMGETTER(OperationDescription(bstr.asOutParam())); 1033 539 if (FAILED(rc)) return rc; 1034 rc = aP ProgressAsync->COMGETTER(OperationWeight(¤tWeight));540 rc = aProgressOther->COMGETTER(OperationWeight(¤tWeight)); 1035 541 if (FAILED(rc)) return rc; 1036 542 rc = SetNextOperation(bstr.raw(), currentWeight); … … 1042 548 } 1043 549 1044 rc = aP ProgressAsync->COMGETTER(OperationPercent(¤tPercent));550 rc = aProgressOther->COMGETTER(OperationPercent(¤tPercent)); 1045 551 if (FAILED(rc)) return rc; 1046 552 if (currentPercent != prevPercent) … … 1055 561 1056 562 /* Make sure the loop is not too tight */ 1057 rc = aP ProgressAsync->WaitForCompletion(100);563 rc = aProgressOther->WaitForCompletion(100); 1058 564 if (FAILED(rc)) return rc; 1059 565 } 1060 566 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 1061 578 LogFlowThisFuncLeave(); 1062 1063 579 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 */ 596 bool 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 */ 625 bool 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 684 HRESULT 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 692 HRESULT 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 } 699 HRESULT 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 717 HRESULT Progress::getCancelable(BOOL *aCancelable) 718 { 719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 720 721 *aCancelable = mCancelable; 722 723 return S_OK; 724 } 725 726 HRESULT 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 753 HRESULT 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 781 HRESULT Progress::getCompleted(BOOL *aCompleted) 782 { 783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 784 785 *aCompleted = mCompleted; 786 787 return S_OK; 788 } 789 790 HRESULT Progress::getCanceled(BOOL *aCanceled) 791 { 792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 793 794 *aCanceled = mCanceled; 795 796 return S_OK; 797 } 798 799 HRESULT 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 811 HRESULT 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 823 HRESULT 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 832 HRESULT 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 841 HRESULT 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 850 HRESULT 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 862 HRESULT 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 871 HRESULT 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 880 HRESULT 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 */ 901 HRESULT 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 */ 932 HRESULT 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 */ 968 HRESULT 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 */ 1022 HRESULT 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; 1064 1074 } 1065 1075
Note:
See TracChangeset
for help on using the changeset viewer.