VirtualBox

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

Last change on this file since 24323 was 23827, checked in by vboxsync, 15 years ago

Main: Cancellation fixes (almost perfect).

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette