VirtualBox

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

Last change on this file since 2676 was 357, checked in by vboxsync, 17 years ago

Main: Added VirtualBoxSupportErrorInfoImpl::setErrorBstr() and Progress::notifyCompleteBstr() to avoid an extra Utf8Str -> Bstr conversion.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use