VirtualBox

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

Last change on this file since 63639 was 63639, checked in by vboxsync, 9 years ago

VBoxC: Looks like VirtualBoxClient::Data::m_pEventSource is holding in extra reference on the ATL module, causing VBoxC to be unloaded too late because DllCanUnloadNow() returns S_FALSE due to GetLockCount() being 1 instead of 0. Created a hack for discounting the module lock held by the EventSource in the VirtualBoxClient singelton.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.4 KB
Line 
1/* $Id: VirtualBoxClientImpl.cpp 63639 2016-08-25 14:31:10Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2010-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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
39LONG VirtualBoxClient::s_cUnnecessaryAtlModuleLocks = 0;
40
41// constructor / destructor
42/////////////////////////////////////////////////////////////////////////////
43
44HRESULT VirtualBoxClient::FinalConstruct()
45{
46 HRESULT rc = init();
47 BaseFinalConstruct();
48 return rc;
49}
50
51void VirtualBoxClient::FinalRelease()
52{
53 uninit();
54 BaseFinalRelease();
55}
56
57// public initializer/uninitializer for internal purposes only
58/////////////////////////////////////////////////////////////////////////////
59
60/**
61 * Initializes the VirtualBoxClient object.
62 *
63 * @returns COM result indicator
64 */
65HRESULT VirtualBoxClient::init()
66{
67 LogFlowThisFuncEnter();
68
69 /* Enclose the state transition NotReady->InInit->Ready */
70 AutoInitSpan autoInitSpan(this);
71 AssertReturn(autoInitSpan.isOk(), E_FAIL);
72
73 /* Important: DO NOT USE any kind of "early return" (except the single
74 * one above, checking the init span success) in this method. It is vital
75 * for correct error handling that it has only one point of return, which
76 * does all the magic on COM to signal object creation success and
77 * reporting the error later for every API method. COM translates any
78 * unsuccessful object creation to REGDB_E_CLASSNOTREG errors or similar
79 * unhelpful ones which cause us a lot of grief with troubleshooting. */
80
81 HRESULT rc = S_OK;
82 try
83 {
84 if (ASMAtomicIncU32(&g_cInstances) != 1)
85 AssertFailedStmt(throw setError(E_FAIL,
86 tr("Attempted to create more than one VirtualBoxClient instance")));
87
88 mData.m_ThreadWatcher = NIL_RTTHREAD;
89 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
90
91 rc = mData.m_pVirtualBox.createLocalObject(CLSID_VirtualBox);
92 if (FAILED(rc))
93 throw rc;
94
95 /* VirtualBox error return is postponed to method calls, fetch it. */
96 ULONG rev;
97 rc = mData.m_pVirtualBox->COMGETTER(Revision)(&rev);
98 if (FAILED(rc))
99 throw rc;
100
101 rc = unconst(mData.m_pEventSource).createObject();
102 AssertComRCThrow(rc, setError(rc,
103 tr("Could not create EventSource for VirtualBoxClient")));
104 rc = mData.m_pEventSource->init();
105 AssertComRCThrow(rc, setError(rc,
106 tr("Could not initialize EventSource for VirtualBoxClient")));
107
108 /* HACK ALERT! This is for DllCanUnloadNow(). */
109 s_cUnnecessaryAtlModuleLocks++;
110 AssertMsg(s_cUnnecessaryAtlModuleLocks == 1, ("%d\n", s_cUnnecessaryAtlModuleLocks));
111
112 /* Setting up the VBoxSVC watcher thread. If anything goes wrong here it
113 * is not considered important enough to cause any sort of visible
114 * failure. The monitoring will not be done, but that's all. */
115 int vrc = RTSemEventCreate(&mData.m_SemEvWatcher);
116 if (RT_FAILURE(vrc))
117 {
118 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
119 AssertRCStmt(vrc, throw setError(VBOX_E_IPRT_ERROR,
120 tr("Failed to create semaphore (rc=%Rrc)"),
121 vrc));
122 }
123
124 vrc = RTThreadCreate(&mData.m_ThreadWatcher, SVCWatcherThread, this, 0,
125 RTTHREADTYPE_INFREQUENT_POLLER, RTTHREADFLAGS_WAITABLE, "VBoxSVCWatcher");
126 if (RT_FAILURE(vrc))
127 {
128 RTSemEventDestroy(mData.m_SemEvWatcher);
129 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
130 AssertRCStmt(vrc, throw setError(VBOX_E_IPRT_ERROR,
131 tr("Failed to create watcher thread (rc=%Rrc)"),
132 vrc));
133 }
134 }
135 catch (HRESULT err)
136 {
137 /* we assume that error info is set by the thrower */
138 rc = err;
139 }
140 catch (...)
141 {
142 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
143 }
144
145 /* Confirm a successful initialization when it's the case. Must be last,
146 * as on failure it will uninitialize the object. */
147 if (SUCCEEDED(rc))
148 autoInitSpan.setSucceeded();
149 else
150 autoInitSpan.setFailed(rc);
151
152 LogFlowThisFunc(("rc=%Rhrc\n", rc));
153 LogFlowThisFuncLeave();
154 /* Unconditionally return success, because the error return is delayed to
155 * the attribute/method calls through the InitFailed object state. */
156 return S_OK;
157}
158
159/**
160 * Uninitializes the instance and sets the ready flag to FALSE.
161 * Called either from FinalRelease() or by the parent when it gets destroyed.
162 */
163void VirtualBoxClient::uninit()
164{
165 LogFlowThisFunc(("\n"));
166
167 /* Enclose the state transition Ready->InUninit->NotReady */
168 AutoUninitSpan autoUninitSpan(this);
169 if (autoUninitSpan.uninitDone())
170 return;
171
172 if (mData.m_ThreadWatcher != NIL_RTTHREAD)
173 {
174 /* Signal the event semaphore and wait for the thread to terminate.
175 * if it hangs for some reason exit anyway, this can cause a crash
176 * though as the object will no longer be available. */
177 RTSemEventSignal(mData.m_SemEvWatcher);
178 RTThreadWait(mData.m_ThreadWatcher, 30000, NULL);
179 mData.m_ThreadWatcher = NIL_RTTHREAD;
180 RTSemEventDestroy(mData.m_SemEvWatcher);
181 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
182 }
183
184 mData.m_pVirtualBox.setNull();
185
186 ASMAtomicDecU32(&g_cInstances);
187}
188
189// IVirtualBoxClient properties
190/////////////////////////////////////////////////////////////////////////////
191
192/**
193 * Returns a reference to the VirtualBox object.
194 *
195 * @returns COM status code
196 * @param aVirtualBox Address of result variable.
197 */
198HRESULT VirtualBoxClient::getVirtualBox(ComPtr<IVirtualBox> &aVirtualBox)
199{
200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
201 aVirtualBox = mData.m_pVirtualBox;
202 return S_OK;
203}
204
205/**
206 * Create a new Session object and return a reference to it.
207 *
208 * @returns COM status code
209 * @param aSession Address of result variable.
210 */
211HRESULT VirtualBoxClient::getSession(ComPtr<ISession> &aSession)
212{
213 /* this is not stored in this object, no need to lock */
214 ComPtr<ISession> pSession;
215 HRESULT rc = pSession.createInprocObject(CLSID_Session);
216 if (SUCCEEDED(rc))
217 aSession = pSession;
218 return rc;
219}
220
221/**
222 * Return reference to the EventSource associated with this object.
223 *
224 * @returns COM status code
225 * @param aEventSource Address of result variable.
226 */
227HRESULT VirtualBoxClient::getEventSource(ComPtr<IEventSource> &aEventSource)
228{
229 /* this is const, no need to lock */
230 aEventSource = mData.m_pEventSource;
231 return aEventSource.isNull() ? E_FAIL : S_OK;
232}
233
234// IVirtualBoxClient methods
235/////////////////////////////////////////////////////////////////////////////
236
237/**
238 * Checks a Machine object for any pending errors.
239 *
240 * @returns COM status code
241 * @param aMachine Machine object to check.
242 */
243HRESULT VirtualBoxClient::checkMachineError(const ComPtr<IMachine> &aMachine)
244{
245 BOOL fAccessible = FALSE;
246 HRESULT rc = aMachine->COMGETTER(Accessible)(&fAccessible);
247 if (FAILED(rc))
248 return setError(rc, tr("Could not check the accessibility status of the VM"));
249 else if (!fAccessible)
250 {
251 ComPtr<IVirtualBoxErrorInfo> pAccessError;
252 rc = aMachine->COMGETTER(AccessError)(pAccessError.asOutParam());
253 if (FAILED(rc))
254 return setError(rc, tr("Could not get the access error message of the VM"));
255 else
256 {
257 ErrorInfo info(pAccessError);
258 ErrorInfoKeeper eik(info);
259 return info.getResultCode();
260 }
261 }
262 return S_OK;
263}
264
265// private methods
266/////////////////////////////////////////////////////////////////////////////
267
268/*static*/
269DECLCALLBACK(int) VirtualBoxClient::SVCWatcherThread(RTTHREAD ThreadSelf,
270 void *pvUser)
271{
272 NOREF(ThreadSelf);
273 Assert(pvUser);
274 VirtualBoxClient *pThis = (VirtualBoxClient *)pvUser;
275 RTSEMEVENT sem = pThis->mData.m_SemEvWatcher;
276 RTMSINTERVAL cMillies = VBOXCLIENT_DEFAULT_INTERVAL;
277 int vrc;
278
279 /* The likelihood of early crashes are high, so start with a short wait. */
280 vrc = RTSemEventWait(sem, cMillies / 2);
281
282 /* As long as the waiting times out keep retrying the wait. */
283 while (RT_FAILURE(vrc))
284 {
285 {
286 HRESULT rc = S_OK;
287 ComPtr<IVirtualBox> pV;
288 {
289 AutoReadLock alock(pThis COMMA_LOCKVAL_SRC_POS);
290 pV = pThis->mData.m_pVirtualBox;
291 }
292 if (!pV.isNull())
293 {
294 ULONG rev;
295 rc = pV->COMGETTER(Revision)(&rev);
296 if (FAILED_DEAD_INTERFACE(rc))
297 {
298 LogRel(("VirtualBoxClient: detected unresponsive VBoxSVC (rc=%Rhrc)\n", rc));
299 {
300 AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
301 /* Throw away the VirtualBox reference, it's no longer
302 * usable as VBoxSVC terminated in the mean time. */
303 pThis->mData.m_pVirtualBox.setNull();
304 }
305 fireVBoxSVCAvailabilityChangedEvent(pThis->mData.m_pEventSource, FALSE);
306 }
307 }
308 else
309 {
310 /* Try to get a new VirtualBox reference straight away, and if
311 * this fails use an increased waiting time as very frequent
312 * restart attempts in some wedged config can cause high CPU
313 * and disk load. */
314 ComPtr<IVirtualBox> pVirtualBox;
315 rc = pVirtualBox.createLocalObject(CLSID_VirtualBox);
316 if (FAILED(rc))
317 cMillies = 3 * VBOXCLIENT_DEFAULT_INTERVAL;
318 else
319 {
320 LogRel(("VirtualBoxClient: detected working VBoxSVC (rc=%Rhrc)\n", rc));
321 {
322 AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
323 /* Update the VirtualBox reference, there's a working
324 * VBoxSVC again from now on. */
325 pThis->mData.m_pVirtualBox = pVirtualBox;
326 }
327 fireVBoxSVCAvailabilityChangedEvent(pThis->mData.m_pEventSource, TRUE);
328 cMillies = VBOXCLIENT_DEFAULT_INTERVAL;
329 }
330 }
331 }
332 vrc = RTSemEventWait(sem, cMillies);
333 }
334 return 0;
335}
336
337/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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