VirtualBox

source: vbox/trunk/src/VBox/Main/ProgressImpl.cpp@ 25414

Last change on this file since 25414 was 25310, checked in by vboxsync, 14 years ago

Main: lock validator, first batch: implement per-thread stack to trace locking (disabled by default, use VBOX_WITH_LOCK_VALIDATOR, but that WILL FAIL presently)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.9 KB
RevLine 
[13580]1/* $Id: ProgressImpl.cpp 25310 2009-12-10 17:06:44Z vboxsync $ */
[1]2/** @file
3 *
[18252]4 * VirtualBox Progress COM class implementation
[1]5 */
6
7/*
[18252]8 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
[1]9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
[5999]13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
[8155]17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
[1]21 */
22
[13915]23#include <iprt/types.h>
24
[3191]25#if defined (VBOX_WITH_XPCOM)
[1]26#include <nsIServiceManager.h>
27#include <nsIExceptionService.h>
28#include <nsCOMPtr.h>
[13580]29#endif /* defined (VBOX_WITH_XPCOM) */
[1]30
31#include "ProgressImpl.h"
[13580]32
[1]33#include "VirtualBoxImpl.h"
34#include "VirtualBoxErrorInfoImpl.h"
35
36#include "Logging.h"
37
38#include <iprt/time.h>
39#include <iprt/semaphore.h>
40
41#include <VBox/err.h>
42
43////////////////////////////////////////////////////////////////////////////////
44// ProgressBase class
45////////////////////////////////////////////////////////////////////////////////
46
47// constructor / destructor
48////////////////////////////////////////////////////////////////////////////////
49
[13580]50DEFINE_EMPTY_CTOR_DTOR (ProgressBase)
51
52/**
53 * Subclasses must call this method from their FinalConstruct() implementations.
54 */
[1]55HRESULT ProgressBase::FinalConstruct()
56{
57 mCancelable = FALSE;
58 mCompleted = FALSE;
59 mCanceled = FALSE;
60 mResultCode = S_OK;
61
[24961]62 m_cOperations
63 = m_ulTotalOperationsWeight
64 = m_ulOperationsCompletedWeight
65 = m_ulCurrentOperation
66 = m_ulCurrentOperationWeight
67 = m_ulOperationPercent
68 = m_cMsTimeout
69 = 0;
[18269]70
[18406]71 // get creation timestamp
72 m_ullTimestamp = RTTimeMilliTS();
73
[23810]74 m_pfnCancelCallback = NULL;
75 m_pvCancelUserArg = NULL;
76
[1]77 return S_OK;
78}
79
[13580]80// protected initializer/uninitializer for internal purposes only
[1]81////////////////////////////////////////////////////////////////////////////////
82
83/**
[13580]84 * Initializes the progress base object.
[1]85 *
[13580]86 * Subclasses should call this or any other #protectedInit() method from their
87 * init() implementations.
[1]88 *
[13580]89 * @param aAutoInitSpan AutoInitSpan object instantiated by a subclass.
90 * @param aParent Parent object (only for server-side Progress objects).
91 * @param aInitiator Initiator of the task (for server-side objects. Can be
92 * NULL which means initiator = parent, otherwise must not
93 * be NULL).
94 * @param aDescription Task description.
95 * @param aID Address of result GUID structure (optional).
[1]96 *
[13580]97 * @return COM result indicator.
[1]98 */
[13580]99HRESULT ProgressBase::protectedInit (AutoInitSpan &aAutoInitSpan,
[1]100#if !defined (VBOX_COM_INPROC)
101 VirtualBox *aParent,
102#endif
103 IUnknown *aInitiator,
[15051]104 CBSTR aDescription, OUT_GUID aId /* = NULL */)
[1]105{
[13580]106 /* Guarantees subclasses call this method at the proper time */
107 NOREF (aAutoInitSpan);
108
[18269]109 AutoCaller autoCaller(this);
[21878]110 AssertReturn(autoCaller.state() == InInit, E_FAIL);
[13580]111
[1]112#if !defined (VBOX_COM_INPROC)
[21878]113 AssertReturn(aParent, E_INVALIDARG);
[1]114#else
[21878]115 AssertReturn(aInitiator, E_INVALIDARG);
[1]116#endif
117
[21878]118 AssertReturn(aDescription, E_INVALIDARG);
[1]119
120#if !defined (VBOX_COM_INPROC)
[13580]121 /* share parent weakly */
[21878]122 unconst(mParent) = aParent;
[1]123#endif
124
125#if !defined (VBOX_COM_INPROC)
[13580]126 /* assign (and therefore addref) initiator only if it is not VirtualBox
127 * (to avoid cycling); otherwise mInitiator will remain null which means
128 * that it is the same as the parent */
[1]129 if (aInitiator && !mParent.equalsTo (aInitiator))
[21878]130 unconst(mInitiator) = aInitiator;
[1]131#else
[21878]132 unconst(mInitiator) = aInitiator;
[1]133#endif
134
[21878]135 unconst(mId).create();
[1]136 if (aId)
[21878]137 mId.cloneTo(aId);
[1]138
139#if !defined (VBOX_COM_INPROC)
[18643]140 /* add to the global collection of progress operations (note: after
[13580]141 * creating mId) */
[1]142 mParent->addProgress (this);
143#endif
144
[21878]145 unconst(mDescription) = aDescription;
[13580]146
[1]147 return S_OK;
148}
149
150/**
[13580]151 * Initializes the progress base object.
[1]152 *
[13580]153 * This is a special initializer that doesn't initialize any field. Used by one
154 * of the Progress::init() forms to create sub-progress operations combined
155 * together using a CombinedProgress instance, so it doesn't require the parent,
156 * initiator, description and doesn't create an ID.
[1]157 *
[13580]158 * Subclasses should call this or any other #protectedInit() method from their
159 * init() implementations.
160 *
161 * @param aAutoInitSpan AutoInitSpan object instantiated by a subclass.
[1]162 */
[13580]163HRESULT ProgressBase::protectedInit (AutoInitSpan &aAutoInitSpan)
[1]164{
[13580]165 /* Guarantees subclasses call this method at the proper time */
166 NOREF (aAutoInitSpan);
167
[1]168 return S_OK;
169}
170
171/**
[13580]172 * Uninitializes the instance.
[1]173 *
[13580]174 * Subclasses should call this from their uninit() implementations.
[1]175 *
[13580]176 * @param aAutoUninitSpan AutoUninitSpan object instantiated by a subclass.
177 *
178 * @note Using the mParent member after this method returns is forbidden.
[1]179 */
[13580]180void ProgressBase::protectedUninit (AutoUninitSpan &aAutoUninitSpan)
[1]181{
[13580]182 /* release initiator (effective only if mInitiator has been assigned in
183 * init()) */
[21878]184 unconst(mInitiator).setNull();
[1]185
186#if !defined (VBOX_COM_INPROC)
187 if (mParent)
188 {
[13580]189 /* remove the added progress on failure to complete the initialization */
190 if (aAutoUninitSpan.initFailed() && !mId.isEmpty())
191 mParent->removeProgress (mId);
192
[21878]193 unconst(mParent).setNull();
[1]194 }
195#endif
196}
197
198// IProgress properties
199/////////////////////////////////////////////////////////////////////////////
200
[19239]201STDMETHODIMP ProgressBase::COMGETTER(Id) (BSTR *aId)
[1]202{
[14972]203 CheckComArgOutPointerValid(aId);
[1]204
[18269]205 AutoCaller autoCaller(this);
[25149]206 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]207
[13580]208 /* mId is constant during life time, no need to lock */
[21878]209 mId.toUtf16().cloneTo(aId);
[1]210
211 return S_OK;
212}
213
214STDMETHODIMP ProgressBase::COMGETTER(Description) (BSTR *aDescription)
215{
[14972]216 CheckComArgOutPointerValid(aDescription);
[1]217
[18269]218 AutoCaller autoCaller(this);
[25149]219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]220
[13580]221 /* mDescription is constant during life time, no need to lock */
[21878]222 mDescription.cloneTo(aDescription);
[1]223
224 return S_OK;
225}
226
227STDMETHODIMP ProgressBase::COMGETTER(Initiator) (IUnknown **aInitiator)
228{
[14972]229 CheckComArgOutPointerValid(aInitiator);
[1]230
[18269]231 AutoCaller autoCaller(this);
[25149]232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]233
[13580]234 /* mInitiator/mParent are constant during life time, no need to lock */
235
[1]236#if !defined (VBOX_COM_INPROC)
237 if (mInitiator)
[21878]238 mInitiator.queryInterfaceTo(aInitiator);
[1]239 else
[21878]240 mParent.queryInterfaceTo(aInitiator);
[1]241#else
[21878]242 mInitiator.queryInterfaceTo(aInitiator);
[1]243#endif
244
245 return S_OK;
246}
247
248STDMETHODIMP ProgressBase::COMGETTER(Cancelable) (BOOL *aCancelable)
249{
[14972]250 CheckComArgOutPointerValid(aCancelable);
[1]251
[18269]252 AutoCaller autoCaller(this);
[25149]253 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]254
[25310]255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[13580]256
[1]257 *aCancelable = mCancelable;
[13580]258
[1]259 return S_OK;
260}
261
[18406]262/**
263 * Internal helper to compute the total percent value based on the member values and
264 * returns it as a "double". This is used both by GetPercent (which returns it as a
265 * rounded ULONG) and GetTimeRemaining().
266 *
267 * Requires locking by the caller!
268 *
269 * @return fractional percentage as a double value.
270 */
271double ProgressBase::calcTotalPercent()
272{
273 // avoid division by zero
274 if (m_ulTotalOperationsWeight == 0)
275 return 0;
276
277 double dPercent = ( (double)m_ulOperationsCompletedWeight // weight of operations that have been completed
278 + ((double)m_ulOperationPercent * (double)m_ulCurrentOperationWeight / (double)100) // plus partial weight of the current operation
279 ) * (double)100 / (double)m_ulTotalOperationsWeight;
280
281 return dPercent;
282}
283
[24969]284/**
285 * Internal helper for automatically timing out the operation.
286 *
287 * The caller should hold the object write lock.
288 */
289void ProgressBase::checkForAutomaticTimeout(void)
290{
291 if ( m_cMsTimeout
292 && mCancelable
293 && !mCanceled
294 && RTTimeMilliTS() - m_ullTimestamp > m_cMsTimeout
295 )
296 Cancel();
297}
298
299
[18406]300STDMETHODIMP ProgressBase::COMGETTER(TimeRemaining)(LONG *aTimeRemaining)
301{
302 CheckComArgOutPointerValid(aTimeRemaining);
303
304 AutoCaller autoCaller(this);
[25149]305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[18406]306
[25310]307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[18406]308
309 if (mCompleted)
310 *aTimeRemaining = 0;
311 else
312 {
313 double dPercentDone = calcTotalPercent();
314 if (dPercentDone < 1)
315 *aTimeRemaining = -1; // unreliable, or avoid division by 0 below
316 else
317 {
318 uint64_t ullTimeNow = RTTimeMilliTS();
319 uint64_t ullTimeElapsed = ullTimeNow - m_ullTimestamp;
320 uint64_t ullTimeTotal = (uint64_t)(ullTimeElapsed / dPercentDone * 100);
321 uint64_t ullTimeRemaining = ullTimeTotal - ullTimeElapsed;
322
323// Log(("ProgressBase::GetTimeRemaining: dPercentDone %RI32, ullTimeNow = %RI64, ullTimeElapsed = %RI64, ullTimeTotal = %RI64, ullTimeRemaining = %RI64\n",
324// (uint32_t)dPercentDone, ullTimeNow, ullTimeElapsed, ullTimeTotal, ullTimeRemaining));
325
326 *aTimeRemaining = (LONG)(ullTimeRemaining / 1000);
327 }
328 }
329
330 return S_OK;
331}
332
[18269]333STDMETHODIMP ProgressBase::COMGETTER(Percent)(ULONG *aPercent)
[1]334{
[14972]335 CheckComArgOutPointerValid(aPercent);
[1]336
[18269]337 AutoCaller autoCaller(this);
[25149]338 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]339
[24969]340 checkForAutomaticTimeout();
[13580]341
[24969]342 /* checkForAutomaticTimeout requires a write lock. */
[25310]343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[24969]344
[21878]345 if (mCompleted && SUCCEEDED(mResultCode))
[1]346 *aPercent = 100;
347 else
348 {
[18406]349 ULONG ulPercent = (ULONG)calcTotalPercent();
[18394]350 // do not report 100% until we're really really done with everything as the Qt GUI dismisses progress dialogs in that case
351 if ( ulPercent == 100
352 && ( m_ulOperationPercent < 100
353 || (m_ulCurrentOperation < m_cOperations -1)
354 )
355 )
356 *aPercent = 99;
357 else
358 *aPercent = ulPercent;
[1]359 }
360
[24969]361 checkForAutomaticTimeout();
362
[1]363 return S_OK;
364}
365
366STDMETHODIMP ProgressBase::COMGETTER(Completed) (BOOL *aCompleted)
367{
[14972]368 CheckComArgOutPointerValid(aCompleted);
[1]369
[18269]370 AutoCaller autoCaller(this);
[25149]371 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]372
[25310]373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[13580]374
[1]375 *aCompleted = mCompleted;
[13580]376
[1]377 return S_OK;
378}
379
380STDMETHODIMP ProgressBase::COMGETTER(Canceled) (BOOL *aCanceled)
381{
[14972]382 CheckComArgOutPointerValid(aCanceled);
[1]383
[18269]384 AutoCaller autoCaller(this);
[25149]385 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]386
[25310]387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[13580]388
[1]389 *aCanceled = mCanceled;
[13580]390
[1]391 return S_OK;
392}
393
[20220]394STDMETHODIMP ProgressBase::COMGETTER(ResultCode) (LONG *aResultCode)
[1]395{
[14972]396 CheckComArgOutPointerValid(aResultCode);
[1]397
[18269]398 AutoCaller autoCaller(this);
[25149]399 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]400
[25310]401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[13580]402
[1]403 if (!mCompleted)
404 return setError (E_FAIL,
405 tr ("Result code is not available, operation is still in progress"));
406
407 *aResultCode = mResultCode;
[13580]408
[1]409 return S_OK;
410}
411
412STDMETHODIMP ProgressBase::COMGETTER(ErrorInfo) (IVirtualBoxErrorInfo **aErrorInfo)
413{
[14972]414 CheckComArgOutPointerValid(aErrorInfo);
[1]415
[18269]416 AutoCaller autoCaller(this);
[25149]417 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]418
[25310]419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[13580]420
[1]421 if (!mCompleted)
422 return setError (E_FAIL,
423 tr ("Error info is not available, operation is still in progress"));
424
[21878]425 mErrorInfo.queryInterfaceTo(aErrorInfo);
[13580]426
[1]427 return S_OK;
428}
429
430STDMETHODIMP ProgressBase::COMGETTER(OperationCount) (ULONG *aOperationCount)
431{
[14972]432 CheckComArgOutPointerValid(aOperationCount);
[1]433
[18269]434 AutoCaller autoCaller(this);
[25149]435 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]436
[25310]437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[13580]438
[18269]439 *aOperationCount = m_cOperations;
[13580]440
[1]441 return S_OK;
442}
443
444STDMETHODIMP ProgressBase::COMGETTER(Operation) (ULONG *aOperation)
445{
[14972]446 CheckComArgOutPointerValid(aOperation);
[1]447
[18269]448 AutoCaller autoCaller(this);
[25149]449 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]450
[25310]451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[13580]452
[18269]453 *aOperation = m_ulCurrentOperation;
[13580]454
[1]455 return S_OK;
456}
457
458STDMETHODIMP ProgressBase::COMGETTER(OperationDescription) (BSTR *aOperationDescription)
459{
[14972]460 CheckComArgOutPointerValid(aOperationDescription);
[1]461
[18269]462 AutoCaller autoCaller(this);
[25149]463 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]464
[25310]465 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[13580]466
[18269]467 m_bstrOperationDescription.cloneTo(aOperationDescription);
[13580]468
[1]469 return S_OK;
470}
471
[18269]472STDMETHODIMP ProgressBase::COMGETTER(OperationPercent)(ULONG *aOperationPercent)
[1]473{
[14972]474 CheckComArgOutPointerValid(aOperationPercent);
[1]475
[18269]476 AutoCaller autoCaller(this);
[25149]477 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[1]478
[25310]479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[13580]480
[21878]481 if (mCompleted && SUCCEEDED(mResultCode))
[1]482 *aOperationPercent = 100;
483 else
[18269]484 *aOperationPercent = m_ulOperationPercent;
[13580]485
[1]486 return S_OK;
487}
488
[24961]489STDMETHODIMP ProgressBase::COMSETTER(Timeout)(ULONG aTimeout)
490{
491 AutoCaller autoCaller(this);
[25149]492 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[24961]493
[25310]494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[24961]495
496 if (!mCancelable)
497 return setError(VBOX_E_INVALID_OBJECT_STATE,
498 tr("Operation cannot be canceled"));
499
500 LogThisFunc(("%#x => %#x\n", m_cMsTimeout, aTimeout));
501 m_cMsTimeout = aTimeout;
502 return S_OK;
503}
504
505STDMETHODIMP ProgressBase::COMGETTER(Timeout)(ULONG *aTimeout)
506{
507 CheckComArgOutPointerValid(aTimeout);
508
509 AutoCaller autoCaller(this);
[25149]510 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[24961]511
[25310]512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[24961]513
514 *aTimeout = m_cMsTimeout;
515 return S_OK;
516}
517
[13580]518// public methods only for internal purposes
[1]519////////////////////////////////////////////////////////////////////////////////
[13580]520
521/**
522 * Sets the error info stored in the given progress object as the error info on
523 * the current thread.
524 *
525 * This method is useful if some other COM method uses IProgress to wait for
526 * something and then wants to return a failed result of the operation it was
527 * waiting for as its own result retaining the extended error info.
528 *
529 * If the operation tracked by this progress object is completed successfully
530 * and returned S_OK, this method does nothing but returns S_OK. Otherwise, the
531 * failed warning or error result code specified at progress completion is
532 * returned and the extended error info object (if any) is set on the current
533 * thread.
534 *
535 * Note that the given progress object must be completed, otherwise this method
536 * will assert and fail.
537 */
538/* static */
539HRESULT ProgressBase::setErrorInfoOnThread (IProgress *aProgress)
540{
[21878]541 AssertReturn(aProgress != NULL, E_INVALIDARG);
[13580]542
[20220]543 LONG iRc;
544 HRESULT rc = aProgress->COMGETTER(ResultCode) (&iRc);
[21878]545 AssertComRCReturnRC(rc);
[20220]546 HRESULT resultCode = iRc;
[13580]547
548 if (resultCode == S_OK)
549 return resultCode;
550
[21878]551 ComPtr<IVirtualBoxErrorInfo> errorInfo;
[13580]552 rc = aProgress->COMGETTER(ErrorInfo) (errorInfo.asOutParam());
[21878]553 AssertComRCReturnRC(rc);
[13580]554
555 if (!errorInfo.isNull())
556 setErrorInfo (errorInfo);
557
558 return resultCode;
559}
560
[23810]561/**
[23827]562 * Sets the cancelation callback, checking for cancelation first.
[23810]563 *
[23827]564 * @returns Success indicator.
565 * @retval true on success.
566 * @retval false if the progress object has already been canceled or is in an
567 * invalid state
568 *
[23810]569 * @param pfnCallback The function to be called upon cancelation.
570 * @param pvUser The callback argument.
571 */
[23827]572bool ProgressBase::setCancelCallback(void (*pfnCallback)(void *), void *pvUser)
[23810]573{
574 AutoCaller autoCaller(this);
[23827]575 AssertComRCReturn(autoCaller.rc(), false);
[23810]576
[25310]577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[23810]578
[24969]579 checkForAutomaticTimeout();
[23827]580 if (mCanceled)
581 return false;
582
[23810]583 m_pvCancelUserArg = pvUser;
584 m_pfnCancelCallback = pfnCallback;
[23827]585 return true;
[23810]586}
587
[13580]588////////////////////////////////////////////////////////////////////////////////
[1]589// Progress class
590////////////////////////////////////////////////////////////////////////////////
591
592HRESULT Progress::FinalConstruct()
593{
594 HRESULT rc = ProgressBase::FinalConstruct();
[25149]595 if (FAILED(rc)) return rc;
[1]596
[351]597 mCompletedSem = NIL_RTSEMEVENTMULTI;
[1]598 mWaitersCount = 0;
599
600 return S_OK;
601}
602
603void Progress::FinalRelease()
604{
605 uninit();
606}
607
608// public initializer/uninitializer for internal purposes only
609////////////////////////////////////////////////////////////////////////////////
610
611/**
[18276]612 * Initializes the normal progress object. With this variant, one can have
613 * an arbitrary number of sub-operation which IProgress can analyze to
614 * have a weighted progress computed.
[1]615 *
[18276]616 * For example, say that one IProgress is supposed to track the cloning
617 * of two hard disk images, which are 100 MB and 1000 MB in size, respectively,
618 * and each of these hard disks should be one sub-operation of the IProgress.
619 *
620 * Obviously the progress would be misleading if the progress displayed 50%
621 * after the smaller image was cloned and would then take much longer for
622 * the second half.
623 *
624 * With weighted progress, one can invoke the following calls:
625 *
626 * 1) create progress object with cOperations = 2 and ulTotalOperationsWeight =
627 * 1100 (100 MB plus 1100, but really the weights can be any ULONG); pass
628 * in ulFirstOperationWeight = 100 for the first sub-operation
629 *
630 * 2) Then keep calling setCurrentOperationProgress() with a percentage
631 * for the first image; the total progress will increase up to a value
632 * of 9% (100MB / 1100MB * 100%).
633 *
634 * 3) Then call setNextOperation with the second weight (1000 for the megabytes
635 * of the second disk).
636 *
637 * 4) Then keep calling setCurrentOperationProgress() with a percentage for
638 * the second image, where 100% of the operation will then yield a 100%
639 * progress of the entire task.
640 *
641 * Weighting is optional; you can simply assign a weight of 1 to each operation
642 * and pass ulTotalOperationsWeight == cOperations to this constructor (but
643 * for that variant and for backwards-compatibility a simpler constructor exists
644 * in ProgressImpl.h as well).
645 *
646 * Even simpler, if you need no sub-operations at all, pass in cOperations =
647 * ulTotalOperationsWeight = ulFirstOperationWeight = 1.
648 *
[13580]649 * @param aParent See ProgressBase::init().
650 * @param aInitiator See ProgressBase::init().
651 * @param aDescription See ProgressBase::init().
652 * @param aCancelable Flag whether the task maybe canceled.
[18276]653 * @param cOperations Number of operations within this task (at least 1).
654 * @param ulTotalOperationsWeight Total weight of operations; must be the sum of ulFirstOperationWeight and
655 * what is later passed with each subsequent setNextOperation() call.
656 * @param bstrFirstOperationDescription Description of the first operation.
657 * @param ulFirstOperationWeight Weight of first sub-operation.
[13580]658 * @param aId See ProgressBase::init().
[1]659 */
660HRESULT Progress::init (
661#if !defined (VBOX_COM_INPROC)
662 VirtualBox *aParent,
663#endif
664 IUnknown *aInitiator,
[23223]665 CBSTR aDescription,
666 BOOL aCancelable,
667 ULONG cOperations,
668 ULONG ulTotalOperationsWeight,
669 CBSTR bstrFirstOperationDescription,
670 ULONG ulFirstOperationWeight,
[15051]671 OUT_GUID aId /* = NULL */)
[1]672{
[23223]673 LogFlowThisFunc(("aDescription=\"%ls\", cOperations=%d, ulTotalOperationsWeight=%d, bstrFirstOperationDescription=\"%ls\", ulFirstOperationWeight=%d\n",
674 aDescription,
675 cOperations,
676 ulTotalOperationsWeight,
677 bstrFirstOperationDescription,
678 ulFirstOperationWeight));
[1]679
[18269]680 AssertReturn(bstrFirstOperationDescription, E_INVALIDARG);
681 AssertReturn(ulTotalOperationsWeight >= 1, E_INVALIDARG);
[1]682
[13580]683 /* Enclose the state transition NotReady->InInit->Ready */
[21878]684 AutoInitSpan autoInitSpan(this);
685 AssertReturn(autoInitSpan.isOk(), E_FAIL);
[1]686
687 HRESULT rc = S_OK;
688
[13580]689 rc = ProgressBase::protectedInit (autoInitSpan,
[1]690#if !defined (VBOX_COM_INPROC)
[13580]691 aParent,
[1]692#endif
[13580]693 aInitiator, aDescription, aId);
[25149]694 if (FAILED(rc)) return rc;
[1]695
[13580]696 mCancelable = aCancelable;
[1]697
[18269]698 m_cOperations = cOperations;
699 m_ulTotalOperationsWeight = ulTotalOperationsWeight;
700 m_ulOperationsCompletedWeight = 0;
701 m_ulCurrentOperation = 0;
702 m_bstrOperationDescription = bstrFirstOperationDescription;
703 m_ulCurrentOperationWeight = ulFirstOperationWeight;
704 m_ulOperationPercent = 0;
[1]705
[13580]706 int vrc = RTSemEventMultiCreate (&mCompletedSem);
707 ComAssertRCRet (vrc, E_FAIL);
[1]708
[13580]709 RTSemEventMultiReset (mCompletedSem);
[1]710
[13580]711 /* Confirm a successful initialization when it's the case */
[21878]712 if (SUCCEEDED(rc))
[13580]713 autoInitSpan.setSucceeded();
[1]714
715 return rc;
716}
717
[13580]718/**
719 * Initializes the sub-progress object that represents a specific operation of
720 * the whole task.
721 *
722 * Objects initialized with this method are then combined together into the
723 * single task using a CombinedProgress instance, so it doesn't require the
724 * parent, initiator, description and doesn't create an ID. Note that calling
725 * respective getter methods on an object initialized with this method is
726 * useless. Such objects are used only to provide a separate wait semaphore and
727 * store individual operation descriptions.
728 *
729 * @param aCancelable Flag whether the task maybe canceled.
730 * @param aOperationCount Number of sub-operations within this task (at least 1).
731 * @param aOperationDescription Description of the individual operation.
732 */
[18269]733HRESULT Progress::init(BOOL aCancelable,
734 ULONG aOperationCount,
735 CBSTR aOperationDescription)
[1]736{
[21878]737 LogFlowThisFunc(("aOperationDescription=\"%ls\"\n", aOperationDescription));
[1]738
[13580]739 /* Enclose the state transition NotReady->InInit->Ready */
[21878]740 AutoInitSpan autoInitSpan(this);
741 AssertReturn(autoInitSpan.isOk(), E_FAIL);
[1]742
743 HRESULT rc = S_OK;
744
[13580]745 rc = ProgressBase::protectedInit (autoInitSpan);
[25149]746 if (FAILED(rc)) return rc;
[1]747
[13580]748 mCancelable = aCancelable;
[1]749
[18269]750 // for this variant we assume for now that all operations are weighed "1"
751 // and equal total weight = operation count
752 m_cOperations = aOperationCount;
753 m_ulTotalOperationsWeight = aOperationCount;
754 m_ulOperationsCompletedWeight = 0;
755 m_ulCurrentOperation = 0;
756 m_bstrOperationDescription = aOperationDescription;
757 m_ulCurrentOperationWeight = 1;
758 m_ulOperationPercent = 0;
[1]759
[13580]760 int vrc = RTSemEventMultiCreate (&mCompletedSem);
761 ComAssertRCRet (vrc, E_FAIL);
[1]762
[13580]763 RTSemEventMultiReset (mCompletedSem);
[1]764
[13580]765 /* Confirm a successful initialization when it's the case */
[21878]766 if (SUCCEEDED(rc))
[13580]767 autoInitSpan.setSucceeded();
[1]768
769 return rc;
770}
771
772/**
[13580]773 * Uninitializes the instance and sets the ready flag to FALSE.
774 *
775 * Called either from FinalRelease() or by the parent when it gets destroyed.
[1]776 */
777void Progress::uninit()
778{
[21878]779 LogFlowThisFunc(("\n"));
[1]780
[13580]781 /* Enclose the state transition Ready->InUninit->NotReady */
[21878]782 AutoUninitSpan autoUninitSpan(this);
[13580]783 if (autoUninitSpan.uninitDone())
[1]784 return;
785
[13580]786 /* wake up all threads still waiting on occasion */
[1]787 if (mWaitersCount > 0)
788 {
789 LogFlow (("WARNING: There are still %d threads waiting for '%ls' completion!\n",
790 mWaitersCount, mDescription.raw()));
791 RTSemEventMultiSignal (mCompletedSem);
792 }
793
794 RTSemEventMultiDestroy (mCompletedSem);
795
[13580]796 ProgressBase::protectedUninit (autoUninitSpan);
[1]797}
798
799// IProgress properties
800/////////////////////////////////////////////////////////////////////////////
801
802// IProgress methods
803/////////////////////////////////////////////////////////////////////////////
804
805/**
[18643]806 * @note XPCOM: when this method is not called on the main XPCOM thread, it
[13580]807 * simply blocks the thread until mCompletedSem is signalled. If the
808 * thread has its own event queue (hmm, what for?) that it must run, then
[18643]809 * calling this method will definitely freeze event processing.
[1]810 */
811STDMETHODIMP Progress::WaitForCompletion (LONG aTimeout)
812{
[13580]813 LogFlowThisFuncEnter();
[21878]814 LogFlowThisFunc(("aTimeout=%d\n", aTimeout));
[1]815
[18269]816 AutoCaller autoCaller(this);
[25149]817 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]818
[25310]819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]820
[13580]821 /* if we're already completed, take a shortcut */
[1]822 if (!mCompleted)
823 {
824 RTTIMESPEC time;
[24989]825 RTTimeNow(&time); /** @todo r=bird: Use monotonic time (RTTimeMilliTS()) here because of daylight saving and things like that. */
[1]826
827 int vrc = VINF_SUCCESS;
[23223]828 bool fForever = aTimeout < 0;
[1]829 int64_t timeLeft = aTimeout;
[24989]830 int64_t lastTime = RTTimeSpecGetMilli(&time);
[1]831
[23223]832 while (!mCompleted && (fForever || timeLeft > 0))
[1]833 {
[23223]834 mWaitersCount++;
[13580]835 alock.leave();
[24989]836 vrc = RTSemEventMultiWait(mCompletedSem,
837 fForever ? RT_INDEFINITE_WAIT : (unsigned)timeLeft);
[13580]838 alock.enter();
[23223]839 mWaitersCount--;
[1]840
[13580]841 /* the last waiter resets the semaphore */
[1]842 if (mWaitersCount == 0)
[23223]843 RTSemEventMultiReset(mCompletedSem);
[1]844
[21878]845 if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
[1]846 break;
847
[23223]848 if (!fForever)
[1]849 {
850 RTTimeNow (&time);
[23223]851 timeLeft -= RTTimeSpecGetMilli(&time) - lastTime;
852 lastTime = RTTimeSpecGetMilli(&time);
[1]853 }
854 }
855
[21878]856 if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
[23223]857 return setError(VBOX_E_IPRT_ERROR,
858 tr("Failed to wait for the task completion (%Rrc)"),
859 vrc);
[1]860 }
861
[13580]862 LogFlowThisFuncLeave();
863
[1]864 return S_OK;
865}
866
867/**
[18643]868 * @note XPCOM: when this method is not called on the main XPCOM thread, it
[13580]869 * simply blocks the thread until mCompletedSem is signalled. If the
870 * thread has its own event queue (hmm, what for?) that it must run, then
[18643]871 * calling this method will definitely freeze event processing.
[1]872 */
[18269]873STDMETHODIMP Progress::WaitForOperationCompletion(ULONG aOperation, LONG aTimeout)
[1]874{
[13580]875 LogFlowThisFuncEnter();
[21878]876 LogFlowThisFunc(("aOperation=%d, aTimeout=%d\n", aOperation, aTimeout));
[1]877
[18269]878 AutoCaller autoCaller(this);
[25149]879 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]880
[25310]881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]882
[18269]883 CheckComArgExpr(aOperation, aOperation < m_cOperations);
[1]884
[13580]885 /* if we're already completed or if the given operation is already done,
886 * then take a shortcut */
[18269]887 if ( !mCompleted
888 && aOperation >= m_ulCurrentOperation)
[1]889 {
890 RTTIMESPEC time;
891 RTTimeNow (&time);
892
893 int vrc = VINF_SUCCESS;
[23223]894 bool fForever = aTimeout < 0;
[1]895 int64_t timeLeft = aTimeout;
896 int64_t lastTime = RTTimeSpecGetMilli (&time);
897
[23223]898 while ( !mCompleted && aOperation >= m_ulCurrentOperation
899 && (fForever || timeLeft > 0))
[1]900 {
901 mWaitersCount ++;
[13580]902 alock.leave();
[24989]903 vrc = RTSemEventMultiWait(mCompletedSem,
904 fForever ? RT_INDEFINITE_WAIT : (unsigned) timeLeft);
[13580]905 alock.enter();
[23223]906 mWaitersCount--;
[1]907
[13580]908 /* the last waiter resets the semaphore */
[1]909 if (mWaitersCount == 0)
[23223]910 RTSemEventMultiReset(mCompletedSem);
[1]911
[21878]912 if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
[1]913 break;
914
[23223]915 if (!fForever)
[1]916 {
[23223]917 RTTimeNow(&time);
918 timeLeft -= RTTimeSpecGetMilli(&time) - lastTime;
919 lastTime = RTTimeSpecGetMilli(&time);
[1]920 }
921 }
922
[21878]923 if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
[23223]924 return setError(E_FAIL,
925 tr("Failed to wait for the operation completion (%Rrc)"),
926 vrc);
[1]927 }
928
[13580]929 LogFlowThisFuncLeave();
930
[1]931 return S_OK;
932}
933
934STDMETHODIMP Progress::Cancel()
935{
[18269]936 AutoCaller autoCaller(this);
[25149]937 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]938
[25310]939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]940
941 if (!mCancelable)
[23223]942 return setError(VBOX_E_INVALID_OBJECT_STATE,
943 tr("Operation cannot be canceled"));
[1]944
[23810]945 if (!mCanceled)
946 {
947 mCanceled = TRUE;
948 if (m_pfnCancelCallback)
949 m_pfnCancelCallback(m_pvCancelUserArg);
950
951 }
[18120]952 return S_OK;
[1]953}
954
955/**
[13580]956 * Updates the percentage value of the current operation.
[1]957 *
[13580]958 * @param aPercent New percentage value of the operation in progress
[1]959 * (in range [0, 100]).
960 */
[23223]961STDMETHODIMP Progress::SetCurrentOperationProgress(ULONG aPercent)
[1]962{
[18269]963 AutoCaller autoCaller(this);
[21878]964 AssertComRCReturnRC(autoCaller.rc());
[13580]965
[25310]966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]967
[18269]968 AssertReturn(aPercent <= 100, E_INVALIDARG);
[1]969
[24969]970 checkForAutomaticTimeout();
[18120]971 if (mCancelable && mCanceled)
972 {
973 Assert(!mCompleted);
974 return E_FAIL;
975 }
[24969]976 AssertReturn(!mCompleted && !mCanceled, E_FAIL);
[18120]977
[18269]978 m_ulOperationPercent = aPercent;
[18120]979
[1]980 return S_OK;
981}
982
983/**
[13580]984 * Signals that the current operation is successfully completed and advances to
985 * the next operation. The operation percentage is reset to 0.
[1]986 *
[13580]987 * @param aOperationDescription Description of the next operation.
[1]988 *
[13580]989 * @note The current operation must not be the last one.
[1]990 */
[23223]991STDMETHODIMP Progress::SetNextOperation(IN_BSTR bstrNextOperationDescription, ULONG ulNextOperationsWeight)
[1]992{
[18269]993 AssertReturn(bstrNextOperationDescription, E_INVALIDARG);
[1]994
[18269]995 AutoCaller autoCaller(this);
[21878]996 AssertComRCReturnRC(autoCaller.rc());
[13580]997
[25310]998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]999
[21878]1000 AssertReturn(!mCompleted && !mCanceled, E_FAIL);
1001 AssertReturn(m_ulCurrentOperation + 1 < m_cOperations, E_FAIL);
[1]1002
[18269]1003 ++m_ulCurrentOperation;
1004 m_ulOperationsCompletedWeight += m_ulCurrentOperationWeight;
[1]1005
[18269]1006 m_bstrOperationDescription = bstrNextOperationDescription;
1007 m_ulCurrentOperationWeight = ulNextOperationsWeight;
1008 m_ulOperationPercent = 0;
1009
[18306]1010 Log(("Progress::setNextOperation(%ls): ulNextOperationsWeight = %d; m_ulCurrentOperation is now %d, m_ulOperationsCompletedWeight is now %d\n",
[18269]1011 m_bstrOperationDescription.raw(), ulNextOperationsWeight, m_ulCurrentOperation, m_ulOperationsCompletedWeight));
1012
[13580]1013 /* wake up all waiting threads */
[1]1014 if (mWaitersCount > 0)
[23223]1015 RTSemEventMultiSignal(mCompletedSem);
[1]1016
1017 return S_OK;
1018}
1019
[23223]1020// public methods only for internal purposes
1021/////////////////////////////////////////////////////////////////////////////
1022
[1]1023/**
[23223]1024 * Sets the internal result code and attempts to retrieve additional error
1025 * info from the current thread. Gets called from Progress::notifyComplete(),
1026 * but can be called again to override a previous result set with
1027 * notifyComplete().
[1]1028 *
[23223]1029 * @param aResultCode
[1]1030 */
[23223]1031HRESULT Progress::setResultCode(HRESULT aResultCode)
[1]1032{
[18269]1033 AutoCaller autoCaller(this);
[21878]1034 AssertComRCReturnRC(autoCaller.rc());
[13580]1035
[25310]1036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]1037
1038 mResultCode = aResultCode;
1039
1040 HRESULT rc = S_OK;
1041
[23223]1042 if (FAILED(aResultCode))
[1]1043 {
1044 /* try to import error info from the current thread */
1045
[3191]1046#if !defined (VBOX_WITH_XPCOM)
[1]1047
[21878]1048 ComPtr<IErrorInfo> err;
[23223]1049 rc = ::GetErrorInfo(0, err.asOutParam());
[1]1050 if (rc == S_OK && err)
1051 {
[21878]1052 rc = err.queryInterfaceTo(mErrorInfo.asOutParam());
1053 if (SUCCEEDED(rc) && !mErrorInfo)
[1]1054 rc = E_FAIL;
1055 }
1056
[13580]1057#else /* !defined (VBOX_WITH_XPCOM) */
[1]1058
[23223]1059 nsCOMPtr<nsIExceptionService> es;
1060 es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
[21878]1061 if (NS_SUCCEEDED(rc))
[1]1062 {
1063 nsCOMPtr <nsIExceptionManager> em;
[23223]1064 rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
[21878]1065 if (NS_SUCCEEDED(rc))
[1]1066 {
[21878]1067 ComPtr<nsIException> ex;
[23223]1068 rc = em->GetCurrentException(ex.asOutParam());
[21878]1069 if (NS_SUCCEEDED(rc) && ex)
[1]1070 {
[21878]1071 rc = ex.queryInterfaceTo(mErrorInfo.asOutParam());
1072 if (NS_SUCCEEDED(rc) && !mErrorInfo)
[1]1073 rc = E_FAIL;
1074 }
1075 }
1076 }
[13580]1077#endif /* !defined (VBOX_WITH_XPCOM) */
[1]1078
1079 AssertMsg (rc == S_OK, ("Couldn't get error info (rc=%08X) while trying "
1080 "to set a failed result (%08X)!\n", rc, aResultCode));
1081 }
[23223]1082
1083 return rc;
1084}
1085
1086/**
1087 * Marks the whole task as complete and sets the result code.
1088 *
1089 * If the result code indicates a failure (|FAILED (@a aResultCode)|) then this
1090 * method will import the error info from the current thread and assign it to
1091 * the errorInfo attribute (it will return an error if no info is available in
1092 * such case).
1093 *
1094 * If the result code indicates a success (|SUCCEEDED(@a aResultCode)|) then
1095 * the current operation is set to the last.
1096 *
1097 * Note that this method may be called only once for the given Progress object.
1098 * Subsequent calls will assert.
1099 *
1100 * @param aResultCode Operation result code.
1101 */
1102HRESULT Progress::notifyComplete(HRESULT aResultCode)
1103{
1104 AutoCaller autoCaller(this);
1105 AssertComRCReturnRC(autoCaller.rc());
1106
[25310]1107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[23223]1108
1109 AssertReturn(mCompleted == FALSE, E_FAIL);
1110
1111 if (mCanceled && SUCCEEDED(aResultCode))
1112 aResultCode = E_FAIL;
1113
1114 HRESULT rc = setResultCode(aResultCode);
1115
1116 mCompleted = TRUE;
1117
1118 if (!FAILED(aResultCode))
[1]1119 {
[18269]1120 m_ulCurrentOperation = m_cOperations - 1; /* last operation */
1121 m_ulOperationPercent = 100;
[1]1122 }
1123
1124#if !defined VBOX_COM_INPROC
1125 /* remove from the global collection of pending progress operations */
1126 if (mParent)
1127 mParent->removeProgress (mId);
1128#endif
1129
1130 /* wake up all waiting threads */
1131 if (mWaitersCount > 0)
1132 RTSemEventMultiSignal (mCompletedSem);
1133
1134 return rc;
1135}
1136
1137/**
[13580]1138 * Marks the operation as complete and attaches full error info.
[1]1139 *
[13580]1140 * See com::SupportErrorInfoImpl::setError(HRESULT, const GUID &, const wchar_t
1141 * *, const char *, ...) for more info.
1142 *
1143 * @param aResultCode Operation result (error) code, must not be S_OK.
[18643]1144 * @param aIID IID of the interface that defines the error.
[13580]1145 * @param aComponent Name of the component that generates the error.
1146 * @param aText Error message (must not be null), an RTStrPrintf-like
1147 * format string in UTF-8 encoding.
1148 * @param ... List of arguments for the format string.
[1]1149 */
[23223]1150HRESULT Progress::notifyComplete(HRESULT aResultCode,
1151 const GUID &aIID,
1152 const Bstr &aComponent,
1153 const char *aText,
1154 ...)
[1]1155{
[357]1156 va_list args;
[23223]1157 va_start(args, aText);
1158 Utf8Str text = Utf8StrFmtVA(aText, args);
[357]1159 va_end (args);
[7992]1160
[18269]1161 AutoCaller autoCaller(this);
[21878]1162 AssertComRCReturnRC(autoCaller.rc());
[13580]1163
[25310]1164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]1165
[21878]1166 AssertReturn(mCompleted == FALSE, E_FAIL);
[18120]1167
1168 if (mCanceled && SUCCEEDED(aResultCode))
1169 aResultCode = E_FAIL;
1170
[1]1171 mCompleted = TRUE;
1172 mResultCode = aResultCode;
1173
[21878]1174 AssertReturn(FAILED (aResultCode), E_FAIL);
[1]1175
[21878]1176 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
[1]1177 HRESULT rc = errorInfo.createObject();
1178 AssertComRC (rc);
[21878]1179 if (SUCCEEDED(rc))
[1]1180 {
[23223]1181 errorInfo->init(aResultCode, aIID, aComponent, Bstr(text));
[21878]1182 errorInfo.queryInterfaceTo(mErrorInfo.asOutParam());
[1]1183 }
1184
1185#if !defined VBOX_COM_INPROC
1186 /* remove from the global collection of pending progress operations */
1187 if (mParent)
1188 mParent->removeProgress (mId);
1189#endif
1190
1191 /* wake up all waiting threads */
1192 if (mWaitersCount > 0)
[23223]1193 RTSemEventMultiSignal(mCompletedSem);
[1]1194
1195 return rc;
1196}
1197
[23827]1198/**
1199 * Notify the progress object that we're almost at the point of no return.
1200 *
1201 * This atomically checks for and disables cancelation. Calls to
1202 * IProgress::Cancel() made after a successfull call to this method will fail
1203 * and the user can be told. While this isn't entirely clean behavior, it
1204 * prevents issues with an irreversible actually operation succeeding while the
1205 * user belive it was rolled back.
1206 *
1207 * @returns Success indicator.
1208 * @retval true on success.
1209 * @retval false if the progress object has already been canceled or is in an
1210 * invalid state
1211 */
1212bool Progress::notifyPointOfNoReturn(void)
1213{
1214 AutoCaller autoCaller(this);
1215 AssertComRCReturn(autoCaller.rc(), false);
1216
[25310]1217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[23827]1218
1219 if (mCanceled)
1220 return false;
1221
1222 mCancelable = FALSE;
1223 return true;
1224}
1225
[1]1226////////////////////////////////////////////////////////////////////////////////
1227// CombinedProgress class
1228////////////////////////////////////////////////////////////////////////////////
1229
1230HRESULT CombinedProgress::FinalConstruct()
1231{
1232 HRESULT rc = ProgressBase::FinalConstruct();
[25149]1233 if (FAILED(rc)) return rc;
[1]1234
1235 mProgress = 0;
1236 mCompletedOperations = 0;
1237
1238 return S_OK;
1239}
1240
1241void CombinedProgress::FinalRelease()
1242{
1243 uninit();
1244}
1245
1246// public initializer/uninitializer for internal purposes only
1247////////////////////////////////////////////////////////////////////////////////
1248
1249/**
[13580]1250 * Initializes this object based on individual combined progresses.
1251 * Must be called only from #init()!
[1]1252 *
[13580]1253 * @param aAutoInitSpan AutoInitSpan object instantiated by a subclass.
1254 * @param aParent See ProgressBase::init().
1255 * @param aInitiator See ProgressBase::init().
1256 * @param aDescription See ProgressBase::init().
1257 * @param aId See ProgressBase::init().
[1]1258 */
[13580]1259HRESULT CombinedProgress::protectedInit (AutoInitSpan &aAutoInitSpan,
[1]1260#if !defined (VBOX_COM_INPROC)
1261 VirtualBox *aParent,
1262#endif
1263 IUnknown *aInitiator,
[15051]1264 CBSTR aDescription, OUT_GUID aId)
[1]1265{
[21878]1266 LogFlowThisFunc(("aDescription={%ls} mProgresses.size()=%d\n",
[13580]1267 aDescription, mProgresses.size()));
[1]1268
1269 HRESULT rc = S_OK;
1270
[13580]1271 rc = ProgressBase::protectedInit (aAutoInitSpan,
[1]1272#if !defined (VBOX_COM_INPROC)
[13580]1273 aParent,
[1]1274#endif
[13580]1275 aInitiator, aDescription, aId);
[25149]1276 if (FAILED(rc)) return rc;
[1]1277
[13580]1278 mProgress = 0; /* the first object */
1279 mCompletedOperations = 0;
[1]1280
[13580]1281 mCompleted = FALSE;
1282 mCancelable = TRUE; /* until any progress returns FALSE */
1283 mCanceled = FALSE;
[1]1284
[18269]1285 m_cOperations = 0; /* will be calculated later */
[1]1286
[18269]1287 m_ulCurrentOperation = 0;
[13580]1288 rc = mProgresses [0]->COMGETTER(OperationDescription) (
[18269]1289 m_bstrOperationDescription.asOutParam());
[25149]1290 if (FAILED(rc)) return rc;
[1]1291
[13580]1292 for (size_t i = 0; i < mProgresses.size(); i ++)
1293 {
1294 if (mCancelable)
[1]1295 {
[13580]1296 BOOL cancelable = FALSE;
1297 rc = mProgresses [i]->COMGETTER(Cancelable) (&cancelable);
[25149]1298 if (FAILED(rc)) return rc;
[1]1299
[13580]1300 if (!cancelable)
1301 mCancelable = FALSE;
[1]1302 }
1303
[13580]1304 {
1305 ULONG opCount = 0;
1306 rc = mProgresses [i]->COMGETTER(OperationCount) (&opCount);
[25149]1307 if (FAILED(rc)) return rc;
[13580]1308
[18269]1309 m_cOperations += opCount;
[13580]1310 }
[1]1311 }
1312
[13580]1313 rc = checkProgress();
[25149]1314 if (FAILED(rc)) return rc;
[1]1315
1316 return rc;
1317}
1318
1319/**
[13580]1320 * Initializes the combined progress object given two normal progress
1321 * objects.
1322 *
1323 * @param aParent See ProgressBase::init().
1324 * @param aInitiator See ProgressBase::init().
1325 * @param aDescription See ProgressBase::init().
1326 * @param aProgress1 First normal progress object.
1327 * @param aProgress2 Second normal progress object.
1328 * @param aId See ProgressBase::init().
[1]1329 */
[13580]1330HRESULT CombinedProgress::init (
1331#if !defined (VBOX_COM_INPROC)
1332 VirtualBox *aParent,
1333#endif
1334 IUnknown *aInitiator,
[15051]1335 CBSTR aDescription,
[13580]1336 IProgress *aProgress1, IProgress *aProgress2,
[15051]1337 OUT_GUID aId /* = NULL */)
[1]1338{
[13580]1339 /* Enclose the state transition NotReady->InInit->Ready */
[21878]1340 AutoInitSpan autoInitSpan(this);
1341 AssertReturn(autoInitSpan.isOk(), E_FAIL);
[1]1342
[13580]1343 mProgresses.resize (2);
1344 mProgresses [0] = aProgress1;
1345 mProgresses [1] = aProgress2;
[1]1346
[13580]1347 HRESULT rc = protectedInit (autoInitSpan,
1348#if !defined (VBOX_COM_INPROC)
1349 aParent,
1350#endif
1351 aInitiator, aDescription, aId);
[1]1352
[13580]1353 /* Confirm a successful initialization when it's the case */
[21878]1354 if (SUCCEEDED(rc))
[13580]1355 autoInitSpan.setSucceeded();
1356
1357 return rc;
1358}
1359
1360/**
1361 * Uninitializes the instance and sets the ready flag to FALSE.
1362 *
1363 * Called either from FinalRelease() or by the parent when it gets destroyed.
1364 */
1365void CombinedProgress::uninit()
1366{
[21878]1367 LogFlowThisFunc(("\n"));
[13580]1368
1369 /* Enclose the state transition Ready->InUninit->NotReady */
[21878]1370 AutoUninitSpan autoUninitSpan(this);
[13580]1371 if (autoUninitSpan.uninitDone())
[1]1372 return;
1373
1374 mProgress = 0;
1375 mProgresses.clear();
1376
[13580]1377 ProgressBase::protectedUninit (autoUninitSpan);
[1]1378}
1379
1380// IProgress properties
1381////////////////////////////////////////////////////////////////////////////////
1382
[18269]1383STDMETHODIMP CombinedProgress::COMGETTER(Percent)(ULONG *aPercent)
[1]1384{
[14972]1385 CheckComArgOutPointerValid(aPercent);
[1]1386
[18269]1387 AutoCaller autoCaller(this);
[25149]1388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]1389
1390 /* checkProgress needs a write lock */
[25310]1391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]1392
[21878]1393 if (mCompleted && SUCCEEDED(mResultCode))
[1]1394 *aPercent = 100;
1395 else
1396 {
1397 HRESULT rc = checkProgress();
[25149]1398 if (FAILED(rc)) return rc;
[1]1399
[13580]1400 /* global percent =
[18269]1401 * (100 / m_cOperations) * mOperation +
1402 * ((100 / m_cOperations) / 100) * m_ulOperationPercent */
1403 *aPercent = (100 * m_ulCurrentOperation + m_ulOperationPercent) / m_cOperations;
[1]1404 }
1405
1406 return S_OK;
1407}
1408
1409STDMETHODIMP CombinedProgress::COMGETTER(Completed) (BOOL *aCompleted)
1410{
[14972]1411 CheckComArgOutPointerValid(aCompleted);
[1]1412
[18269]1413 AutoCaller autoCaller(this);
[25149]1414 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]1415
1416 /* checkProgress needs a write lock */
[25310]1417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]1418
1419 HRESULT rc = checkProgress();
[25149]1420 if (FAILED(rc)) return rc;
[1]1421
1422 return ProgressBase::COMGETTER(Completed) (aCompleted);
1423}
1424
1425STDMETHODIMP CombinedProgress::COMGETTER(Canceled) (BOOL *aCanceled)
1426{
[14972]1427 CheckComArgOutPointerValid(aCanceled);
[1]1428
[18269]1429 AutoCaller autoCaller(this);
[25149]1430 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]1431
1432 /* checkProgress needs a write lock */
[25310]1433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]1434
1435 HRESULT rc = checkProgress();
[25149]1436 if (FAILED(rc)) return rc;
[1]1437
1438 return ProgressBase::COMGETTER(Canceled) (aCanceled);
1439}
1440
[20220]1441STDMETHODIMP CombinedProgress::COMGETTER(ResultCode) (LONG *aResultCode)
[1]1442{
[14972]1443 CheckComArgOutPointerValid(aResultCode);
[1]1444
[18269]1445 AutoCaller autoCaller(this);
[25149]1446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]1447
1448 /* checkProgress needs a write lock */
[25310]1449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]1450
1451 HRESULT rc = checkProgress();
[25149]1452 if (FAILED(rc)) return rc;
[1]1453
1454 return ProgressBase::COMGETTER(ResultCode) (aResultCode);
1455}
1456
1457STDMETHODIMP CombinedProgress::COMGETTER(ErrorInfo) (IVirtualBoxErrorInfo **aErrorInfo)
1458{
[14972]1459 CheckComArgOutPointerValid(aErrorInfo);
[1]1460
[18269]1461 AutoCaller autoCaller(this);
[25149]1462 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]1463
1464 /* checkProgress needs a write lock */
[25310]1465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]1466
1467 HRESULT rc = checkProgress();
[25149]1468 if (FAILED(rc)) return rc;
[1]1469
1470 return ProgressBase::COMGETTER(ErrorInfo) (aErrorInfo);
1471}
1472
1473STDMETHODIMP CombinedProgress::COMGETTER(Operation) (ULONG *aOperation)
1474{
[14972]1475 CheckComArgOutPointerValid(aOperation);
[1]1476
[18269]1477 AutoCaller autoCaller(this);
[25149]1478 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]1479
1480 /* checkProgress needs a write lock */
[25310]1481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]1482
1483 HRESULT rc = checkProgress();
[25149]1484 if (FAILED(rc)) return rc;
[1]1485
1486 return ProgressBase::COMGETTER(Operation) (aOperation);
1487}
1488
1489STDMETHODIMP CombinedProgress::COMGETTER(OperationDescription) (BSTR *aOperationDescription)
1490{
[14972]1491 CheckComArgOutPointerValid(aOperationDescription);
[1]1492
[18269]1493 AutoCaller autoCaller(this);
[25149]1494 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]1495
1496 /* checkProgress needs a write lock */
[25310]1497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]1498
1499 HRESULT rc = checkProgress();
[25149]1500 if (FAILED(rc)) return rc;
[1]1501
1502 return ProgressBase::COMGETTER(OperationDescription) (aOperationDescription);
1503}
1504
[18269]1505STDMETHODIMP CombinedProgress::COMGETTER(OperationPercent)(ULONG *aOperationPercent)
[1]1506{
[14972]1507 CheckComArgOutPointerValid(aOperationPercent);
[1]1508
[18269]1509 AutoCaller autoCaller(this);
[25149]1510 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]1511
1512 /* checkProgress needs a write lock */
[25310]1513 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]1514
1515 HRESULT rc = checkProgress();
[25149]1516 if (FAILED(rc)) return rc;
[1]1517
1518 return ProgressBase::COMGETTER(OperationPercent) (aOperationPercent);
1519}
1520
[24961]1521STDMETHODIMP CombinedProgress::COMSETTER(Timeout)(ULONG aTimeout)
1522{
1523 NOREF(aTimeout);
1524 AssertFailed();
1525 return E_NOTIMPL;
1526}
1527
1528STDMETHODIMP CombinedProgress::COMGETTER(Timeout)(ULONG *aTimeout)
1529{
1530 CheckComArgOutPointerValid(aTimeout);
1531
1532 AssertFailed();
1533 return E_NOTIMPL;
1534}
1535
[1]1536// IProgress methods
1537/////////////////////////////////////////////////////////////////////////////
1538
1539/**
[18643]1540 * @note XPCOM: when this method is called not on the main XPCOM thread, it
[13580]1541 * simply blocks the thread until mCompletedSem is signalled. If the
1542 * thread has its own event queue (hmm, what for?) that it must run, then
[18643]1543 * calling this method will definitely freeze event processing.
[1]1544 */
1545STDMETHODIMP CombinedProgress::WaitForCompletion (LONG aTimeout)
1546{
[13580]1547 LogFlowThisFuncEnter();
[21878]1548 LogFlowThisFunc(("aTtimeout=%d\n", aTimeout));
[1]1549
[18269]1550 AutoCaller autoCaller(this);
[25149]1551 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]1552
[25310]1553 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]1554
[13580]1555 /* if we're already completed, take a shortcut */
[1]1556 if (!mCompleted)
1557 {
1558 RTTIMESPEC time;
[25149]1559 RTTimeNow(&time);
[1]1560
1561 HRESULT rc = S_OK;
1562 bool forever = aTimeout < 0;
1563 int64_t timeLeft = aTimeout;
[25149]1564 int64_t lastTime = RTTimeSpecGetMilli(&time);
[1]1565
1566 while (!mCompleted && (forever || timeLeft > 0))
1567 {
[13580]1568 alock.leave();
[25149]1569 rc = mProgresses.back()->WaitForCompletion(forever ? -1 : (LONG) timeLeft);
[13580]1570 alock.enter();
[1]1571
[21878]1572 if (SUCCEEDED(rc))
[1]1573 rc = checkProgress();
1574
[25149]1575 if (FAILED(rc)) break;
[1]1576
1577 if (!forever)
1578 {
[25149]1579 RTTimeNow(&time);
1580 timeLeft -= RTTimeSpecGetMilli(&time) - lastTime;
1581 lastTime = RTTimeSpecGetMilli(&time);
[1]1582 }
1583 }
1584
[25149]1585 if (FAILED(rc)) return rc;
[1]1586 }
1587
[13580]1588 LogFlowThisFuncLeave();
1589
[1]1590 return S_OK;
1591}
1592
1593/**
[18643]1594 * @note XPCOM: when this method is called not on the main XPCOM thread, it
[13580]1595 * simply blocks the thread until mCompletedSem is signalled. If the
1596 * thread has its own event queue (hmm, what for?) that it must run, then
[18643]1597 * calling this method will definitely freeze event processing.
[1]1598 */
1599STDMETHODIMP CombinedProgress::WaitForOperationCompletion (ULONG aOperation, LONG aTimeout)
1600{
[13580]1601 LogFlowThisFuncEnter();
[21878]1602 LogFlowThisFunc(("aOperation=%d, aTimeout=%d\n", aOperation, aTimeout));
[1]1603
[18269]1604 AutoCaller autoCaller(this);
[25149]1605 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]1606
[25310]1607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]1608
[18269]1609 if (aOperation >= m_cOperations)
[1]1610 return setError (E_FAIL,
[18269]1611 tr ("Operation number must be in range [0, %d]"), m_ulCurrentOperation - 1);
[1]1612
[13580]1613 /* if we're already completed or if the given operation is already done,
1614 * then take a shortcut */
[18269]1615 if (!mCompleted && aOperation >= m_ulCurrentOperation)
[1]1616 {
1617 HRESULT rc = S_OK;
1618
[13580]1619 /* find the right progress object to wait for */
[1]1620 size_t progress = mProgress;
1621 ULONG operation = 0, completedOps = mCompletedOperations;
1622 do
1623 {
1624 ULONG opCount = 0;
1625 rc = mProgresses [progress]->COMGETTER(OperationCount) (&opCount);
1626 if (FAILED (rc))
1627 return rc;
1628
1629 if (completedOps + opCount > aOperation)
1630 {
[13580]1631 /* found the right progress object */
[1]1632 operation = aOperation - completedOps;
1633 break;
1634 }
1635
1636 completedOps += opCount;
1637 progress ++;
1638 ComAssertRet (progress < mProgresses.size(), E_FAIL);
1639 }
1640 while (1);
1641
[21878]1642 LogFlowThisFunc(("will wait for mProgresses [%d] (%d)\n",
[13580]1643 progress, operation));
[1]1644
1645 RTTIMESPEC time;
1646 RTTimeNow (&time);
1647
1648 bool forever = aTimeout < 0;
1649 int64_t timeLeft = aTimeout;
1650 int64_t lastTime = RTTimeSpecGetMilli (&time);
1651
[18269]1652 while (!mCompleted && aOperation >= m_ulCurrentOperation &&
[1]1653 (forever || timeLeft > 0))
1654 {
[13580]1655 alock.leave();
1656 /* wait for the appropriate progress operation completion */
[25149]1657 rc = mProgresses[progress]-> WaitForOperationCompletion(operation,
1658 forever ? -1 : (LONG) timeLeft);
[13580]1659 alock.enter();
[1]1660
[21878]1661 if (SUCCEEDED(rc))
[1]1662 rc = checkProgress();
1663
[25149]1664 if (FAILED(rc)) break;
[1]1665
1666 if (!forever)
1667 {
[25149]1668 RTTimeNow(&time);
1669 timeLeft -= RTTimeSpecGetMilli(&time) - lastTime;
1670 lastTime = RTTimeSpecGetMilli(&time);
[1]1671 }
1672 }
1673
[25149]1674 if (FAILED(rc)) return rc;
[1]1675 }
1676
[13580]1677 LogFlowThisFuncLeave();
1678
[1]1679 return S_OK;
1680}
1681
1682STDMETHODIMP CombinedProgress::Cancel()
1683{
[18269]1684 AutoCaller autoCaller(this);
[25149]1685 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[13580]1686
[25310]1687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]1688
1689 if (!mCancelable)
[23827]1690 return setError (E_FAIL, tr ("Operation cannot be canceled"));
[1]1691
[23810]1692 if (!mCanceled)
1693 {
1694 mCanceled = TRUE;
1695/** @todo Teleportation: Shouldn't this be propagated to mProgresses? If
1696 * powerUp creates passes a combined progress object to the client, I
1697 * won't get called back since I'm only getting the powerupProgress ...
1698 * Or what? */
1699 if (m_pfnCancelCallback)
1700 m_pfnCancelCallback(m_pvCancelUserArg);
1701
1702 }
[18120]1703 return S_OK;
[1]1704}
1705
1706// private methods
1707////////////////////////////////////////////////////////////////////////////////
1708
1709/**
[13580]1710 * Fetches the properties of the current progress object and, if it is
[18643]1711 * successfully completed, advances to the next uncompleted or unsuccessfully
[13580]1712 * completed object in the vector of combined progress objects.
[1]1713 *
[13580]1714 * @note Must be called from under this object's write lock!
[1]1715 */
1716HRESULT CombinedProgress::checkProgress()
1717{
[13580]1718 /* do nothing if we're already marked ourselves as completed */
[1]1719 if (mCompleted)
1720 return S_OK;
1721
[21878]1722 AssertReturn(mProgress < mProgresses.size(), E_FAIL);
[1]1723
[24989]1724 ComPtr<IProgress> progress = mProgresses[mProgress];
[1]1725 ComAssertRet (!progress.isNull(), E_FAIL);
1726
1727 HRESULT rc = S_OK;
[24989]1728 BOOL fCompleted = FALSE;
[1]1729
1730 do
1731 {
[24989]1732 rc = progress->COMGETTER(Completed)(&fCompleted);
[1]1733 if (FAILED (rc))
1734 return rc;
1735
[24989]1736 if (fCompleted)
[1]1737 {
[24989]1738 rc = progress->COMGETTER(Canceled)(&mCanceled);
[1]1739 if (FAILED (rc))
1740 return rc;
1741
[20220]1742 LONG iRc;
[24989]1743 rc = progress->COMGETTER(ResultCode)(&iRc);
[1]1744 if (FAILED (rc))
1745 return rc;
[20220]1746 mResultCode = iRc;
[1]1747
1748 if (FAILED (mResultCode))
1749 {
1750 rc = progress->COMGETTER(ErrorInfo) (mErrorInfo.asOutParam());
1751 if (FAILED (rc))
1752 return rc;
1753 }
1754
1755 if (FAILED (mResultCode) || mCanceled)
1756 {
1757 mCompleted = TRUE;
1758 }
1759 else
1760 {
1761 ULONG opCount = 0;
1762 rc = progress->COMGETTER(OperationCount) (&opCount);
1763 if (FAILED (rc))
1764 return rc;
1765
1766 mCompletedOperations += opCount;
1767 mProgress ++;
1768
1769 if (mProgress < mProgresses.size())
1770 progress = mProgresses [mProgress];
1771 else
1772 mCompleted = TRUE;
1773 }
1774 }
1775 }
[24989]1776 while (fCompleted && !mCompleted);
[1]1777
[18269]1778 rc = progress->COMGETTER(OperationPercent) (&m_ulOperationPercent);
[21878]1779 if (SUCCEEDED(rc))
[1]1780 {
1781 ULONG operation = 0;
1782 rc = progress->COMGETTER(Operation) (&operation);
[21878]1783 if (SUCCEEDED(rc) && mCompletedOperations + operation > m_ulCurrentOperation)
[1]1784 {
[18269]1785 m_ulCurrentOperation = mCompletedOperations + operation;
[1]1786 rc = progress->COMGETTER(OperationDescription) (
[18269]1787 m_bstrOperationDescription.asOutParam());
[1]1788 }
1789 }
1790
1791 return rc;
1792}
[14772]1793/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use