VirtualBox

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

Last change on this file since 31595 was 31333, checked in by vboxsync, 14 years ago

Main: rework new implementation of Machine::Unregister() and Machine::Delete() to be more flexible and still easy to use

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