VirtualBox

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

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

stdint.h & C++ fun.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use