VirtualBox

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

Last change on this file since 13762 was 13580, checked in by vboxsync, 16 years ago

Ported s2 branch (r37120:38456).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use