VirtualBox

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

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

Main: coding style: have Main obey the standard VirtualBox coding style rules (no functional changes)

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