VirtualBox

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

Last change on this file since 6382 was 6375, checked in by vboxsync, 17 years ago

Main: Fixed VM error callback used during powerup so that it doesn't complete the progress object but simply saves the received error message; Fixed Progress::notifyComplete() to not accept the second and subsequent calls (all for #2238).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.0 KB
Line 
1/** @file
2 *
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#if defined (VBOX_WITH_XPCOM)
19#include <nsIServiceManager.h>
20#include <nsIExceptionService.h>
21#include <nsCOMPtr.h>
22#endif // defined (VBOX_WITH_XPCOM)
23
24#include "ProgressImpl.h"
25#include "VirtualBoxImpl.h"
26#include "VirtualBoxErrorInfoImpl.h"
27
28#include "Logging.h"
29
30#include <iprt/time.h>
31#include <iprt/semaphore.h>
32
33#include <VBox/err.h>
34
35////////////////////////////////////////////////////////////////////////////////
36// ProgressBase class
37////////////////////////////////////////////////////////////////////////////////
38
39// constructor / destructor
40////////////////////////////////////////////////////////////////////////////////
41
42/** Subclasses must call this method from their FinalConstruct() implementations */
43HRESULT ProgressBase::FinalConstruct()
44{
45 mCancelable = FALSE;
46 mCompleted = FALSE;
47 mCanceled = FALSE;
48 mResultCode = S_OK;
49 mOperationCount = 0;
50 mOperation = 0;
51 mOperationPercent = 0;
52
53 return S_OK;
54}
55
56// public initializer/uninitializer for internal purposes only
57////////////////////////////////////////////////////////////////////////////////
58
59/**
60 * Initializes the progress base object.
61 *
62 * Subclasses should call this or any other #init() method from their
63 * init() implementations.
64 *
65 * @param aParent
66 * Parent object (only for server-side Progress objects)
67 * @param aInitiator
68 * Initiator of the task (for server-side objects
69 * can be NULL which means initiator = parent, otherwise
70 * must not be NULL)
71 * @param aDescription
72 * Task description
73 * @param aID
74 * Address of result GUID structure (optional)
75 *
76 * @note
77 * This method doesn't do |isReady()| check and doesn't call
78 * |setReady (true)| on success!
79 * @note
80 * This method must be called from under the object's lock!
81 */
82HRESULT ProgressBase::protectedInit (
83#if !defined (VBOX_COM_INPROC)
84 VirtualBox *aParent,
85#endif
86 IUnknown *aInitiator,
87 const BSTR aDescription, GUIDPARAMOUT aId /* = NULL */)
88{
89#if !defined (VBOX_COM_INPROC)
90 ComAssertRet (aParent, E_POINTER);
91#else
92 ComAssertRet (aInitiator, E_POINTER);
93#endif
94
95 ComAssertRet (aDescription, E_INVALIDARG);
96
97#if !defined (VBOX_COM_INPROC)
98 mParent = aParent;
99#endif
100
101#if !defined (VBOX_COM_INPROC)
102 // assign (and therefore addref) initiator only if it is not VirtualBox
103 // (to avoid cycling); otherwise mInitiator will remain null which means
104 // that it is the same as the parent
105 if (aInitiator && !mParent.equalsTo (aInitiator))
106 mInitiator = aInitiator;
107#else
108 mInitiator = aInitiator;
109#endif
110
111 mDescription = aDescription;
112
113 mId.create();
114 if (aId)
115 mId.cloneTo (aId);
116
117#if !defined (VBOX_COM_INPROC)
118 // add to the global colleciton of progess operations
119 mParent->addProgress (this);
120 // cause #uninit() to be called automatically upon VirtualBox uninit
121 mParent->addDependentChild (this);
122#endif
123
124 return S_OK;
125}
126
127/**
128 * Initializes the progress base object.
129 * This is a special initializator for progress objects that are combined
130 * within a CombinedProgress instance, so it doesn't require the parent,
131 * initiator, description and doesn't create an ID. Note that calling respective
132 * getter methods on an object initialized with this constructor will hit an
133 * assertion.
134 *
135 * Subclasses should call this or any other #init() method from their
136 * init() implementations.
137 *
138 * @note
139 * This method doesn't do |isReady()| check and doesn't call
140 * |setReady (true)| on success!
141 * @note
142 * This method must be called from under the object's lock!
143 */
144HRESULT ProgressBase::protectedInit()
145{
146 return S_OK;
147}
148
149/**
150 * Uninitializes the instance.
151 * Subclasses should call this from their uninit() implementations.
152 * The readiness flag must be true on input and will be set to false
153 * on output.
154 *
155 * @param alock this object's autolock (with lock level = 1!)
156 *
157 * @note
158 * Using mParent member after this method returns is forbidden.
159 */
160void ProgressBase::protectedUninit (AutoLock &alock)
161{
162 LogFlowMember (("ProgressBase::protectedUninit()\n"));
163
164 Assert (alock.belongsTo (this) && alock.level() == 1);
165 Assert (isReady());
166
167 /*
168 * release initiator
169 * (effective only if mInitiator has been assigned in init())
170 */
171 mInitiator.setNull();
172
173 setReady (false);
174
175#if !defined (VBOX_COM_INPROC)
176 if (mParent)
177 {
178 alock.leave();
179 mParent->removeDependentChild (this);
180 alock.enter();
181 }
182
183 mParent.setNull();
184#endif
185}
186
187// IProgress properties
188/////////////////////////////////////////////////////////////////////////////
189
190STDMETHODIMP ProgressBase::COMGETTER(Id) (GUIDPARAMOUT aId)
191{
192 if (!aId)
193 return E_POINTER;
194
195 AutoLock lock (this);
196 CHECK_READY();
197
198 ComAssertRet (!mId.isEmpty(), E_FAIL);
199
200 mId.cloneTo (aId);
201 return S_OK;
202}
203
204STDMETHODIMP ProgressBase::COMGETTER(Description) (BSTR *aDescription)
205{
206 if (!aDescription)
207 return E_POINTER;
208
209 AutoLock lock (this);
210 CHECK_READY();
211
212 ComAssertRet (!mDescription.isNull(), E_FAIL);
213
214 mDescription.cloneTo (aDescription);
215 return S_OK;
216}
217
218STDMETHODIMP ProgressBase::COMGETTER(Initiator) (IUnknown **aInitiator)
219{
220 if (!aInitiator)
221 return E_POINTER;
222
223 AutoLock lock(this);
224 CHECK_READY();
225
226#if !defined (VBOX_COM_INPROC)
227 ComAssertRet (!mInitiator.isNull() || !mParent.isNull(), E_FAIL);
228
229 if (mInitiator)
230 mInitiator.queryInterfaceTo (aInitiator);
231 else
232 mParent.queryInterfaceTo (aInitiator);
233#else
234 ComAssertRet (!mInitiator.isNull(), E_FAIL);
235
236 mInitiator.queryInterfaceTo (aInitiator);
237#endif
238
239 return S_OK;
240}
241
242STDMETHODIMP ProgressBase::COMGETTER(Cancelable) (BOOL *aCancelable)
243{
244 if (!aCancelable)
245 return E_POINTER;
246
247 AutoLock lock(this);
248 CHECK_READY();
249
250 *aCancelable = mCancelable;
251 return S_OK;
252}
253
254STDMETHODIMP ProgressBase::COMGETTER(Percent) (LONG *aPercent)
255{
256 if (!aPercent)
257 return E_POINTER;
258
259 AutoLock lock(this);
260 CHECK_READY();
261
262 if (mCompleted && SUCCEEDED (mResultCode))
263 *aPercent = 100;
264 else
265 {
266 // global percent = (100 / mOperationCount) * mOperation +
267 // ((100 / mOperationCount) / 100) * mOperationPercent
268 *aPercent = (100 * mOperation + mOperationPercent) / mOperationCount;
269 }
270
271 return S_OK;
272}
273
274STDMETHODIMP ProgressBase::COMGETTER(Completed) (BOOL *aCompleted)
275{
276 if (!aCompleted)
277 return E_POINTER;
278
279 AutoLock lock(this);
280 CHECK_READY();
281
282 *aCompleted = mCompleted;
283 return S_OK;
284}
285
286STDMETHODIMP ProgressBase::COMGETTER(Canceled) (BOOL *aCanceled)
287{
288 if (!aCanceled)
289 return E_POINTER;
290
291 AutoLock lock(this);
292 CHECK_READY();
293
294 *aCanceled = mCanceled;
295 return S_OK;
296}
297
298STDMETHODIMP ProgressBase::COMGETTER(ResultCode) (HRESULT *aResultCode)
299{
300 if (!aResultCode)
301 return E_POINTER;
302
303 AutoLock lock(this);
304 CHECK_READY();
305
306 if (!mCompleted)
307 return setError (E_FAIL,
308 tr ("Result code is not available, operation is still in progress"));
309
310 *aResultCode = mResultCode;
311 return S_OK;
312}
313
314STDMETHODIMP ProgressBase::COMGETTER(ErrorInfo) (IVirtualBoxErrorInfo **aErrorInfo)
315{
316 if (!aErrorInfo)
317 return E_POINTER;
318
319 AutoLock lock(this);
320 CHECK_READY();
321
322 if (!mCompleted)
323 return setError (E_FAIL,
324 tr ("Error info is not available, operation is still in progress"));
325
326 mErrorInfo.queryInterfaceTo (aErrorInfo);
327 return S_OK;
328}
329
330STDMETHODIMP ProgressBase::COMGETTER(OperationCount) (ULONG *aOperationCount)
331{
332 if (!aOperationCount)
333 return E_POINTER;
334
335 AutoLock lock(this);
336 CHECK_READY();
337
338 *aOperationCount = mOperationCount;
339 return S_OK;
340}
341
342STDMETHODIMP ProgressBase::COMGETTER(Operation) (ULONG *aOperation)
343{
344 if (!aOperation)
345 return E_POINTER;
346
347 AutoLock lock(this);
348 CHECK_READY();
349
350 *aOperation = mOperation;
351 return S_OK;
352}
353
354STDMETHODIMP ProgressBase::COMGETTER(OperationDescription) (BSTR *aOperationDescription)
355{
356 if (!aOperationDescription)
357 return E_POINTER;
358
359 AutoLock lock(this);
360 CHECK_READY();
361
362 mOperationDescription.cloneTo (aOperationDescription);
363 return S_OK;
364}
365
366STDMETHODIMP ProgressBase::COMGETTER(OperationPercent) (LONG *aOperationPercent)
367{
368 if (!aOperationPercent)
369 return E_POINTER;
370
371 AutoLock lock(this);
372 CHECK_READY();
373
374 if (mCompleted && SUCCEEDED (mResultCode))
375 *aOperationPercent = 100;
376 else
377 *aOperationPercent = mOperationPercent;
378 return S_OK;
379}
380
381////////////////////////////////////////////////////////////////////////////////
382// Progress class
383////////////////////////////////////////////////////////////////////////////////
384
385HRESULT Progress::FinalConstruct()
386{
387 HRESULT rc = ProgressBase::FinalConstruct();
388 if (FAILED (rc))
389 return rc;
390
391 mCompletedSem = NIL_RTSEMEVENTMULTI;
392 mWaitersCount = 0;
393
394 return S_OK;
395}
396
397void Progress::FinalRelease()
398{
399 uninit();
400}
401
402// public initializer/uninitializer for internal purposes only
403////////////////////////////////////////////////////////////////////////////////
404
405/**
406 * Initializes the progress object.
407 *
408 * @param aParent see ProgressBase::init()
409 * @param aInitiator see ProgressBase::init()
410 * @param aDescription see ProgressBase::init()
411 * @param aCancelable Flag whether the task maybe canceled
412 * @param aOperationCount Number of operations within this task (at least 1)
413 * @param aOperationDescription Description of the first operation
414 * @param aId see ProgressBase::init()
415 */
416HRESULT Progress::init (
417#if !defined (VBOX_COM_INPROC)
418 VirtualBox *aParent,
419#endif
420 IUnknown *aInitiator,
421 const BSTR aDescription, BOOL aCancelable,
422 ULONG aOperationCount, const BSTR aOperationDescription,
423 GUIDPARAMOUT aId /* = NULL */)
424{
425 LogFlowMember(("Progress::init(): aDescription={%ls}\n", aDescription));
426
427 ComAssertRet (aOperationDescription, E_INVALIDARG);
428 ComAssertRet (aOperationCount >= 1, E_INVALIDARG);
429
430 AutoLock lock(this);
431 ComAssertRet (!isReady(), E_UNEXPECTED);
432
433 HRESULT rc = S_OK;
434
435 do
436 {
437 rc = ProgressBase::protectedInit (
438#if !defined (VBOX_COM_INPROC)
439 aParent,
440#endif
441 aInitiator, aDescription, aId);
442 CheckComRCBreakRC (rc);
443
444 // set ready to let protectedUninit() be called on failure
445 setReady (true);
446
447 mCancelable = aCancelable;
448
449 mOperationCount = aOperationCount;
450 mOperation = 0; // the first operation
451 mOperationDescription = aOperationDescription;
452
453 int vrc = RTSemEventMultiCreate (&mCompletedSem);
454 ComAssertRCBreak (vrc, rc = E_FAIL);
455
456 RTSemEventMultiReset (mCompletedSem);
457 }
458 while (0);
459
460 if (FAILED (rc))
461 uninit();
462
463 return rc;
464}
465
466HRESULT Progress::init (BOOL aCancelable, ULONG aOperationCount,
467 const BSTR aOperationDescription)
468{
469 LogFlowMember(("Progress::init(): <undescriptioned>\n"));
470
471 AutoLock lock(this);
472 ComAssertRet (!isReady(), E_UNEXPECTED);
473
474 HRESULT rc = S_OK;
475
476 do
477 {
478 rc = ProgressBase::protectedInit();
479 CheckComRCBreakRC (rc);
480
481 // set ready to let protectedUninit() be called on failure
482 setReady (true);
483
484 mCancelable = aCancelable;
485
486 mOperationCount = aOperationCount;
487 mOperation = 0; // the first operation
488 mOperationDescription = aOperationDescription;
489
490 int vrc = RTSemEventMultiCreate (&mCompletedSem);
491 ComAssertRCBreak (vrc, rc = E_FAIL);
492
493 RTSemEventMultiReset (mCompletedSem);
494 }
495 while (0);
496
497 if (FAILED (rc))
498 uninit();
499
500 return rc;
501}
502
503/**
504 * Uninitializes the instance and sets the ready flag to FALSE.
505 * Called either from FinalRelease() or by the parent when it gets destroyed.
506 */
507void Progress::uninit()
508{
509 LogFlowMember (("Progress::uninit()\n"));
510
511 AutoLock alock (this);
512
513 LogFlowMember (("Progress::uninit(): isReady=%d\n", isReady()));
514
515 if (!isReady())
516 return;
517
518 // wake up all threads still waiting by occasion
519 if (mWaitersCount > 0)
520 {
521 LogFlow (("WARNING: There are still %d threads waiting for '%ls' completion!\n",
522 mWaitersCount, mDescription.raw()));
523 RTSemEventMultiSignal (mCompletedSem);
524 }
525
526 RTSemEventMultiDestroy (mCompletedSem);
527
528 ProgressBase::protectedUninit (alock);
529}
530
531// IProgress properties
532/////////////////////////////////////////////////////////////////////////////
533
534// IProgress methods
535/////////////////////////////////////////////////////////////////////////////
536
537/**
538 * @note
539 * XPCOM: when this method is called not on the main XPCOM thread, it
540 * it simply blocks the thread until mCompletedSem is signalled. If the
541 * thread has its own event queue (hmm, what for?) that it must run, then
542 * calling this method will definitey freese event processing.
543 */
544STDMETHODIMP Progress::WaitForCompletion (LONG aTimeout)
545{
546 LogFlowMember(("Progress::WaitForCompletion: BEGIN: timeout=%d\n", aTimeout));
547
548 AutoLock lock(this);
549 CHECK_READY();
550
551 // if we're already completed, take a shortcut
552 if (!mCompleted)
553 {
554 RTTIMESPEC time;
555 RTTimeNow (&time);
556
557 int vrc = VINF_SUCCESS;
558 bool forever = aTimeout < 0;
559 int64_t timeLeft = aTimeout;
560 int64_t lastTime = RTTimeSpecGetMilli (&time);
561
562 while (!mCompleted && (forever || timeLeft > 0))
563 {
564 mWaitersCount ++;
565 lock.unlock();
566 int vrc = RTSemEventMultiWait (mCompletedSem,
567 forever ? RT_INDEFINITE_WAIT
568 : (unsigned) timeLeft);
569 lock.lock();
570 mWaitersCount --;
571
572 // the progress might have been uninitialized
573 if (!isReady())
574 break;
575
576 // the last waiter resets the semaphore
577 if (mWaitersCount == 0)
578 RTSemEventMultiReset (mCompletedSem);
579
580 if (VBOX_FAILURE (vrc) && vrc != VERR_TIMEOUT)
581 break;
582
583 if (!forever)
584 {
585 RTTimeNow (&time);
586 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
587 lastTime = RTTimeSpecGetMilli (&time);
588 }
589 }
590
591 if (VBOX_FAILURE (vrc) && vrc != VERR_TIMEOUT)
592 return setError (E_FAIL,
593 tr ("Failed to wait for the task completion (%Vrc)"), vrc);
594 }
595
596 LogFlowMember(("Progress::WaitForCompletion: END\n"));
597 return S_OK;
598}
599
600/**
601 * @note
602 * XPCOM: when this method is called not on the main XPCOM thread, it
603 * it simply blocks the thread until mCompletedSem is signalled. If the
604 * thread has its own event queue (hmm, what for?) that it must run, then
605 * calling this method will definitey freese event processing.
606 */
607STDMETHODIMP Progress::WaitForOperationCompletion (ULONG aOperation, LONG aTimeout)
608{
609 LogFlowMember(("Progress::WaitForOperationCompletion: BEGIN: "
610 "operation=%d, timeout=%d\n", aOperation, aTimeout));
611
612 AutoLock lock(this);
613 CHECK_READY();
614
615 if (aOperation >= mOperationCount)
616 return setError (E_FAIL,
617 tr ("Operation number must be in range [0, %d]"), mOperation - 1);
618
619 // if we're already completed or if the given operation is already done,
620 // then take a shortcut
621 if (!mCompleted && aOperation >= mOperation)
622 {
623 RTTIMESPEC time;
624 RTTimeNow (&time);
625
626 int vrc = VINF_SUCCESS;
627 bool forever = aTimeout < 0;
628 int64_t timeLeft = aTimeout;
629 int64_t lastTime = RTTimeSpecGetMilli (&time);
630
631 while (!mCompleted && aOperation >= mOperation &&
632 (forever || timeLeft > 0))
633 {
634 mWaitersCount ++;
635 lock.unlock();
636 int vrc = RTSemEventMultiWait (mCompletedSem,
637 forever ? RT_INDEFINITE_WAIT
638 : (unsigned) timeLeft);
639 lock.lock();
640 mWaitersCount --;
641
642 // the progress might have been uninitialized
643 if (!isReady())
644 break;
645
646 // the last waiter resets the semaphore
647 if (mWaitersCount == 0)
648 RTSemEventMultiReset (mCompletedSem);
649
650 if (VBOX_FAILURE (vrc) && vrc != VERR_TIMEOUT)
651 break;
652
653 if (!forever)
654 {
655 RTTimeNow (&time);
656 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
657 lastTime = RTTimeSpecGetMilli (&time);
658 }
659 }
660
661 if (VBOX_FAILURE (vrc) && vrc != VERR_TIMEOUT)
662 return setError (E_FAIL,
663 tr ("Failed to wait for the operation completion (%Vrc)"), vrc);
664 }
665
666 LogFlowMember(("Progress::WaitForOperationCompletion: END\n"));
667 return S_OK;
668}
669
670STDMETHODIMP Progress::Cancel()
671{
672 AutoLock lock(this);
673 CHECK_READY();
674
675 if (!mCancelable)
676 return setError (E_FAIL, tr ("Operation cannot be cancelled"));
677
678/// @todo (dmik): implement operation cancellation!
679// mCompleted = TRUE;
680// mCanceled = TRUE;
681// return S_OK;
682
683 ComAssertMsgFailed (("Not implemented!"));
684 return E_NOTIMPL;
685}
686
687// public methods only for internal purposes
688/////////////////////////////////////////////////////////////////////////////
689
690/**
691 * Updates the percentage value of the current operation.
692 *
693 * @param aPercent New percentage value of the operation in progress
694 * (in range [0, 100]).
695 */
696HRESULT Progress::notifyProgress (LONG aPercent)
697{
698 AutoLock lock (this);
699 AssertReturn (isReady(), E_UNEXPECTED);
700
701 AssertReturn (!mCompleted && !mCanceled, E_FAIL);
702 AssertReturn (aPercent >= 0 && aPercent <= 100, E_INVALIDARG);
703
704 mOperationPercent = aPercent;
705 return S_OK;
706}
707
708/**
709 * Signals that the current operation is successfully completed
710 * and advances to the next operation. The operation percentage is reset
711 * to 0.
712 *
713 * @param aOperationDescription Description of the next operation
714 *
715 * @note
716 * The current operation must not be the last one.
717 */
718HRESULT Progress::advanceOperation (const BSTR aOperationDescription)
719{
720 AssertReturn (aOperationDescription, E_INVALIDARG);
721
722 AutoLock lock (this);
723 AssertReturn (isReady(), E_UNEXPECTED);
724
725 AssertReturn (!mCompleted && !mCanceled, E_FAIL);
726 AssertReturn (mOperation + 1 < mOperationCount, E_FAIL);
727
728 mOperation ++;
729 mOperationDescription = aOperationDescription;
730 mOperationPercent = 0;
731
732 // wake up all waiting threads
733 if (mWaitersCount > 0)
734 RTSemEventMultiSignal (mCompletedSem);
735
736 return S_OK;
737}
738
739/**
740 * Marks the whole task as complete and sets the result code.
741 *
742 * If the result code indicates a failure (|FAILED (@a aResultCode)|)
743 * then this method will import the error info from the current
744 * thread and assign it to the errorInfo attribute (it will return an
745 * error if no info is available in such case).
746 *
747 * If the result code indicates a success (|SUCCEEDED (@a aResultCode)|)
748 * then the current operation is set to the last
749 *
750 * Note that this method may be called only once for the given Progress object.
751 * Subsequent calls will assert.
752 *
753 * @param aResultCode Operation result code
754 */
755HRESULT Progress::notifyComplete (HRESULT aResultCode)
756{
757 AutoLock lock (this);
758 AssertReturn (isReady(), E_FAIL);
759
760 AssertReturn (mCompleted == FALSE, E_FAIL);
761
762 mCompleted = TRUE;
763 mResultCode = aResultCode;
764
765 HRESULT rc = S_OK;
766
767 if (FAILED (aResultCode))
768 {
769 /* try to import error info from the current thread */
770
771#if !defined (VBOX_WITH_XPCOM)
772#if defined (RT_OS_WINDOWS)
773
774 ComPtr <IErrorInfo> err;
775 rc = ::GetErrorInfo (0, err.asOutParam());
776 if (rc == S_OK && err)
777 {
778 rc = err.queryInterfaceTo (mErrorInfo.asOutParam());
779 if (SUCCEEDED (rc) && !mErrorInfo)
780 rc = E_FAIL;
781 }
782
783#endif // !defined (RT_OS_WINDOWS)
784#else // !defined (VBOX_WITH_XPCOM)
785
786 nsCOMPtr <nsIExceptionService> es;
787 es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
788 if (NS_SUCCEEDED (rc))
789 {
790 nsCOMPtr <nsIExceptionManager> em;
791 rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
792 if (NS_SUCCEEDED (rc))
793 {
794 ComPtr <nsIException> ex;
795 rc = em->GetCurrentException (ex.asOutParam());
796 if (NS_SUCCEEDED (rc) && ex)
797 {
798 rc = ex.queryInterfaceTo (mErrorInfo.asOutParam());
799 if (NS_SUCCEEDED (rc) && !mErrorInfo)
800 rc = E_FAIL;
801 }
802 }
803 }
804#endif // !defined (VBOX_WITH_XPCOM)
805
806 AssertMsg (rc == S_OK, ("Couldn't get error info (rc=%08X) while trying "
807 "to set a failed result (%08X)!\n", rc, aResultCode));
808 }
809 else
810 {
811 mOperation = mOperationCount - 1; /* last operation */
812 mOperationPercent = 100;
813 }
814
815#if !defined VBOX_COM_INPROC
816 /* remove from the global collection of pending progress operations */
817 if (mParent)
818 mParent->removeProgress (mId);
819#endif
820
821 /* wake up all waiting threads */
822 if (mWaitersCount > 0)
823 RTSemEventMultiSignal (mCompletedSem);
824
825 return rc;
826}
827
828/**
829 * Marks the operation as complete and attaches full error info.
830 * See VirtualBoxSupportErrorInfoImpl::setError(HRESULT, const GUID &, const wchar_t *, const char *, ...)
831 * for more info.
832 *
833 * @param aResultCode operation result (error) code, must not be S_OK
834 * @param aIID IID of the intrface that defines the error
835 * @param aComponent name of the component that generates the error
836 * @param aText error message (must not be null), an RTStrPrintf-like
837 * format string in UTF-8 encoding
838 * @param ... list of arguments for the format string
839 */
840HRESULT Progress::notifyComplete (HRESULT aResultCode, const GUID &aIID,
841 const Bstr &aComponent,
842 const char *aText, ...)
843{
844 va_list args;
845 va_start (args, aText);
846 Bstr text = Utf8StrFmtVA (aText, args);
847 va_end (args);
848
849 return notifyCompleteBstr (aResultCode, aIID, aComponent, text);
850}
851
852/**
853 * Marks the operation as complete and attaches full error info.
854 * See VirtualBoxSupportErrorInfoImpl::setError(HRESULT, const GUID &, const wchar_t *, const char *, ...)
855 * for more info.
856 *
857 * This method is preferred iy you have a ready (translated and formatted)
858 * Bstr string, because it omits an extra conversion Utf8Str -> Bstr.
859 *
860 * @param aResultCode operation result (error) code, must not be S_OK
861 * @param aIID IID of the intrface that defines the error
862 * @param aComponent name of the component that generates the error
863 * @param aText error message (must not be null)
864 */
865HRESULT Progress::notifyCompleteBstr (HRESULT aResultCode, const GUID &aIID,
866 const Bstr &aComponent, const Bstr &aText)
867{
868 AutoLock lock (this);
869 AssertReturn (isReady(), E_UNEXPECTED);
870
871 mCompleted = TRUE;
872 mResultCode = aResultCode;
873
874 AssertReturn (FAILED (aResultCode), E_FAIL);
875
876 ComObjPtr <VirtualBoxErrorInfo> errorInfo;
877 HRESULT rc = errorInfo.createObject();
878 AssertComRC (rc);
879 if (SUCCEEDED (rc))
880 {
881 errorInfo->init (aResultCode, aIID, aComponent, aText);
882 errorInfo.queryInterfaceTo (mErrorInfo.asOutParam());
883 }
884
885#if !defined VBOX_COM_INPROC
886 /* remove from the global collection of pending progress operations */
887 if (mParent)
888 mParent->removeProgress (mId);
889#endif
890
891 /* wake up all waiting threads */
892 if (mWaitersCount > 0)
893 RTSemEventMultiSignal (mCompletedSem);
894
895 return rc;
896}
897
898////////////////////////////////////////////////////////////////////////////////
899// CombinedProgress class
900////////////////////////////////////////////////////////////////////////////////
901
902HRESULT CombinedProgress::FinalConstruct()
903{
904 HRESULT rc = ProgressBase::FinalConstruct();
905 if (FAILED (rc))
906 return rc;
907
908 mProgress = 0;
909 mCompletedOperations = 0;
910
911 return S_OK;
912}
913
914void CombinedProgress::FinalRelease()
915{
916 uninit();
917}
918
919// public initializer/uninitializer for internal purposes only
920////////////////////////////////////////////////////////////////////////////////
921
922/**
923 * Initializes this object based on individual combined progresses.
924 * Must be called only from #init()!
925 *
926 * @param aParent see ProgressBase::init()
927 * @param aInitiator see ProgressBase::init()
928 * @param aDescription see ProgressBase::init()
929 * @param aId see ProgressBase::init()
930 */
931HRESULT CombinedProgress::protectedInit (
932#if !defined (VBOX_COM_INPROC)
933 VirtualBox *aParent,
934#endif
935 IUnknown *aInitiator,
936 const BSTR aDescription, GUIDPARAMOUT aId)
937{
938 LogFlowMember (("CombinedProgress::protectedInit(): "
939 "aDescription={%ls} mProgresses.size()=%d\n",
940 aDescription, mProgresses.size()));
941
942 HRESULT rc = S_OK;
943
944 do
945 {
946 rc = ProgressBase::protectedInit (
947#if !defined (VBOX_COM_INPROC)
948 aParent,
949#endif
950 aInitiator, aDescription, aId);
951 CheckComRCBreakRC (rc);
952
953 // set ready to let protectedUninit() be called on failure
954 setReady (true);
955
956 mProgress = 0; // the first object
957 mCompletedOperations = 0;
958
959 mCompleted = FALSE;
960 mCancelable = TRUE; // until any progress returns FALSE
961 mCanceled = FALSE;
962
963 mOperationCount = 0; // will be calculated later
964 mOperation = 0;
965 rc = mProgresses [0]->COMGETTER(OperationDescription) (
966 mOperationDescription.asOutParam());
967 CheckComRCBreakRC (rc);
968
969 for (size_t i = 0; i < mProgresses.size(); i ++)
970 {
971 if (mCancelable)
972 {
973 BOOL cancelable = FALSE;
974 rc = mProgresses [i]->COMGETTER(Cancelable) (&cancelable);
975 if (FAILED (rc))
976 return rc;
977 if (!cancelable)
978 mCancelable = FALSE;
979 }
980
981 {
982 ULONG opCount = 0;
983 rc = mProgresses [i]->COMGETTER(OperationCount) (&opCount);
984 if (FAILED (rc))
985 return rc;
986 mOperationCount += opCount;
987 }
988 }
989
990 rc = checkProgress();
991 CheckComRCBreakRC (rc);
992 }
993 while (0);
994
995 if (FAILED (rc))
996 uninit();
997
998 return rc;
999}
1000
1001/**
1002 * Uninitializes the instance and sets the ready flag to FALSE.
1003 * Called either from FinalRelease() or by the parent when it gets destroyed.
1004 */
1005void CombinedProgress::uninit()
1006{
1007 LogFlowMember (("CombinedProgress::uninit()\n"));
1008
1009 AutoLock alock (this);
1010
1011 LogFlowMember (("CombinedProgress::uninit(): isReady=%d\n", isReady()));
1012
1013 if (!isReady())
1014 return;
1015
1016 mProgress = 0;
1017 mProgresses.clear();
1018
1019 ProgressBase::protectedUninit (alock);
1020}
1021
1022// IProgress properties
1023////////////////////////////////////////////////////////////////////////////////
1024
1025STDMETHODIMP CombinedProgress::COMGETTER(Percent) (LONG *aPercent)
1026{
1027 if (!aPercent)
1028 return E_POINTER;
1029
1030 AutoLock lock(this);
1031 CHECK_READY();
1032
1033 if (mCompleted && SUCCEEDED (mResultCode))
1034 *aPercent = 100;
1035 else
1036 {
1037 HRESULT rc = checkProgress();
1038 if (FAILED (rc))
1039 return rc;
1040
1041 // global percent = (100 / mOperationCount) * mOperation +
1042 // ((100 / mOperationCount) / 100) * mOperationPercent
1043 *aPercent = (100 * mOperation + mOperationPercent) / mOperationCount;
1044 }
1045
1046 return S_OK;
1047}
1048
1049STDMETHODIMP CombinedProgress::COMGETTER(Completed) (BOOL *aCompleted)
1050{
1051 if (!aCompleted)
1052 return E_POINTER;
1053
1054 AutoLock lock(this);
1055 CHECK_READY();
1056
1057 HRESULT rc = checkProgress();
1058 if (FAILED (rc))
1059 return rc;
1060
1061 return ProgressBase::COMGETTER(Completed) (aCompleted);
1062}
1063
1064STDMETHODIMP CombinedProgress::COMGETTER(Canceled) (BOOL *aCanceled)
1065{
1066 if (!aCanceled)
1067 return E_POINTER;
1068
1069 AutoLock lock(this);
1070 CHECK_READY();
1071
1072 HRESULT rc = checkProgress();
1073 if (FAILED (rc))
1074 return rc;
1075
1076 return ProgressBase::COMGETTER(Canceled) (aCanceled);
1077}
1078
1079STDMETHODIMP CombinedProgress::COMGETTER(ResultCode) (HRESULT *aResultCode)
1080{
1081 if (!aResultCode)
1082 return E_POINTER;
1083
1084 AutoLock lock(this);
1085 CHECK_READY();
1086
1087 HRESULT rc = checkProgress();
1088 if (FAILED (rc))
1089 return rc;
1090
1091 return ProgressBase::COMGETTER(ResultCode) (aResultCode);
1092}
1093
1094STDMETHODIMP CombinedProgress::COMGETTER(ErrorInfo) (IVirtualBoxErrorInfo **aErrorInfo)
1095{
1096 if (!aErrorInfo)
1097 return E_POINTER;
1098
1099 AutoLock lock(this);
1100 CHECK_READY();
1101
1102 HRESULT rc = checkProgress();
1103 if (FAILED (rc))
1104 return rc;
1105
1106 return ProgressBase::COMGETTER(ErrorInfo) (aErrorInfo);
1107}
1108
1109STDMETHODIMP CombinedProgress::COMGETTER(Operation) (ULONG *aOperation)
1110{
1111 if (!aOperation)
1112 return E_POINTER;
1113
1114 AutoLock lock(this);
1115 CHECK_READY();
1116
1117 HRESULT rc = checkProgress();
1118 if (FAILED (rc))
1119 return rc;
1120
1121 return ProgressBase::COMGETTER(Operation) (aOperation);
1122}
1123
1124STDMETHODIMP CombinedProgress::COMGETTER(OperationDescription) (BSTR *aOperationDescription)
1125{
1126 if (!aOperationDescription)
1127 return E_POINTER;
1128
1129 AutoLock lock(this);
1130 CHECK_READY();
1131
1132 HRESULT rc = checkProgress();
1133 if (FAILED (rc))
1134 return rc;
1135
1136 return ProgressBase::COMGETTER(OperationDescription) (aOperationDescription);
1137}
1138
1139STDMETHODIMP CombinedProgress::COMGETTER(OperationPercent) (LONG *aOperationPercent)
1140{
1141 if (!aOperationPercent)
1142 return E_POINTER;
1143
1144 AutoLock lock(this);
1145 CHECK_READY();
1146
1147 HRESULT rc = checkProgress();
1148 if (FAILED (rc))
1149 return rc;
1150
1151 return ProgressBase::COMGETTER(OperationPercent) (aOperationPercent);
1152}
1153
1154// IProgress methods
1155/////////////////////////////////////////////////////////////////////////////
1156
1157/**
1158 * @note
1159 * XPCOM: when this method is called not on the main XPCOM thread, it
1160 * it simply blocks the thread until mCompletedSem is signalled. If the
1161 * thread has its own event queue (hmm, what for?) that it must run, then
1162 * calling this method will definitey freese event processing.
1163 */
1164STDMETHODIMP CombinedProgress::WaitForCompletion (LONG aTimeout)
1165{
1166 LogFlowMember (("CombinedProgress::WaitForCompletion: BEGIN: timeout=%d\n",
1167 aTimeout));
1168
1169 AutoLock lock (this);
1170 CHECK_READY();
1171
1172 // if we're already completed, take a shortcut
1173 if (!mCompleted)
1174 {
1175 RTTIMESPEC time;
1176 RTTimeNow (&time);
1177
1178 HRESULT rc = S_OK;
1179 bool forever = aTimeout < 0;
1180 int64_t timeLeft = aTimeout;
1181 int64_t lastTime = RTTimeSpecGetMilli (&time);
1182
1183 while (!mCompleted && (forever || timeLeft > 0))
1184 {
1185 lock.unlock();
1186 rc = mProgresses.back()->WaitForCompletion (
1187 forever ? -1 : (LONG) timeLeft);
1188 lock.lock();
1189
1190 // the progress might have been uninitialized
1191 if (!isReady())
1192 break;
1193
1194 if (SUCCEEDED (rc))
1195 rc = checkProgress();
1196
1197 if (FAILED (rc))
1198 break;
1199
1200 if (!forever)
1201 {
1202 RTTimeNow (&time);
1203 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
1204 lastTime = RTTimeSpecGetMilli (&time);
1205 }
1206 }
1207
1208 if (FAILED (rc))
1209 return rc;
1210 }
1211
1212 LogFlowMember(("CombinedProgress::WaitForCompletion: END\n"));
1213 return S_OK;
1214}
1215
1216/**
1217 * @note
1218 * XPCOM: when this method is called not on the main XPCOM thread, it
1219 * it simply blocks the thread until mCompletedSem is signalled. If the
1220 * thread has its own event queue (hmm, what for?) that it must run, then
1221 * calling this method will definitey freese event processing.
1222 */
1223STDMETHODIMP CombinedProgress::WaitForOperationCompletion (ULONG aOperation, LONG aTimeout)
1224{
1225 LogFlowMember(("CombinedProgress::WaitForOperationCompletion: BEGIN: "
1226 "operation=%d, timeout=%d\n", aOperation, aTimeout));
1227
1228 AutoLock lock(this);
1229 CHECK_READY();
1230
1231 if (aOperation >= mOperationCount)
1232 return setError (E_FAIL,
1233 tr ("Operation number must be in range [0, %d]"), mOperation - 1);
1234
1235 // if we're already completed or if the given operation is already done,
1236 // then take a shortcut
1237 if (!mCompleted && aOperation >= mOperation)
1238 {
1239 HRESULT rc = S_OK;
1240
1241 // find the right progress object to wait for
1242 size_t progress = mProgress;
1243 ULONG operation = 0, completedOps = mCompletedOperations;
1244 do
1245 {
1246 ULONG opCount = 0;
1247 rc = mProgresses [progress]->COMGETTER(OperationCount) (&opCount);
1248 if (FAILED (rc))
1249 return rc;
1250
1251 if (completedOps + opCount > aOperation)
1252 {
1253 // found the right progress object
1254 operation = aOperation - completedOps;
1255 break;
1256 }
1257
1258 completedOps += opCount;
1259 progress ++;
1260 ComAssertRet (progress < mProgresses.size(), E_FAIL);
1261 }
1262 while (1);
1263
1264 LogFlowMember (("CombinedProgress::WaitForOperationCompletion(): "
1265 "will wait for mProgresses [%d] (%d)\n",
1266 progress, operation));
1267
1268 RTTIMESPEC time;
1269 RTTimeNow (&time);
1270
1271 bool forever = aTimeout < 0;
1272 int64_t timeLeft = aTimeout;
1273 int64_t lastTime = RTTimeSpecGetMilli (&time);
1274
1275 while (!mCompleted && aOperation >= mOperation &&
1276 (forever || timeLeft > 0))
1277 {
1278 lock.unlock();
1279 // wait for the appropriate progress operation completion
1280 rc = mProgresses [progress]-> WaitForOperationCompletion (
1281 operation, forever ? -1 : (LONG) timeLeft);
1282 lock.lock();
1283
1284 // the progress might have been uninitialized
1285 if (!isReady())
1286 break;
1287
1288 if (SUCCEEDED (rc))
1289 rc = checkProgress();
1290
1291 if (FAILED (rc))
1292 break;
1293
1294 if (!forever)
1295 {
1296 RTTimeNow (&time);
1297 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
1298 lastTime = RTTimeSpecGetMilli (&time);
1299 }
1300 }
1301
1302 if (FAILED (rc))
1303 return rc;
1304 }
1305
1306 LogFlowMember(("CombinedProgress::WaitForOperationCompletion: END\n"));
1307 return S_OK;
1308}
1309
1310STDMETHODIMP CombinedProgress::Cancel()
1311{
1312 AutoLock lock(this);
1313 CHECK_READY();
1314
1315 if (!mCancelable)
1316 return setError (E_FAIL, tr ("Operation cannot be cancelled"));
1317
1318/// @todo (dmik): implement operation cancellation!
1319// mCompleted = TRUE;
1320// mCanceled = TRUE;
1321// return S_OK;
1322
1323 return setError (E_NOTIMPL, ("Not implemented!"));
1324}
1325
1326// private methods
1327////////////////////////////////////////////////////////////////////////////////
1328
1329/**
1330 * Fetches the properties of the current progress object and, if it is
1331 * successfully completed, advances to the next uncompleted or unsucessfully
1332 * completed object in the vector of combined progress objects.
1333 *
1334 * @note Must be called from under the object's lock!
1335 */
1336HRESULT CombinedProgress::checkProgress()
1337{
1338 // do nothing if we're already marked ourselves as completed
1339 if (mCompleted)
1340 return S_OK;
1341
1342 ComAssertRet (mProgress < mProgresses.size(), E_FAIL);
1343
1344 ComPtr <IProgress> progress = mProgresses [mProgress];
1345 ComAssertRet (!progress.isNull(), E_FAIL);
1346
1347 HRESULT rc = S_OK;
1348 BOOL completed = FALSE;
1349
1350 do
1351 {
1352 rc = progress->COMGETTER(Completed) (&completed);
1353 if (FAILED (rc))
1354 return rc;
1355
1356 if (completed)
1357 {
1358 rc = progress->COMGETTER(Canceled) (&mCanceled);
1359 if (FAILED (rc))
1360 return rc;
1361
1362 rc = progress->COMGETTER(ResultCode) (&mResultCode);
1363 if (FAILED (rc))
1364 return rc;
1365
1366 if (FAILED (mResultCode))
1367 {
1368 rc = progress->COMGETTER(ErrorInfo) (mErrorInfo.asOutParam());
1369 if (FAILED (rc))
1370 return rc;
1371 }
1372
1373 if (FAILED (mResultCode) || mCanceled)
1374 {
1375 mCompleted = TRUE;
1376 }
1377 else
1378 {
1379 ULONG opCount = 0;
1380 rc = progress->COMGETTER(OperationCount) (&opCount);
1381 if (FAILED (rc))
1382 return rc;
1383
1384 mCompletedOperations += opCount;
1385 mProgress ++;
1386
1387 if (mProgress < mProgresses.size())
1388 progress = mProgresses [mProgress];
1389 else
1390 mCompleted = TRUE;
1391 }
1392 }
1393 }
1394 while (completed && !mCompleted);
1395
1396 rc = progress->COMGETTER(OperationPercent) (&mOperationPercent);
1397 if (SUCCEEDED (rc))
1398 {
1399 ULONG operation = 0;
1400 rc = progress->COMGETTER(Operation) (&operation);
1401 if (SUCCEEDED (rc) && mCompletedOperations + operation > mOperation)
1402 {
1403 mOperation = mCompletedOperations + operation;
1404 rc = progress->COMGETTER(OperationDescription) (
1405 mOperationDescription.asOutParam());
1406 }
1407 }
1408
1409 return rc;
1410}
1411
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