VirtualBox

source: vbox/trunk/src/VBox/Main/ProgressProxyImpl.cpp@ 29901

Last change on this file since 29901 was 29859, checked in by vboxsync, 15 years ago

Main: ProgressProxyImpl.cpp/h initial coding.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.3 KB
Line 
1/* $Id: ProgressProxyImpl.cpp 29859 2010-05-28 13:17:24Z vboxsync $ */
2/** @file
3 * IProgress implementation for Machine::openRemoteSession in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <iprt/types.h>
19
20#if defined (VBOX_WITH_XPCOM)
21#include <nsIServiceManager.h>
22#include <nsIExceptionService.h>
23#include <nsCOMPtr.h>
24#endif /* defined (VBOX_WITH_XPCOM) */
25
26#include "ProgressProxyImpl.h"
27
28#include "VirtualBoxImpl.h"
29#include "VirtualBoxErrorInfoImpl.h"
30
31#include "Logging.h"
32
33#include <iprt/time.h>
34#include <iprt/semaphore.h>
35
36#include <VBox/err.h>
37
38////////////////////////////////////////////////////////////////////////////////
39// ProgressProxy class
40////////////////////////////////////////////////////////////////////////////////
41
42// constructor / destructor / uninitializer
43////////////////////////////////////////////////////////////////////////////////
44
45
46HRESULT ProgressProxy::FinalConstruct()
47{
48 mcOtherProgressObjects = 1;
49 miCurOtherProgressObject = 0;
50
51 HRESULT rc = Progress::FinalConstruct();
52 return rc;
53}
54
55/**
56 * Initalize it as a one operation Progress object.
57 *
58 * This is used by SessionMachine::OnSessionEnd.
59 */
60HRESULT ProgressProxy::init(
61#if !defined (VBOX_COM_INPROC)
62 VirtualBox *pParent,
63#endif
64 IUnknown *pInitiator,
65 CBSTR bstrDescription,
66 BOOL fCancelable)
67{
68 miCurOtherProgressObject = 0;
69 mcOtherProgressObjects = 0;
70
71 return Progress::init(
72#if !defined (VBOX_COM_INPROC)
73 pParent,
74#endif
75 pInitiator,
76 bstrDescription,
77 fCancelable,
78 1 /* cOperations */,
79 1 /* ulTotalOperationsWeight */,
80 bstrDescription /* bstrFirstOperationDescription */,
81 1 /* ulFirstOperationWeight */,
82 NULL /* pId */);
83}
84
85/**
86 * Initialize for proxying one or more other objects, assuming we start out and
87 * end without proxying anyone.
88 *
89 * The user must call clearOtherProgressObject when there are no more progress
90 * objects to be proxied or we'll leave threads waiting forever.
91 */
92HRESULT ProgressProxy::init(
93#if !defined (VBOX_COM_INPROC)
94 VirtualBox *pParent,
95#endif
96 IUnknown *pInitiator,
97 CBSTR bstrDescription,
98 BOOL fCancelable,
99 ULONG cOtherProgressObjects,
100 ULONG uTotalOperationsWeight,
101 CBSTR bstrFirstOperationDescription,
102 ULONG uFirstOperationWeight,
103 OUT_GUID pId)
104{
105 miCurOtherProgressObject = 0;
106 mcOtherProgressObjects = cOtherProgressObjects;
107
108 return Progress::init(
109#if !defined (VBOX_COM_INPROC)
110 pParent,
111#endif
112 pInitiator,
113 bstrDescription,
114 fCancelable,
115 1 + cOtherProgressObjects + 1 /* cOperations */,
116 uTotalOperationsWeight,
117 bstrFirstOperationDescription,
118 uFirstOperationWeight,
119 pId);
120}
121
122void ProgressProxy::FinalRelease()
123{
124 uninit();
125 miCurOtherProgressObject = 0;
126}
127
128void ProgressProxy::uninit()
129{
130 LogFlowThisFunc(("\n"));
131
132 mptrOtherProgress.setNull();
133 Progress::uninit();
134}
135
136// Public methods
137////////////////////////////////////////////////////////////////////////////////
138
139/** Just a wrapper so we can automatically do the handover before setting
140 * the result locally. */
141HRESULT ProgressProxy::setResultCode(HRESULT aResultCode)
142{
143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
144 clearOtherProgressObjectInternal(true /* fEarly */);
145 return Progress::setResultCode(aResultCode);
146}
147
148/** Just a wrapper so we can automatically do the handover before setting
149 * the result locally. */
150HRESULT ProgressProxy::notifyComplete(HRESULT aResultCode)
151{
152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
153 clearOtherProgressObjectInternal(true /* fEarly */);
154 return Progress::notifyComplete(aResultCode);
155}
156
157/** Just a wrapper so we can automatically do the handover before setting
158 * the result locally. */
159HRESULT ProgressProxy::notifyComplete(HRESULT aResultCode,
160 const GUID &aIID,
161 const Bstr &aComponent,
162 const char *aText,
163 ...)
164{
165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
166 clearOtherProgressObjectInternal(true /* fEarly */);
167
168 va_list va;
169 va_start(va, aText);
170 HRESULT hrc = Progress::notifyCompleteV(aResultCode, aIID, aComponent, aText, va);
171 va_end(va);
172 return hrc;
173}
174
175/** Just a wrapper so we can automatically do the handover before setting
176 * the result locally. */
177bool ProgressProxy::notifyPointOfNoReturn(void)
178{
179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
180 clearOtherProgressObjectInternal(true /* fEarly */);
181 return Progress::notifyPointOfNoReturn();
182}
183
184/**
185 * Sets the other progress object unless the operation has been completed /
186 * canceled already.
187 *
188 * @returns false if failed/canceled, true if not.
189 * @param pOtherProgress The other progress object. Must not be NULL.
190 * @param uOperationWeight The weight of this operation. (The description
191 * is taken from the other progress object.)
192 */
193bool ProgressProxy::setOtherProgressObject(IProgress *pOtherProgress, ULONG uOperationWeight)
194{
195 LogFlowThisFunc(("setOtherProgressObject: %p %u\n", pOtherProgress, uOperationWeight));
196 ComPtr<IProgress> ptrOtherProgress = pOtherProgress;
197
198 /* Get the description first. */
199 Bstr bstrOperationDescription;
200 HRESULT hrc = pOtherProgress->COMGETTER(Description)(bstrOperationDescription.asOutParam());
201 if (FAILED(hrc))
202 bstrOperationDescription = "oops";
203
204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
205
206 /* Do the hand over from any previous progress object. */
207 clearOtherProgressObjectInternal(false /*fEarly*/);
208 BOOL fCompletedOrCanceled = mCompleted || mCanceled;
209 if (!fCompletedOrCanceled)
210 {
211 /* Advance to the next object and operation, checking for cancelation
212 and completion right away to be on the safe side. */
213 Assert(miCurOtherProgressObject < mcOtherProgressObjects);
214 mptrOtherProgress = ptrOtherProgress;
215
216 Progress::SetNextOperation(bstrOperationDescription, uOperationWeight);
217
218 BOOL f;
219 hrc = ptrOtherProgress->COMGETTER(Completed)(&f);
220 fCompletedOrCanceled = FAILED(hrc) || f;
221
222 if (!fCompletedOrCanceled)
223 {
224 hrc = ptrOtherProgress->COMGETTER(Canceled)(&f);
225 fCompletedOrCanceled = SUCCEEDED(hrc) && f;
226 }
227
228 if (fCompletedOrCanceled)
229 {
230 LogFlowThisFunc(("Other object completed or canceled, clearing...\n"));
231 clearOtherProgressObjectInternal(false /*fEarly*/);
232 }
233 else
234 {
235 /* Mirror the cancelable property. */
236 if (mCancelable)
237 {
238 hrc = ptrOtherProgress->COMGETTER(Cancelable)(&f);
239 if (SUCCEEDED(hrc) && !f)
240 {
241 LogFlowThisFunc(("The other progress object is not cancelable\n"));
242 mCancelable = FALSE;
243 }
244 }
245 }
246 }
247 else
248 {
249 LogFlowThisFunc(("mCompleted=%RTbool mCanceled=%RTbool - Canceling the other progress object!\n",
250 mCompleted, mCanceled));
251 hrc = ptrOtherProgress->Cancel();
252 LogFlowThisFunc(("Cancel -> %Rhrc", hrc));
253 }
254
255 LogFlowThisFunc(("Returns %RTbool\n", !fCompletedOrCanceled));
256 return !fCompletedOrCanceled;
257}
258
259/**
260 * Clears the last other progress objects.
261 *
262 * @returns false if failed/canceled, true if not.
263 * @param pszLastOperationDescription The description of the final bit.
264 * @param uLastOperationWeight The weight of the final bit.
265 */
266bool ProgressProxy::clearOtherProgressObject(const char *pszLastOperationDescription, ULONG uLastOperationWeight)
267{
268 LogFlowThisFunc(("%p %u\n", pszLastOperationDescription, uLastOperationWeight));
269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
270
271 clearOtherProgressObjectInternal(false /* fEarly */);
272
273 /* Advance to the next operation if applicable. */
274 bool fCompletedOrCanceled = mCompleted || mCanceled;
275 if (!fCompletedOrCanceled)
276 Progress::SetNextOperation(Bstr(pszLastOperationDescription), uLastOperationWeight);
277
278 LogFlowThisFunc(("Returns %RTbool\n", !fCompletedOrCanceled));
279 return !fCompletedOrCanceled;
280}
281
282// Internal methods.
283////////////////////////////////////////////////////////////////////////////////
284
285
286/**
287 * Internal version of clearOtherProgressObject that doesn't advance to the next
288 * operation.
289 *
290 * This is used both by clearOtherProgressObject as well as a number of places
291 * where we automatically do the hand over because of failure/completion.
292 *
293 * @param fEarly Early clearing or not.
294 */
295void ProgressProxy::clearOtherProgressObjectInternal(bool fEarly)
296{
297 if (!mptrOtherProgress.isNull())
298 {
299 ComPtr<IProgress> ptrOtherProgress = mptrOtherProgress;
300 mptrOtherProgress.setNull();
301 copyProgressInfo(ptrOtherProgress, fEarly);
302
303 miCurOtherProgressObject++;
304 Assert(miCurOtherProgressObject <= mcOtherProgressObjects);
305 }
306}
307
308/**
309 * Called to copy over the progress information from @a pOtherProgress.
310 *
311 * @param pOtherProgress The source of the information.
312 * @param fEarly Early copy.
313 *
314 * @note The caller owns the write lock and as cleared mptrOtherProgress
315 * already (or we might recurse forever)!
316 */
317void ProgressProxy::copyProgressInfo(IProgress *pOtherProgress, bool fEarly)
318{
319 HRESULT hrc;
320 LogFlowThisFunc(("\n"));
321
322 /*
323 * No point in doing this if the progress object was canceled already.
324 */
325 if (!mCanceled)
326 {
327 /* Detect if the other progress object was canceled. */
328 BOOL fCanceled;
329 hrc = pOtherProgress->COMGETTER(Canceled)(&fCanceled); AssertComRC(hrc);
330 if (FAILED(hrc))
331 fCanceled = FALSE;
332 if (fCanceled)
333 {
334 LogFlowThisFunc(("Canceled\n"));
335 mCanceled = TRUE;
336 if (m_pfnCancelCallback)
337 m_pfnCancelCallback(m_pvCancelUserArg);
338 }
339 else
340 {
341 /* Has it completed? */
342 BOOL fCompleted;
343 hrc = pOtherProgress->COMGETTER(Completed)(&fCompleted); AssertComRC(hrc);
344 if (FAILED(hrc))
345 fCompleted = TRUE;
346 Assert(fCompleted || fEarly);
347 if (fCompleted)
348 {
349 /* Check the result. */
350 LONG hrcResult;
351 hrc = pOtherProgress->COMGETTER(ResultCode)(&hrcResult); AssertComRC(hrc);
352 if (FAILED(hrc))
353 hrcResult = hrc;
354 if (SUCCEEDED((HRESULT)hrcResult))
355 LogFlowThisFunc(("Succeeded\n"));
356 else
357 {
358 /* Get the error information. */
359 ComPtr<IVirtualBoxErrorInfo> ptrErrorInfo;
360 hrc = pOtherProgress->COMGETTER(ErrorInfo)(ptrErrorInfo.asOutParam());
361 if (SUCCEEDED(hrc))
362 {
363 Bstr bstrIID;
364 hrc = ptrErrorInfo->COMGETTER(InterfaceID)(bstrIID.asOutParam());
365 if (FAILED(hrc))
366 bstrIID.setNull();
367
368 Bstr bstrComponent;
369 hrc = ptrErrorInfo->COMGETTER(Component)(bstrComponent.asOutParam());
370 if (FAILED(hrc))
371 bstrComponent = "failed";
372
373 Bstr bstrText;
374 hrc = ptrErrorInfo->COMGETTER(Text)(bstrText.asOutParam());
375 if (FAILED(hrc))
376 bstrText = "<failed>";
377
378 Utf8Str strText(bstrText);
379 LogFlowThisFunc(("Got ErrorInfo(%s); hrcResult=%Rhrc\n", strText.c_str(), hrcResult));
380 Progress::notifyComplete((HRESULT)hrcResult, Guid(bstrIID), bstrComponent, "%s", strText.c_str());
381 }
382 else
383 {
384 LogFlowThisFunc(("ErrorInfo failed with hrc=%Rhrc; hrcResult=%Rhrc\n", hrc, hrcResult));
385 Progress::notifyComplete((HRESULT)hrcResult);
386 }
387 }
388 }
389 else
390 LogFlowThisFunc(("Not completed\n"));
391 }
392 }
393 else
394 LogFlowThisFunc(("Already canceled\n"));
395
396 /*
397 * Did cancelable state change (point of no return)?
398 */
399 if (mCancelable)
400 {
401 BOOL fCancelable;
402 hrc = pOtherProgress->COMGETTER(Cancelable)(&fCancelable); AssertComRC(hrc);
403 if (SUCCEEDED(hrc) && !fCancelable)
404 {
405 LogFlowThisFunc(("point-of-no-return reached\n"));
406 mCancelable = FALSE;
407 }
408 }
409}
410
411
412// IProgress properties
413////////////////////////////////////////////////////////////////////////////////
414
415STDMETHODIMP ProgressProxy::COMGETTER(Percent)(ULONG *aPercent)
416{
417#if 0
418 CheckComArgOutPointerValid(aPercent);
419
420 AutoCaller autoCaller(this);
421 HRESULT hrc = autoCaller.rc();
422 if (SUCCEEDED(rc))
423 {
424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
425
426 if (mptrOtherProgress.isNull())
427 hrc = Progress::COMGETTER(Percent)(aPercent);
428 else
429 {
430 ULONG uPct;
431 hrc = mptrOtherProgress->COMGETTER(Percent)(&uPct);
432 ....
433 }
434 }
435 return hrc;
436#else
437 return Progress::COMGETTER(Percent)(aPercent);
438#endif
439}
440
441STDMETHODIMP ProgressProxy::COMGETTER(Completed)(BOOL *aCompleted)
442{
443 /* Not proxied since we EXPECT a hand back call. */
444 return Progress::COMGETTER(Completed)(aCompleted);
445}
446
447STDMETHODIMP ProgressProxy::COMGETTER(Canceled)(BOOL *aCanceled)
448{
449 CheckComArgOutPointerValid(aCanceled);
450
451 AutoCaller autoCaller(this);
452 HRESULT hrc = autoCaller.rc();
453 if (SUCCEEDED(hrc))
454 {
455 /* Check the local data first, then the other object. */
456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
457 hrc = Progress::COMGETTER(Canceled)(aCanceled);
458 if ( SUCCEEDED(hrc)
459 && !*aCanceled
460 && !mptrOtherProgress.isNull())
461 {
462 hrc = mptrOtherProgress->COMGETTER(Canceled)(aCanceled);
463 if (SUCCEEDED(hrc) && *aCanceled)
464 clearOtherProgressObjectInternal(true /*fEarly*/);
465 }
466 }
467 return hrc;
468}
469
470STDMETHODIMP ProgressProxy::COMGETTER(ResultCode)(LONG *aResultCode)
471{
472 /* Not proxied yet since we EXPECT a hand back call. */
473 return Progress::COMGETTER(ResultCode)(aResultCode);
474}
475
476STDMETHODIMP ProgressProxy::COMGETTER(ErrorInfo)(IVirtualBoxErrorInfo **aErrorInfo)
477{
478 /* Not proxied yet since we EXPECT a hand back call. */
479 return Progress::COMGETTER(ErrorInfo)(aErrorInfo);
480}
481
482STDMETHODIMP ProgressProxy::COMGETTER(OperationPercent)(ULONG *aOperationPercent)
483{
484 /* Not proxied, should be proxied later on. */
485 return Progress::COMGETTER(OperationPercent)(aOperationPercent);
486}
487
488STDMETHODIMP ProgressProxy::COMSETTER(Timeout)(ULONG aTimeout)
489{
490 /* Not currently supported. */
491 NOREF(aTimeout);
492 AssertFailed();
493 return E_NOTIMPL;
494}
495
496STDMETHODIMP ProgressProxy::COMGETTER(Timeout)(ULONG *aTimeout)
497{
498 /* Not currently supported. */
499 CheckComArgOutPointerValid(aTimeout);
500
501 AssertFailed();
502 return E_NOTIMPL;
503}
504
505// IProgress methods
506/////////////////////////////////////////////////////////////////////////////
507
508STDMETHODIMP ProgressProxy::WaitForCompletion(LONG aTimeout)
509{
510 HRESULT hrc;
511 LogFlowThisFuncEnter();
512 LogFlowThisFunc(("aTimeout=%d\n", aTimeout));
513
514 /* For now we'll always block locally. */
515 hrc = Progress::WaitForCompletion(aTimeout);
516
517 LogFlowThisFuncLeave();
518 return hrc;
519}
520
521STDMETHODIMP ProgressProxy::WaitForOperationCompletion(ULONG aOperation, LONG aTimeout)
522{
523 HRESULT hrc;
524 LogFlowThisFuncEnter();
525 LogFlowThisFunc(("aOperation=%d aTimeout=%d\n", aOperation, aTimeout));
526
527 /* For now we'll always block locally. Later though, we could consider
528 blocking remotely when we can. */
529 hrc = Progress::WaitForOperationCompletion(aOperation, aTimeout);
530
531 LogFlowThisFuncLeave();
532 return hrc;
533}
534
535STDMETHODIMP ProgressProxy::Cancel()
536{
537 LogFlowThisFunc(("\n"));
538 AutoCaller autoCaller(this);
539 HRESULT hrc = autoCaller.rc();
540 if (SUCCEEDED(hrc))
541 {
542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
543 if (mCancelable)
544 {
545 if (!mptrOtherProgress.isNull())
546 {
547 hrc = mptrOtherProgress->Cancel();
548 if (SUCCEEDED(hrc))
549 {
550 if (m_pfnCancelCallback)
551 m_pfnCancelCallback(m_pvCancelUserArg);
552 clearOtherProgressObjectInternal(true /*fEarly*/);
553 }
554 }
555 else
556 hrc = Progress::Cancel();
557 }
558 else
559 hrc = setError(E_FAIL, tr("Operation cannot be canceled"));
560 }
561
562 LogFlowThisFunc(("returns %Rhrc\n", hrc));
563 return hrc;
564}
565
566STDMETHODIMP ProgressProxy::SetCurrentOperationProgress(ULONG aPercent)
567{
568 /* Not supported - why do we actually expose this? */
569 NOREF(aPercent);
570 return E_NOTIMPL;
571}
572
573STDMETHODIMP ProgressProxy::SetNextOperation(IN_BSTR bstrNextOperationDescription, ULONG ulNextOperationsWeight)
574{
575 /* Not supported - why do we actually expose this? */
576 NOREF(bstrNextOperationDescription);
577 NOREF(ulNextOperationsWeight);
578 return E_NOTIMPL;
579}
580
581/* vi: set tabstop=4 shiftwidth=4 expandtab: */
582
Note: See TracBrowser for help on using the repository browser.

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