VirtualBox

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

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

Backed out 55600; unable to create new VMs

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

© 2023 Oracle
ContactPrivacy policyTerms of Use