VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/VirtualBoxClientImpl.cpp@ 47908

Last change on this file since 47908 was 44970, checked in by vboxsync, 12 years ago

Main/VirtualBoxClient: add method to perform VM error checking which simplifies client code
com/ErrorInfo: small bugfix (the object where the error information originated was always lost), added a way to inject whole error object structures which needed the bugfix
Frontends/VBoxSDL: sample code how to use the new method

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.4 KB
Line 
1/* $Id: VirtualBoxClientImpl.cpp 44970 2013-03-11 09:59:05Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2010-2013 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 "VirtualBoxClientImpl.h"
19
20#include "AutoCaller.h"
21#include "VBoxEvents.h"
22#include "Logging.h"
23#include "VBox/com/ErrorInfo.h"
24
25#include <iprt/asm.h>
26#include <iprt/thread.h>
27#include <iprt/critsect.h>
28#include <iprt/semaphore.h>
29#include <iprt/cpp/utils.h>
30
31
32/** Waiting time between probing whether VBoxSVC is alive. */
33#define VBOXCLIENT_DEFAULT_INTERVAL 30000
34
35
36/** Initialize instance counter class variable */
37uint32_t VirtualBoxClient::g_cInstances = 0;
38
39
40// constructor / destructor
41/////////////////////////////////////////////////////////////////////////////
42
43HRESULT VirtualBoxClient::FinalConstruct()
44{
45 HRESULT rc = init();
46 BaseFinalConstruct();
47 return rc;
48}
49
50void VirtualBoxClient::FinalRelease()
51{
52 uninit();
53 BaseFinalRelease();
54}
55
56// public initializer/uninitializer for internal purposes only
57/////////////////////////////////////////////////////////////////////////////
58
59/**
60 * Initializes the VirtualBoxClient object.
61 *
62 * @returns COM result indicator
63 */
64HRESULT VirtualBoxClient::init()
65{
66 LogFlowThisFunc(("\n"));
67
68 HRESULT rc;
69 /* Enclose the state transition NotReady->InInit->Ready */
70 AutoInitSpan autoInitSpan(this);
71 AssertReturn(autoInitSpan.isOk(), E_FAIL);
72
73 mData.m_ThreadWatcher = NIL_RTTHREAD;
74 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
75
76 if (ASMAtomicIncU32(&g_cInstances) != 1)
77 AssertFailedReturn(E_FAIL);
78
79 rc = mData.m_pVirtualBox.createLocalObject(CLSID_VirtualBox);
80 AssertComRCReturnRC(rc);
81
82 rc = unconst(mData.m_pEventSource).createObject();
83 AssertComRCReturnRC(rc);
84 rc = mData.m_pEventSource->init(static_cast<IVirtualBoxClient *>(this));
85 AssertComRCReturnRC(rc);
86
87 /* Setting up the VBoxSVC watcher thread. If anything goes wrong here it
88 * is not considered important enough to cause any sort of visible
89 * failure. The monitoring will not be done, but that's all. */
90 int vrc = RTSemEventCreate(&mData.m_SemEvWatcher);
91 AssertRC(vrc);
92 if (RT_SUCCESS(vrc))
93 {
94 vrc = RTThreadCreate(&mData.m_ThreadWatcher, SVCWatcherThread,
95 this, 0, RTTHREADTYPE_INFREQUENT_POLLER,
96 RTTHREADFLAGS_WAITABLE, "VBoxSVCWatcher");
97 AssertRC(vrc);
98 }
99 else
100 {
101 RTSemEventDestroy(mData.m_SemEvWatcher);
102 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
103 }
104
105 /* Confirm a successful initialization */
106 autoInitSpan.setSucceeded();
107
108 return rc;
109}
110
111/**
112 * Uninitializes the instance and sets the ready flag to FALSE.
113 * Called either from FinalRelease() or by the parent when it gets destroyed.
114 */
115void VirtualBoxClient::uninit()
116{
117 LogFlowThisFunc(("\n"));
118
119 /* Enclose the state transition Ready->InUninit->NotReady */
120 AutoUninitSpan autoUninitSpan(this);
121 if (autoUninitSpan.uninitDone())
122 return;
123
124 if (mData.m_ThreadWatcher != NIL_RTTHREAD)
125 {
126 /* Signal the event semaphore and wait for the thread to terminate.
127 * if it hangs for some reason exit anyway, this can cause a crash
128 * though as the object will no longer be available. */
129 RTSemEventSignal(mData.m_SemEvWatcher);
130 RTThreadWait(mData.m_ThreadWatcher, 30000, NULL);
131 mData.m_ThreadWatcher = NIL_RTTHREAD;
132 RTSemEventDestroy(mData.m_SemEvWatcher);
133 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
134 }
135
136 mData.m_pVirtualBox.setNull();
137
138 ASMAtomicDecU32(&g_cInstances);
139}
140
141// IVirtualBoxClient properties
142/////////////////////////////////////////////////////////////////////////////
143
144/**
145 * Returns a reference to the VirtualBox object.
146 *
147 * @returns COM status code
148 * @param aVirtualBox Address of result variable.
149 */
150STDMETHODIMP VirtualBoxClient::COMGETTER(VirtualBox)(IVirtualBox **aVirtualBox)
151{
152 CheckComArgOutPointerValid(aVirtualBox);
153
154 AutoCaller autoCaller(this);
155 if (FAILED(autoCaller.rc())) return autoCaller.rc();
156
157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
158 mData.m_pVirtualBox.queryInterfaceTo(aVirtualBox);
159 return S_OK;
160}
161
162/**
163 * Create a new Session object and return a reference to it.
164 *
165 * @returns COM status code
166 * @param aSession Address of result variable.
167 */
168STDMETHODIMP VirtualBoxClient::COMGETTER(Session)(ISession **aSession)
169{
170 HRESULT rc;
171 CheckComArgOutPointerValid(aSession);
172
173 AutoCaller autoCaller(this);
174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
175
176 /* this is not stored in this object, no need to lock */
177 ComPtr<ISession> pSession;
178 rc = pSession.createInprocObject(CLSID_Session);
179 if (SUCCEEDED(rc))
180 pSession.queryInterfaceTo(aSession);
181
182 return rc;
183}
184
185/**
186 * Return reference to the EventSource associated with this object.
187 *
188 * @returns COM status code
189 * @param aEventSource Address of result variable.
190 */
191STDMETHODIMP VirtualBoxClient::COMGETTER(EventSource)(IEventSource **aEventSource)
192{
193 CheckComArgOutPointerValid(aEventSource);
194
195 AutoCaller autoCaller(this);
196 if (FAILED(autoCaller.rc())) return autoCaller.rc();
197
198 /* this is const, no need to lock */
199 mData.m_pEventSource.queryInterfaceTo(aEventSource);
200
201 return mData.m_pEventSource.isNull() ? E_FAIL : S_OK;
202}
203
204/**
205 * Checks a Machine object for any pending errors.
206 *
207 * @returns COM status code
208 * @param aMachine Machine object to check.
209 */
210STDMETHODIMP VirtualBoxClient::CheckMachineError(IMachine *aMachine)
211{
212 HRESULT rc;
213 CheckComArgNotNull(aMachine);
214
215 BOOL fAccessible = FALSE;
216 rc = aMachine->COMGETTER(Accessible)(&fAccessible);
217 if (FAILED(rc))
218 return setError(rc, tr("Could not check the accessibility status of the VM"));
219 else if (!fAccessible)
220 {
221 ComPtr<IVirtualBoxErrorInfo> pAccessError;
222 rc = aMachine->COMGETTER(AccessError)(pAccessError.asOutParam());
223 if (FAILED(rc))
224 return setError(rc, tr("Could not get the access error message of the VM"));
225 else
226 {
227 ErrorInfo info(pAccessError);
228 ErrorInfoKeeper eik(info);
229 return info.getResultCode();
230 }
231 }
232 return S_OK;
233}
234
235// private methods
236/////////////////////////////////////////////////////////////////////////////
237
238/*static*/
239DECLCALLBACK(int) VirtualBoxClient::SVCWatcherThread(RTTHREAD ThreadSelf,
240 void *pvUser)
241{
242 NOREF(ThreadSelf);
243 Assert(pvUser);
244 VirtualBoxClient *pThis = (VirtualBoxClient *)pvUser;
245 RTSEMEVENT sem = pThis->mData.m_SemEvWatcher;
246 RTMSINTERVAL cMillies = VBOXCLIENT_DEFAULT_INTERVAL;
247 int vrc;
248
249 /* The likelihood of early crashes are high, so start with a short wait. */
250 vrc = RTSemEventWait(sem, cMillies / 2);
251
252 /* As long as the waiting times out keep retrying the wait. */
253 while (RT_FAILURE(vrc))
254 {
255 {
256 HRESULT rc = S_OK;
257 ComPtr<IVirtualBox> pV;
258 {
259 AutoReadLock alock(pThis COMMA_LOCKVAL_SRC_POS);
260 pV = pThis->mData.m_pVirtualBox;
261 }
262 if (!pV.isNull())
263 {
264 ULONG rev;
265 rc = pV->COMGETTER(Revision)(&rev);
266 if (FAILED_DEAD_INTERFACE(rc))
267 {
268 LogRel(("VirtualBoxClient: detected unresponsive VBoxSVC (rc=%Rhrc)\n", rc));
269 {
270 AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
271 /* Throw away the VirtualBox reference, it's no longer
272 * usable as VBoxSVC terminated in the mean time. */
273 pThis->mData.m_pVirtualBox.setNull();
274 }
275 fireVBoxSVCAvailabilityChangedEvent(pThis->mData.m_pEventSource, FALSE);
276 }
277 }
278 else
279 {
280 /* Try to get a new VirtualBox reference straight away, and if
281 * this fails use an increased waiting time as very frequent
282 * restart attempts in some wedged config can cause high CPU
283 * and disk load. */
284 ComPtr<IVirtualBox> pVBox;
285 rc = pVBox.createLocalObject(CLSID_VirtualBox);
286 if (FAILED(rc))
287 cMillies = 3 * VBOXCLIENT_DEFAULT_INTERVAL;
288 else
289 {
290 LogRel(("VirtualBoxClient: detected working VBoxSVC (rc=%Rhrc)\n", rc));
291 {
292 AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
293 /* Update the VirtualBox reference, there's a working
294 * VBoxSVC again from now on. */
295 pThis->mData.m_pVirtualBox = pVBox;
296 }
297 fireVBoxSVCAvailabilityChangedEvent(pThis->mData.m_pEventSource, TRUE);
298 cMillies = VBOXCLIENT_DEFAULT_INTERVAL;
299 }
300 }
301 }
302 vrc = RTSemEventWait(sem, cMillies);
303 }
304 return 0;
305}
306
307/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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