VirtualBox

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

Last change on this file since 16560 was 15165, checked in by vboxsync, 16 years ago

#3285: Improve error handling API to include unique error numbers
Document

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

© 2023 Oracle
ContactPrivacy policyTerms of Use