VirtualBox

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

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

Performance API enabled for Linux, Solaris and Windows.
POSIX timers enabled for Linux and Solaris.

  • 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.215389.xyz. 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.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette