VirtualBox

source: vbox/trunk/src/VBox/Main/glue/initterm.cpp@ 57929

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

Main/glue: fix a buglet (don't know how it gets triggered) in the com::Shutdown code path for non-main threads on XPCOM

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.8 KB
Line 
1/* $Id: initterm.cpp 55451 2015-04-27 13:34:49Z vboxsync $ */
2
3/** @file
4 * MS COM / XPCOM Abstraction Layer - Initialization and Termination.
5 */
6
7/*
8 * Copyright (C) 2006-2014 Oracle Corporation
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
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.
17 */
18
19#if !defined(VBOX_WITH_XPCOM)
20
21# include <objbase.h>
22
23#else /* !defined(VBOX_WITH_XPCOM) */
24
25# include <stdlib.h>
26
27# include <nsIComponentRegistrar.h>
28# include <nsIServiceManager.h>
29# include <nsCOMPtr.h>
30# include <nsEventQueueUtils.h>
31# include <nsEmbedString.h>
32
33# include <nsILocalFile.h>
34# include <nsIDirectoryService.h>
35# include <nsDirectoryServiceDefs.h>
36
37#endif /* !defined(VBOX_WITH_XPCOM) */
38
39#include "VBox/com/com.h"
40#include "VBox/com/assert.h"
41#include "VBox/com/NativeEventQueue.h"
42#include "VBox/com/AutoLock.h"
43
44#include "../include/Logging.h"
45
46#include <iprt/asm.h>
47#include <iprt/env.h>
48#include <iprt/param.h>
49#include <iprt/path.h>
50#include <iprt/string.h>
51#include <iprt/thread.h>
52
53#include <VBox/err.h>
54
55namespace com
56{
57
58#if defined(VBOX_WITH_XPCOM)
59
60class DirectoryServiceProvider : public nsIDirectoryServiceProvider
61{
62public:
63
64 NS_DECL_ISUPPORTS
65
66 DirectoryServiceProvider()
67 : mCompRegLocation(NULL), mXPTIDatLocation(NULL)
68 , mComponentDirLocation(NULL), mCurrProcDirLocation(NULL)
69 {}
70
71 virtual ~DirectoryServiceProvider();
72
73 HRESULT init(const char *aCompRegLocation,
74 const char *aXPTIDatLocation,
75 const char *aComponentDirLocation,
76 const char *aCurrProcDirLocation);
77
78 NS_DECL_NSIDIRECTORYSERVICEPROVIDER
79
80private:
81 /** @remarks This is not a UTF-8 string. */
82 char *mCompRegLocation;
83 /** @remarks This is not a UTF-8 string. */
84 char *mXPTIDatLocation;
85 /** @remarks This is not a UTF-8 string. */
86 char *mComponentDirLocation;
87 /** @remarks This is not a UTF-8 string. */
88 char *mCurrProcDirLocation;
89};
90
91NS_IMPL_ISUPPORTS1(DirectoryServiceProvider, nsIDirectoryServiceProvider)
92
93DirectoryServiceProvider::~DirectoryServiceProvider()
94{
95 if (mCompRegLocation)
96 {
97 RTStrFree(mCompRegLocation);
98 mCompRegLocation = NULL;
99 }
100 if (mXPTIDatLocation)
101 {
102 RTStrFree(mXPTIDatLocation);
103 mXPTIDatLocation = NULL;
104 }
105 if (mComponentDirLocation)
106 {
107 RTStrFree(mComponentDirLocation);
108 mComponentDirLocation = NULL;
109 }
110 if (mCurrProcDirLocation)
111 {
112 RTStrFree(mCurrProcDirLocation);
113 mCurrProcDirLocation = NULL;
114 }
115}
116
117/**
118 * @param aCompRegLocation Path to compreg.dat, in Utf8.
119 * @param aXPTIDatLocation Path to xpti.data, in Utf8.
120 */
121HRESULT
122DirectoryServiceProvider::init(const char *aCompRegLocation,
123 const char *aXPTIDatLocation,
124 const char *aComponentDirLocation,
125 const char *aCurrProcDirLocation)
126{
127 AssertReturn(aCompRegLocation, NS_ERROR_INVALID_ARG);
128 AssertReturn(aXPTIDatLocation, NS_ERROR_INVALID_ARG);
129
130/** @todo r=bird: Gotta check how this encoding stuff plays out on darwin!
131 * We get down to [VBoxNsxp]NS_NewNativeLocalFile and that file isn't
132 * nsLocalFileUnix.cpp on 32-bit darwin. On 64-bit darwin it's a question
133 * of what we're doing in IPRT and such... We should probably add a
134 * RTPathConvertToNative for use here. */
135 int vrc = RTStrUtf8ToCurrentCP(&mCompRegLocation, aCompRegLocation);
136 if (RT_SUCCESS(vrc))
137 vrc = RTStrUtf8ToCurrentCP(&mXPTIDatLocation, aXPTIDatLocation);
138 if (RT_SUCCESS(vrc) && aComponentDirLocation)
139 vrc = RTStrUtf8ToCurrentCP(&mComponentDirLocation, aComponentDirLocation);
140 if (RT_SUCCESS(vrc) && aCurrProcDirLocation)
141 vrc = RTStrUtf8ToCurrentCP(&mCurrProcDirLocation, aCurrProcDirLocation);
142
143 return RT_SUCCESS(vrc) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
144}
145
146NS_IMETHODIMP
147DirectoryServiceProvider::GetFile(const char *aProp,
148 PRBool *aPersistent,
149 nsIFile **aRetval)
150{
151 nsCOMPtr <nsILocalFile> localFile;
152 nsresult rv = NS_ERROR_FAILURE;
153
154 *aRetval = nsnull;
155 *aPersistent = PR_TRUE;
156
157 const char *fileLocation = NULL;
158
159 if (strcmp(aProp, NS_XPCOM_COMPONENT_REGISTRY_FILE) == 0)
160 fileLocation = mCompRegLocation;
161 else if (strcmp(aProp, NS_XPCOM_XPTI_REGISTRY_FILE) == 0)
162 fileLocation = mXPTIDatLocation;
163 else if (mComponentDirLocation && strcmp(aProp, NS_XPCOM_COMPONENT_DIR) == 0)
164 fileLocation = mComponentDirLocation;
165 else if (mCurrProcDirLocation && strcmp(aProp, NS_XPCOM_CURRENT_PROCESS_DIR) == 0)
166 fileLocation = mCurrProcDirLocation;
167 else
168 return NS_ERROR_FAILURE;
169
170 rv = NS_NewNativeLocalFile(nsEmbedCString(fileLocation),
171 PR_TRUE, getter_AddRefs(localFile));
172 if (NS_FAILED(rv))
173 return rv;
174
175 return localFile->QueryInterface(NS_GET_IID(nsIFile), (void **)aRetval);
176}
177
178/**
179 * Global XPCOM initialization flag (we maintain it ourselves since XPCOM
180 * doesn't provide such functionality)
181 */
182static bool volatile gIsXPCOMInitialized = false;
183
184/**
185 * Number of Initialize() calls on the main thread.
186 */
187static unsigned int gXPCOMInitCount = 0;
188
189#else /* !defined(VBOX_WITH_XPCOM) */
190
191/**
192 * The COM main thread handle. (The first caller of com::Initialize().)
193 */
194static RTTHREAD volatile gCOMMainThread = NIL_RTTHREAD;
195
196/**
197 * Number of Initialize() calls on the main thread.
198 */
199static uint32_t gCOMMainInitCount = 0;
200
201#endif /* !defined(VBOX_WITH_XPCOM) */
202
203
204/**
205 * Initializes the COM runtime.
206 *
207 * This method must be called on each thread of the client application that
208 * wants to access COM facilities. The initialization must be performed before
209 * calling any other COM method or attempting to instantiate COM objects.
210 *
211 * On platforms using XPCOM, this method uses the following scheme to search for
212 * XPCOM runtime:
213 *
214 * 1. If the VBOX_APP_HOME environment variable is set, the path it specifies
215 * is used to search XPCOM libraries and components. If this method fails to
216 * initialize XPCOM runtime using this path, it will immediately return a
217 * failure and will NOT check for other paths as described below.
218 *
219 * 2. If VBOX_APP_HOME is not set, this methods tries the following paths in the
220 * given order:
221 *
222 * a) Compiled-in application data directory (as returned by
223 * RTPathAppPrivateArch())
224 * b) "/usr/lib/virtualbox" (Linux only)
225 * c) "/opt/VirtualBox" (Linux only)
226 *
227 * The first path for which the initialization succeeds will be used.
228 *
229 * On MS COM platforms, the COM runtime is provided by the system and does not
230 * need to be searched for.
231 *
232 * Once the COM subsystem is no longer necessary on a given thread, Shutdown()
233 * must be called to free resources allocated for it. Note that a thread may
234 * call Initialize() several times but for each of tese calls there must be a
235 * corresponding Shutdown() call.
236 *
237 * @return S_OK on success and a COM result code in case of failure.
238 */
239HRESULT Initialize(bool fGui)
240{
241 HRESULT rc = E_FAIL;
242
243#if !defined(VBOX_WITH_XPCOM)
244
245 /*
246 * We initialize COM in GUI thread in STA, to be compliant with QT and
247 * OLE requirments (for example to allow D&D), while other threads
248 * initialized in regular MTA. To allow fast proxyless access from
249 * GUI thread to COM objects, we explicitly provide our COM objects
250 * with free threaded marshaller.
251 * !!!!! Please think twice before touching this code !!!!!
252 */
253 DWORD flags = fGui ?
254 COINIT_APARTMENTTHREADED
255 | COINIT_SPEED_OVER_MEMORY
256 :
257 COINIT_MULTITHREADED
258 | COINIT_DISABLE_OLE1DDE
259 | COINIT_SPEED_OVER_MEMORY;
260
261 rc = CoInitializeEx(NULL, flags);
262
263 /* the overall result must be either S_OK or S_FALSE (S_FALSE means
264 * "already initialized using the same apartment model") */
265 AssertMsg(rc == S_OK || rc == S_FALSE, ("rc=%08X\n", rc));
266
267 /* To be flow compatible with the XPCOM case, we return here if this isn't
268 * the main thread or if it isn't its first initialization call.
269 * Note! CoInitializeEx and CoUninitialize does it's own reference
270 * counting, so this exercise is entirely for the EventQueue init. */
271 bool fRc;
272 RTTHREAD hSelf = RTThreadSelf();
273 if (hSelf != NIL_RTTHREAD)
274 ASMAtomicCmpXchgHandle(&gCOMMainThread, hSelf, NIL_RTTHREAD, fRc);
275 else
276 fRc = false;
277
278 if (fGui)
279 Assert(RTThreadIsMain(hSelf));
280
281 if (!fRc)
282 {
283 if ( gCOMMainThread == hSelf
284 && SUCCEEDED(rc))
285 gCOMMainInitCount++;
286
287 AssertComRC(rc);
288 return rc;
289 }
290 Assert(RTThreadIsMain(hSelf));
291
292 /* this is the first main thread initialization */
293 Assert(gCOMMainInitCount == 0);
294 if (SUCCEEDED(rc))
295 gCOMMainInitCount = 1;
296
297#else /* !defined(VBOX_WITH_XPCOM) */
298
299 /* Unused here */
300 NOREF(fGui);
301
302 if (ASMAtomicXchgBool(&gIsXPCOMInitialized, true) == true)
303 {
304 /* XPCOM is already initialized on the main thread, no special
305 * initialization is necessary on additional threads. Just increase
306 * the init counter if it's a main thread again (to correctly support
307 * nested calls to Initialize()/Shutdown() for compatibility with
308 * Win32). */
309
310 nsCOMPtr<nsIEventQueue> eventQ;
311 rc = NS_GetMainEventQ(getter_AddRefs(eventQ));
312
313 if (NS_SUCCEEDED(rc))
314 {
315 PRBool isOnMainThread = PR_FALSE;
316 rc = eventQ->IsOnCurrentThread(&isOnMainThread);
317 if (NS_SUCCEEDED(rc) && isOnMainThread)
318 ++gXPCOMInitCount;
319 }
320
321 AssertComRC(rc);
322 return rc;
323 }
324 Assert(RTThreadIsMain(RTThreadSelf()));
325
326 /* this is the first initialization */
327 gXPCOMInitCount = 1;
328
329 /* prepare paths for registry files */
330 char szCompReg[RTPATH_MAX];
331 char szXptiDat[RTPATH_MAX];
332
333 int vrc = GetVBoxUserHomeDirectory(szCompReg, sizeof(szCompReg));
334 if (vrc == VERR_ACCESS_DENIED)
335 return NS_ERROR_FILE_ACCESS_DENIED;
336 AssertRCReturn(vrc, NS_ERROR_FAILURE);
337 vrc = RTStrCopy(szXptiDat, sizeof(szXptiDat), szCompReg);
338 AssertRCReturn(vrc, NS_ERROR_FAILURE);
339#ifdef VBOX_IN_32_ON_64_MAIN_API
340 vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg-x86.dat");
341 AssertRCReturn(vrc, NS_ERROR_FAILURE);
342 vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti-x86.dat");
343 AssertRCReturn(vrc, NS_ERROR_FAILURE);
344#else
345 vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg.dat");
346 AssertRCReturn(vrc, NS_ERROR_FAILURE);
347 vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti.dat");
348 AssertRCReturn(vrc, NS_ERROR_FAILURE);
349#endif
350
351 LogFlowFunc(("component registry : \"%s\"\n", szCompReg));
352 LogFlowFunc(("XPTI data file : \"%s\"\n", szXptiDat));
353
354 static const char *kAppPathsToProbe[] =
355 {
356 NULL, /* 0: will use VBOX_APP_HOME */
357 NULL, /* 1: will try RTPathAppPrivateArch(), correctly installed release builds will never go further */
358 NULL, /* 2: will try parent directory of RTPathAppPrivateArch(), only for testcases in non-hardened builds */
359 /* There used to be hard coded paths, but they only caused trouble
360 * because they often led to mixing of builds or even versions.
361 * If you feel tempted to add anything here, think again. They would
362 * only be used if option 1 would not work, which is a sign of a big
363 * problem, as it returns a fixed location defined at compile time.
364 * It is better to fail than blindly trying to cover the problem. */
365 };
366
367 /* Find out the directory where VirtualBox binaries are located */
368 for (size_t i = 0; i < RT_ELEMENTS(kAppPathsToProbe); ++ i)
369 {
370 char szAppHomeDir[RTPATH_MAX];
371
372 if (i == 0)
373 {
374 /* Use VBOX_APP_HOME if present */
375 vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_APP_HOME", szAppHomeDir, sizeof(szAppHomeDir), NULL);
376 if (vrc == VERR_ENV_VAR_NOT_FOUND)
377 continue;
378 AssertRC(vrc);
379 }
380 else if (i == 1)
381 {
382 /* Use RTPathAppPrivateArch() first */
383 vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir));
384 AssertRC(vrc);
385 }
386 else if (i == 2)
387 {
388#ifdef VBOX_WITH_HARDENING
389 continue;
390#else /* !VBOX_WITH_HARDENING */
391 /* Use parent of RTPathAppPrivateArch() if ends with "testcase" */
392 vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir));
393 AssertRC(vrc);
394 vrc = RTPathStripTrailingSlash(szAppHomeDir);
395 AssertRC(vrc);
396 char *filename = RTPathFilename(szAppHomeDir);
397 if (!filename || strcmp(filename, "testcase"))
398 continue;
399 RTPathStripFilename(szAppHomeDir);
400#endif /* !VBOX_WITH_HARDENING */
401 }
402 else
403 {
404 /* Iterate over all other paths */
405 RTStrCopy(szAppHomeDir, sizeof(szAppHomeDir), kAppPathsToProbe[i]);
406 vrc = VINF_SUCCESS;
407 }
408 if (RT_FAILURE(vrc))
409 {
410 rc = NS_ERROR_FAILURE;
411 continue;
412 }
413 char szCompDir[RTPATH_MAX];
414 vrc = RTStrCopy(szCompDir, sizeof(szCompDir), szAppHomeDir);
415 if (RT_FAILURE(vrc))
416 {
417 rc = NS_ERROR_FAILURE;
418 continue;
419 }
420 vrc = RTPathAppend(szCompDir, sizeof(szCompDir), "components");
421 if (RT_FAILURE(vrc))
422 {
423 rc = NS_ERROR_FAILURE;
424 continue;
425 }
426 LogFlowFunc(("component directory : \"%s\"\n", szCompDir));
427
428 nsCOMPtr<DirectoryServiceProvider> dsProv;
429 dsProv = new DirectoryServiceProvider();
430 if (dsProv)
431 rc = dsProv->init(szCompReg, szXptiDat, szCompDir, szAppHomeDir);
432 else
433 rc = NS_ERROR_OUT_OF_MEMORY;
434 if (NS_FAILED(rc))
435 break;
436
437 /* Setup the application path for NS_InitXPCOM2. Note that we properly
438 * answer the NS_XPCOM_CURRENT_PROCESS_DIR query in our directory
439 * service provider but it seems to be activated after the directory
440 * service is used for the first time (see the source NS_InitXPCOM2). So
441 * use the same value here to be on the safe side. */
442 nsCOMPtr <nsIFile> appDir;
443 {
444 char *appDirCP = NULL;
445 vrc = RTStrUtf8ToCurrentCP(&appDirCP, szAppHomeDir);
446 if (RT_SUCCESS(vrc))
447 {
448 nsCOMPtr<nsILocalFile> file;
449 rc = NS_NewNativeLocalFile(nsEmbedCString(appDirCP),
450 PR_FALSE, getter_AddRefs(file));
451 if (NS_SUCCEEDED(rc))
452 appDir = do_QueryInterface(file, &rc);
453
454 RTStrFree(appDirCP);
455 }
456 else
457 rc = NS_ERROR_FAILURE;
458 }
459 if (NS_FAILED(rc))
460 break;
461
462 /* Set VBOX_XPCOM_HOME to the same app path to make XPCOM sources that
463 * still use it instead of the directory service happy */
464 vrc = RTEnvSetEx(RTENV_DEFAULT, "VBOX_XPCOM_HOME", szAppHomeDir);
465 AssertRC(vrc);
466
467 /* Finally, initialize XPCOM */
468 {
469 nsCOMPtr<nsIServiceManager> serviceManager;
470 rc = NS_InitXPCOM2(getter_AddRefs(serviceManager), appDir, dsProv);
471 if (NS_SUCCEEDED(rc))
472 {
473 nsCOMPtr<nsIComponentRegistrar> registrar =
474 do_QueryInterface(serviceManager, &rc);
475 if (NS_SUCCEEDED(rc))
476 {
477 rc = registrar->AutoRegister(nsnull);
478 if (NS_SUCCEEDED(rc))
479 {
480 /* We succeeded, stop probing paths */
481 LogFlowFunc(("Succeeded.\n"));
482 break;
483 }
484 }
485 }
486 }
487
488 /* clean up before the new try */
489 HRESULT rc2 = NS_ShutdownXPCOM(nsnull);
490 if (SUCCEEDED(rc))
491 rc = rc2;
492
493 if (i == 0)
494 {
495 /* We failed with VBOX_APP_HOME, don't probe other paths */
496 break;
497 }
498 }
499
500#endif /* !defined(VBOX_WITH_XPCOM) */
501
502 AssertComRCReturnRC(rc);
503
504 // for both COM and XPCOM, we only get here if this is the main thread;
505 // only then initialize the autolock system (AutoLock.cpp)
506 Assert(RTThreadIsMain(RTThreadSelf()));
507 util::InitAutoLockSystem();
508
509 /*
510 * Init the main event queue (ASSUMES it cannot fail).
511 */
512 if (SUCCEEDED(rc))
513 NativeEventQueue::init();
514
515 return rc;
516}
517
518HRESULT Shutdown()
519{
520 HRESULT rc = S_OK;
521
522#if !defined(VBOX_WITH_XPCOM)
523
524 /* EventQueue::uninit reference counting fun. */
525 RTTHREAD hSelf = RTThreadSelf();
526 if ( hSelf == gCOMMainThread
527 && hSelf != NIL_RTTHREAD)
528 {
529 if (-- gCOMMainInitCount == 0)
530 {
531 NativeEventQueue::uninit();
532 ASMAtomicWriteHandle(&gCOMMainThread, NIL_RTTHREAD);
533 }
534 }
535
536 CoUninitialize();
537
538#else /* !defined(VBOX_WITH_XPCOM) */
539
540 nsCOMPtr<nsIEventQueue> eventQ;
541 rc = NS_GetMainEventQ(getter_AddRefs(eventQ));
542
543 if (NS_SUCCEEDED(rc) || rc == NS_ERROR_NOT_AVAILABLE)
544 {
545 /* NS_ERROR_NOT_AVAILABLE seems to mean that
546 * nsIEventQueue::StopAcceptingEvents() has been called (see
547 * nsEventQueueService.cpp). We hope that this error code always means
548 * just that in this case and assume that we're on the main thread
549 * (it's a kind of unexpected behavior if a non-main thread ever calls
550 * StopAcceptingEvents() on the main event queue). */
551
552 PRBool isOnMainThread = PR_FALSE;
553 if (NS_SUCCEEDED(rc))
554 {
555 rc = eventQ->IsOnCurrentThread(&isOnMainThread);
556 eventQ = nsnull; /* early release before shutdown */
557 }
558 else
559 {
560 isOnMainThread = RTThreadIsMain(RTThreadSelf());
561 rc = NS_OK;
562 }
563
564 if (NS_SUCCEEDED(rc) && isOnMainThread)
565 {
566 /* only the main thread needs to uninitialize XPCOM and only if
567 * init counter drops to zero */
568 if (--gXPCOMInitCount == 0)
569 {
570 NativeEventQueue::uninit();
571 rc = NS_ShutdownXPCOM(nsnull);
572
573 /* This is a thread initialized XPCOM and set gIsXPCOMInitialized to
574 * true. Reset it back to false. */
575 bool wasInited = ASMAtomicXchgBool(&gIsXPCOMInitialized, false);
576 Assert(wasInited == true);
577 NOREF(wasInited);
578 }
579 }
580 }
581
582#endif /* !defined(VBOX_WITH_XPCOM) */
583
584 AssertComRC(rc);
585
586 return rc;
587}
588
589} /* namespace com */
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