VirtualBox

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

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

ProcessImpl.cpp: Added todo about replacing walltime (RTTimeNow) with monotonic time (RTTimeMilliTS) in the timeout handling.

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