VirtualBox

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

Last change on this file since 7992 was 7992, checked in by vboxsync, 16 years ago

Main: Implemented true AutoReaderLock (#2768).

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