VirtualBox

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

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

OVF: fix progress values for disks on import

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