VirtualBox

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

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

Main: Bstr makeover (second attempt) -- make Bstr(NULL) and Bstr() behave the same; resulting cleanup; make some more internal methods use Utf8Str instead of Bstr; fix a lot of CheckComArgNotNull?() usage

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