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