VirtualBox

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

Last change on this file since 8486 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

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