VirtualBox

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

Last change on this file since 7554 was 6935, checked in by vboxsync, 17 years ago

Main: Changed 'defined (RT_OS_WINDOWS)' => '!defined (VBOX_WITH_XPCOM)' in relevant places, for clarity (not XPCOM is possible only on Windows so far).

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