VirtualBox

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

Last change on this file since 18394 was 18394, checked in by vboxsync, 16 years ago

Main: do not allow IProgress to report 100% until we're really done

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