VirtualBox

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

Last change on this file since 4753 was 4134, checked in by vboxsync, 17 years ago

Main: Add Utf8StrFmtVA to resolve the dangerous ambiguity of ellipsis and va_list.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.9 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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * 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 * @param aResultCode Operation result code
751 */
752HRESULT Progress::notifyComplete (HRESULT aResultCode)
753{
754 AutoLock lock (this);
755 AssertReturn (isReady(), E_FAIL);
756
757 mCompleted = TRUE;
758 mResultCode = aResultCode;
759
760 HRESULT rc = S_OK;
761
762 if (FAILED (aResultCode))
763 {
764 /* try to import error info from the current thread */
765
766#if !defined (VBOX_WITH_XPCOM)
767#if defined (RT_OS_WINDOWS)
768
769 ComPtr <IErrorInfo> err;
770 rc = ::GetErrorInfo (0, err.asOutParam());
771 if (rc == S_OK && err)
772 {
773 rc = err.queryInterfaceTo (mErrorInfo.asOutParam());
774 if (SUCCEEDED (rc) && !mErrorInfo)
775 rc = E_FAIL;
776 }
777
778#endif // !defined (RT_OS_WINDOWS)
779#else // !defined (VBOX_WITH_XPCOM)
780
781 nsCOMPtr <nsIExceptionService> es;
782 es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
783 if (NS_SUCCEEDED (rc))
784 {
785 nsCOMPtr <nsIExceptionManager> em;
786 rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
787 if (NS_SUCCEEDED (rc))
788 {
789 ComPtr <nsIException> ex;
790 rc = em->GetCurrentException (ex.asOutParam());
791 if (NS_SUCCEEDED (rc) && ex)
792 {
793 rc = ex.queryInterfaceTo (mErrorInfo.asOutParam());
794 if (NS_SUCCEEDED (rc) && !mErrorInfo)
795 rc = E_FAIL;
796 }
797 }
798 }
799#endif // !defined (VBOX_WITH_XPCOM)
800
801 AssertMsg (rc == S_OK, ("Couldn't get error info (rc=%08X) while trying "
802 "to set a failed result (%08X)!\n", rc, aResultCode));
803 }
804 else
805 {
806 mOperation = mOperationCount - 1; /* last operation */
807 mOperationPercent = 100;
808 }
809
810#if !defined VBOX_COM_INPROC
811 /* remove from the global collection of pending progress operations */
812 if (mParent)
813 mParent->removeProgress (mId);
814#endif
815
816 /* wake up all waiting threads */
817 if (mWaitersCount > 0)
818 RTSemEventMultiSignal (mCompletedSem);
819
820 return rc;
821}
822
823/**
824 * Marks the operation as complete and attaches full error info.
825 * See VirtualBoxSupportErrorInfoImpl::setError(HRESULT, const GUID &, const wchar_t *, const char *, ...)
826 * for more info.
827 *
828 * @param aResultCode operation result (error) code, must not be S_OK
829 * @param aIID IID of the intrface that defines the error
830 * @param aComponent name of the component that generates the error
831 * @param aText error message (must not be null), an RTStrPrintf-like
832 * format string in UTF-8 encoding
833 * @param ... list of arguments for the format string
834 */
835HRESULT Progress::notifyComplete (HRESULT aResultCode, const GUID &aIID,
836 const Bstr &aComponent,
837 const char *aText, ...)
838{
839 va_list args;
840 va_start (args, aText);
841 Bstr text = Utf8StrFmtVA (aText, args);
842 va_end (args);
843
844 return notifyCompleteBstr (aResultCode, aIID, aComponent, text);
845}
846
847/**
848 * Marks the operation as complete and attaches full error info.
849 * See VirtualBoxSupportErrorInfoImpl::setError(HRESULT, const GUID &, const wchar_t *, const char *, ...)
850 * for more info.
851 *
852 * This method is preferred iy you have a ready (translated and formatted)
853 * Bstr string, because it omits an extra conversion Utf8Str -> Bstr.
854 *
855 * @param aResultCode operation result (error) code, must not be S_OK
856 * @param aIID IID of the intrface that defines the error
857 * @param aComponent name of the component that generates the error
858 * @param aText error message (must not be null)
859 */
860HRESULT Progress::notifyCompleteBstr (HRESULT aResultCode, const GUID &aIID,
861 const Bstr &aComponent, const Bstr &aText)
862{
863 AutoLock lock (this);
864 AssertReturn (isReady(), E_UNEXPECTED);
865
866 mCompleted = TRUE;
867 mResultCode = aResultCode;
868
869 AssertReturn (FAILED (aResultCode), E_FAIL);
870
871 ComObjPtr <VirtualBoxErrorInfo> errorInfo;
872 HRESULT rc = errorInfo.createObject();
873 AssertComRC (rc);
874 if (SUCCEEDED (rc))
875 {
876 errorInfo->init (aResultCode, aIID, aComponent, aText);
877 errorInfo.queryInterfaceTo (mErrorInfo.asOutParam());
878 }
879
880#if !defined VBOX_COM_INPROC
881 /* remove from the global collection of pending progress operations */
882 if (mParent)
883 mParent->removeProgress (mId);
884#endif
885
886 /* wake up all waiting threads */
887 if (mWaitersCount > 0)
888 RTSemEventMultiSignal (mCompletedSem);
889
890 return rc;
891}
892
893////////////////////////////////////////////////////////////////////////////////
894// CombinedProgress class
895////////////////////////////////////////////////////////////////////////////////
896
897HRESULT CombinedProgress::FinalConstruct()
898{
899 HRESULT rc = ProgressBase::FinalConstruct();
900 if (FAILED (rc))
901 return rc;
902
903 mProgress = 0;
904 mCompletedOperations = 0;
905
906 return S_OK;
907}
908
909void CombinedProgress::FinalRelease()
910{
911 uninit();
912}
913
914// public initializer/uninitializer for internal purposes only
915////////////////////////////////////////////////////////////////////////////////
916
917/**
918 * Initializes this object based on individual combined progresses.
919 * Must be called only from #init()!
920 *
921 * @param aParent see ProgressBase::init()
922 * @param aInitiator see ProgressBase::init()
923 * @param aDescription see ProgressBase::init()
924 * @param aId see ProgressBase::init()
925 */
926HRESULT CombinedProgress::protectedInit (
927#if !defined (VBOX_COM_INPROC)
928 VirtualBox *aParent,
929#endif
930 IUnknown *aInitiator,
931 const BSTR aDescription, GUIDPARAMOUT aId)
932{
933 LogFlowMember (("CombinedProgress::protectedInit(): "
934 "aDescription={%ls} mProgresses.size()=%d\n",
935 aDescription, mProgresses.size()));
936
937 HRESULT rc = S_OK;
938
939 do
940 {
941 rc = ProgressBase::protectedInit (
942#if !defined (VBOX_COM_INPROC)
943 aParent,
944#endif
945 aInitiator, aDescription, aId);
946 CheckComRCBreakRC (rc);
947
948 // set ready to let protectedUninit() be called on failure
949 setReady (true);
950
951 mProgress = 0; // the first object
952 mCompletedOperations = 0;
953
954 mCompleted = FALSE;
955 mCancelable = TRUE; // until any progress returns FALSE
956 mCanceled = FALSE;
957
958 mOperationCount = 0; // will be calculated later
959 mOperation = 0;
960 rc = mProgresses [0]->COMGETTER(OperationDescription) (
961 mOperationDescription.asOutParam());
962 CheckComRCBreakRC (rc);
963
964 for (size_t i = 0; i < mProgresses.size(); i ++)
965 {
966 if (mCancelable)
967 {
968 BOOL cancelable = FALSE;
969 rc = mProgresses [i]->COMGETTER(Cancelable) (&cancelable);
970 if (FAILED (rc))
971 return rc;
972 if (!cancelable)
973 mCancelable = FALSE;
974 }
975
976 {
977 ULONG opCount = 0;
978 rc = mProgresses [i]->COMGETTER(OperationCount) (&opCount);
979 if (FAILED (rc))
980 return rc;
981 mOperationCount += opCount;
982 }
983 }
984
985 rc = checkProgress();
986 CheckComRCBreakRC (rc);
987 }
988 while (0);
989
990 if (FAILED (rc))
991 uninit();
992
993 return rc;
994}
995
996/**
997 * Uninitializes the instance and sets the ready flag to FALSE.
998 * Called either from FinalRelease() or by the parent when it gets destroyed.
999 */
1000void CombinedProgress::uninit()
1001{
1002 LogFlowMember (("CombinedProgress::uninit()\n"));
1003
1004 AutoLock alock (this);
1005
1006 LogFlowMember (("CombinedProgress::uninit(): isReady=%d\n", isReady()));
1007
1008 if (!isReady())
1009 return;
1010
1011 mProgress = 0;
1012 mProgresses.clear();
1013
1014 ProgressBase::protectedUninit (alock);
1015}
1016
1017// IProgress properties
1018////////////////////////////////////////////////////////////////////////////////
1019
1020STDMETHODIMP CombinedProgress::COMGETTER(Percent) (LONG *aPercent)
1021{
1022 if (!aPercent)
1023 return E_POINTER;
1024
1025 AutoLock lock(this);
1026 CHECK_READY();
1027
1028 if (mCompleted && SUCCEEDED (mResultCode))
1029 *aPercent = 100;
1030 else
1031 {
1032 HRESULT rc = checkProgress();
1033 if (FAILED (rc))
1034 return rc;
1035
1036 // global percent = (100 / mOperationCount) * mOperation +
1037 // ((100 / mOperationCount) / 100) * mOperationPercent
1038 *aPercent = (100 * mOperation + mOperationPercent) / mOperationCount;
1039 }
1040
1041 return S_OK;
1042}
1043
1044STDMETHODIMP CombinedProgress::COMGETTER(Completed) (BOOL *aCompleted)
1045{
1046 if (!aCompleted)
1047 return E_POINTER;
1048
1049 AutoLock lock(this);
1050 CHECK_READY();
1051
1052 HRESULT rc = checkProgress();
1053 if (FAILED (rc))
1054 return rc;
1055
1056 return ProgressBase::COMGETTER(Completed) (aCompleted);
1057}
1058
1059STDMETHODIMP CombinedProgress::COMGETTER(Canceled) (BOOL *aCanceled)
1060{
1061 if (!aCanceled)
1062 return E_POINTER;
1063
1064 AutoLock lock(this);
1065 CHECK_READY();
1066
1067 HRESULT rc = checkProgress();
1068 if (FAILED (rc))
1069 return rc;
1070
1071 return ProgressBase::COMGETTER(Canceled) (aCanceled);
1072}
1073
1074STDMETHODIMP CombinedProgress::COMGETTER(ResultCode) (HRESULT *aResultCode)
1075{
1076 if (!aResultCode)
1077 return E_POINTER;
1078
1079 AutoLock lock(this);
1080 CHECK_READY();
1081
1082 HRESULT rc = checkProgress();
1083 if (FAILED (rc))
1084 return rc;
1085
1086 return ProgressBase::COMGETTER(ResultCode) (aResultCode);
1087}
1088
1089STDMETHODIMP CombinedProgress::COMGETTER(ErrorInfo) (IVirtualBoxErrorInfo **aErrorInfo)
1090{
1091 if (!aErrorInfo)
1092 return E_POINTER;
1093
1094 AutoLock lock(this);
1095 CHECK_READY();
1096
1097 HRESULT rc = checkProgress();
1098 if (FAILED (rc))
1099 return rc;
1100
1101 return ProgressBase::COMGETTER(ErrorInfo) (aErrorInfo);
1102}
1103
1104STDMETHODIMP CombinedProgress::COMGETTER(Operation) (ULONG *aOperation)
1105{
1106 if (!aOperation)
1107 return E_POINTER;
1108
1109 AutoLock lock(this);
1110 CHECK_READY();
1111
1112 HRESULT rc = checkProgress();
1113 if (FAILED (rc))
1114 return rc;
1115
1116 return ProgressBase::COMGETTER(Operation) (aOperation);
1117}
1118
1119STDMETHODIMP CombinedProgress::COMGETTER(OperationDescription) (BSTR *aOperationDescription)
1120{
1121 if (!aOperationDescription)
1122 return E_POINTER;
1123
1124 AutoLock lock(this);
1125 CHECK_READY();
1126
1127 HRESULT rc = checkProgress();
1128 if (FAILED (rc))
1129 return rc;
1130
1131 return ProgressBase::COMGETTER(OperationDescription) (aOperationDescription);
1132}
1133
1134STDMETHODIMP CombinedProgress::COMGETTER(OperationPercent) (LONG *aOperationPercent)
1135{
1136 if (!aOperationPercent)
1137 return E_POINTER;
1138
1139 AutoLock lock(this);
1140 CHECK_READY();
1141
1142 HRESULT rc = checkProgress();
1143 if (FAILED (rc))
1144 return rc;
1145
1146 return ProgressBase::COMGETTER(OperationPercent) (aOperationPercent);
1147}
1148
1149// IProgress methods
1150/////////////////////////////////////////////////////////////////////////////
1151
1152/**
1153 * @note
1154 * XPCOM: when this method is called not on the main XPCOM thread, it
1155 * it simply blocks the thread until mCompletedSem is signalled. If the
1156 * thread has its own event queue (hmm, what for?) that it must run, then
1157 * calling this method will definitey freese event processing.
1158 */
1159STDMETHODIMP CombinedProgress::WaitForCompletion (LONG aTimeout)
1160{
1161 LogFlowMember (("CombinedProgress::WaitForCompletion: BEGIN: timeout=%d\n",
1162 aTimeout));
1163
1164 AutoLock lock (this);
1165 CHECK_READY();
1166
1167 // if we're already completed, take a shortcut
1168 if (!mCompleted)
1169 {
1170 RTTIMESPEC time;
1171 RTTimeNow (&time);
1172
1173 HRESULT rc = S_OK;
1174 bool forever = aTimeout < 0;
1175 int64_t timeLeft = aTimeout;
1176 int64_t lastTime = RTTimeSpecGetMilli (&time);
1177
1178 while (!mCompleted && (forever || timeLeft > 0))
1179 {
1180 lock.unlock();
1181 rc = mProgresses.back()->WaitForCompletion (
1182 forever ? -1 : (LONG) timeLeft);
1183 lock.lock();
1184
1185 // the progress might have been uninitialized
1186 if (!isReady())
1187 break;
1188
1189 if (SUCCEEDED (rc))
1190 rc = checkProgress();
1191
1192 if (FAILED (rc))
1193 break;
1194
1195 if (!forever)
1196 {
1197 RTTimeNow (&time);
1198 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
1199 lastTime = RTTimeSpecGetMilli (&time);
1200 }
1201 }
1202
1203 if (FAILED (rc))
1204 return rc;
1205 }
1206
1207 LogFlowMember(("CombinedProgress::WaitForCompletion: END\n"));
1208 return S_OK;
1209}
1210
1211/**
1212 * @note
1213 * XPCOM: when this method is called not on the main XPCOM thread, it
1214 * it simply blocks the thread until mCompletedSem is signalled. If the
1215 * thread has its own event queue (hmm, what for?) that it must run, then
1216 * calling this method will definitey freese event processing.
1217 */
1218STDMETHODIMP CombinedProgress::WaitForOperationCompletion (ULONG aOperation, LONG aTimeout)
1219{
1220 LogFlowMember(("CombinedProgress::WaitForOperationCompletion: BEGIN: "
1221 "operation=%d, timeout=%d\n", aOperation, aTimeout));
1222
1223 AutoLock lock(this);
1224 CHECK_READY();
1225
1226 if (aOperation >= mOperationCount)
1227 return setError (E_FAIL,
1228 tr ("Operation number must be in range [0, %d]"), mOperation - 1);
1229
1230 // if we're already completed or if the given operation is already done,
1231 // then take a shortcut
1232 if (!mCompleted && aOperation >= mOperation)
1233 {
1234 HRESULT rc = S_OK;
1235
1236 // find the right progress object to wait for
1237 size_t progress = mProgress;
1238 ULONG operation = 0, completedOps = mCompletedOperations;
1239 do
1240 {
1241 ULONG opCount = 0;
1242 rc = mProgresses [progress]->COMGETTER(OperationCount) (&opCount);
1243 if (FAILED (rc))
1244 return rc;
1245
1246 if (completedOps + opCount > aOperation)
1247 {
1248 // found the right progress object
1249 operation = aOperation - completedOps;
1250 break;
1251 }
1252
1253 completedOps += opCount;
1254 progress ++;
1255 ComAssertRet (progress < mProgresses.size(), E_FAIL);
1256 }
1257 while (1);
1258
1259 LogFlowMember (("CombinedProgress::WaitForOperationCompletion(): "
1260 "will wait for mProgresses [%d] (%d)\n",
1261 progress, operation));
1262
1263 RTTIMESPEC time;
1264 RTTimeNow (&time);
1265
1266 bool forever = aTimeout < 0;
1267 int64_t timeLeft = aTimeout;
1268 int64_t lastTime = RTTimeSpecGetMilli (&time);
1269
1270 while (!mCompleted && aOperation >= mOperation &&
1271 (forever || timeLeft > 0))
1272 {
1273 lock.unlock();
1274 // wait for the appropriate progress operation completion
1275 rc = mProgresses [progress]-> WaitForOperationCompletion (
1276 operation, forever ? -1 : (LONG) timeLeft);
1277 lock.lock();
1278
1279 // the progress might have been uninitialized
1280 if (!isReady())
1281 break;
1282
1283 if (SUCCEEDED (rc))
1284 rc = checkProgress();
1285
1286 if (FAILED (rc))
1287 break;
1288
1289 if (!forever)
1290 {
1291 RTTimeNow (&time);
1292 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
1293 lastTime = RTTimeSpecGetMilli (&time);
1294 }
1295 }
1296
1297 if (FAILED (rc))
1298 return rc;
1299 }
1300
1301 LogFlowMember(("CombinedProgress::WaitForOperationCompletion: END\n"));
1302 return S_OK;
1303}
1304
1305STDMETHODIMP CombinedProgress::Cancel()
1306{
1307 AutoLock lock(this);
1308 CHECK_READY();
1309
1310 if (!mCancelable)
1311 return setError (E_FAIL, tr ("Operation cannot be cancelled"));
1312
1313/// @todo (dmik): implement operation cancellation!
1314// mCompleted = TRUE;
1315// mCanceled = TRUE;
1316// return S_OK;
1317
1318 return setError (E_NOTIMPL, ("Not implemented!"));
1319}
1320
1321// private methods
1322////////////////////////////////////////////////////////////////////////////////
1323
1324/**
1325 * Fetches the properties of the current progress object and, if it is
1326 * successfully completed, advances to the next uncompleted or unsucessfully
1327 * completed object in the vector of combined progress objects.
1328 *
1329 * @note Must be called from under the object's lock!
1330 */
1331HRESULT CombinedProgress::checkProgress()
1332{
1333 // do nothing if we're already marked ourselves as completed
1334 if (mCompleted)
1335 return S_OK;
1336
1337 ComAssertRet (mProgress < mProgresses.size(), E_FAIL);
1338
1339 ComPtr <IProgress> progress = mProgresses [mProgress];
1340 ComAssertRet (!progress.isNull(), E_FAIL);
1341
1342 HRESULT rc = S_OK;
1343 BOOL completed = FALSE;
1344
1345 do
1346 {
1347 rc = progress->COMGETTER(Completed) (&completed);
1348 if (FAILED (rc))
1349 return rc;
1350
1351 if (completed)
1352 {
1353 rc = progress->COMGETTER(Canceled) (&mCanceled);
1354 if (FAILED (rc))
1355 return rc;
1356
1357 rc = progress->COMGETTER(ResultCode) (&mResultCode);
1358 if (FAILED (rc))
1359 return rc;
1360
1361 if (FAILED (mResultCode))
1362 {
1363 rc = progress->COMGETTER(ErrorInfo) (mErrorInfo.asOutParam());
1364 if (FAILED (rc))
1365 return rc;
1366 }
1367
1368 if (FAILED (mResultCode) || mCanceled)
1369 {
1370 mCompleted = TRUE;
1371 }
1372 else
1373 {
1374 ULONG opCount = 0;
1375 rc = progress->COMGETTER(OperationCount) (&opCount);
1376 if (FAILED (rc))
1377 return rc;
1378
1379 mCompletedOperations += opCount;
1380 mProgress ++;
1381
1382 if (mProgress < mProgresses.size())
1383 progress = mProgresses [mProgress];
1384 else
1385 mCompleted = TRUE;
1386 }
1387 }
1388 }
1389 while (completed && !mCompleted);
1390
1391 rc = progress->COMGETTER(OperationPercent) (&mOperationPercent);
1392 if (SUCCEEDED (rc))
1393 {
1394 ULONG operation = 0;
1395 rc = progress->COMGETTER(Operation) (&operation);
1396 if (SUCCEEDED (rc) && mCompletedOperations + operation > mOperation)
1397 {
1398 mOperation = mCompletedOperations + operation;
1399 rc = progress->COMGETTER(OperationDescription) (
1400 mOperationDescription.asOutParam());
1401 }
1402 }
1403
1404 return rc;
1405}
1406
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