VirtualBox

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

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

Main: COM header cleanup (remove obscure and unused templates)

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