VirtualBox

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

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

win build fix

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