VirtualBox

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

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

Main: Made it build on OS/2.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use