VirtualBox

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

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

COM/XPCOM glue: Bug fixes around the EventQueue init/uninit calls from com::Initialize and com::Shutdown. As com::Initialize() clearly states, this function will be called by all client threads doing COM/XPCOM work. Calling it more than once on the same thread is also documented as normal behavior (this is also evident from the counting done in the XPCOM version of the code). However it seems like the media checker is one of the few use cases for this and it doesn't kick in on all systems it seems... So, I hope this actually works as I cannot reproduce it here.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.6 KB
Line 
1/* $Id: initterm.cpp 23128 2009-09-18 12:50:55Z vboxsync $ */
2
3/** @file
4 * MS COM / XPCOM Abstraction Layer - Initialization and Termination.
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * 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.
21 */
22
23#if !defined (VBOX_WITH_XPCOM)
24
25#include <objbase.h>
26
27#else /* !defined (VBOX_WITH_XPCOM) */
28
29#include <stdlib.h>
30
31/* XPCOM_GLUE is defined when the client uses the standalone glue
32 * (i.e. dynamically picks up the existing XPCOM shared library installation).
33 * This is not the case for VirtualBox XPCOM clients (they are always
34 * distrubuted with the self-built XPCOM library, and therefore have a binary
35 * dependency on it) but left here for clarity.
36 */
37#if defined (XPCOM_GLUE)
38#include <nsXPCOMGlue.h>
39#endif
40
41#include <nsIComponentRegistrar.h>
42#include <nsIServiceManager.h>
43#include <nsCOMPtr.h>
44#include <nsEventQueueUtils.h>
45#include <nsEmbedString.h>
46
47#include <nsILocalFile.h>
48#include <nsIDirectoryService.h>
49#include <nsDirectoryServiceDefs.h>
50
51#endif /* !defined (VBOX_WITH_XPCOM) */
52
53#include "VBox/com/com.h"
54#include "VBox/com/assert.h"
55#include "VBox/com/EventQueue.h"
56
57#include "../include/Logging.h"
58
59#include <iprt/asm.h>
60#include <iprt/env.h>
61#include <iprt/param.h>
62#include <iprt/path.h>
63#include <iprt/string.h>
64#include <iprt/thread.h>
65
66#include <VBox/err.h>
67
68namespace com
69{
70
71#if defined (VBOX_WITH_XPCOM)
72
73class DirectoryServiceProvider : public nsIDirectoryServiceProvider
74{
75public:
76
77 NS_DECL_ISUPPORTS
78
79 DirectoryServiceProvider()
80 : mCompRegLocation (NULL), mXPTIDatLocation (NULL)
81 , mComponentDirLocation (NULL), mCurrProcDirLocation (NULL)
82 {}
83
84 virtual ~DirectoryServiceProvider();
85
86 HRESULT init (const char *aCompRegLocation,
87 const char *aXPTIDatLocation,
88 const char *aComponentDirLocation,
89 const char *aCurrProcDirLocation);
90
91 NS_DECL_NSIDIRECTORYSERVICEPROVIDER
92
93private:
94
95 char *mCompRegLocation;
96 char *mXPTIDatLocation;
97 char *mComponentDirLocation;
98 char *mCurrProcDirLocation;
99};
100
101NS_IMPL_ISUPPORTS1 (DirectoryServiceProvider, nsIDirectoryServiceProvider)
102
103DirectoryServiceProvider::~DirectoryServiceProvider()
104{
105 if (mCompRegLocation)
106 {
107 RTStrFree (mCompRegLocation);
108 mCompRegLocation = NULL;
109 }
110 if (mXPTIDatLocation)
111 {
112 RTStrFree (mXPTIDatLocation);
113 mXPTIDatLocation = NULL;
114 }
115 if (mComponentDirLocation)
116 {
117 RTStrFree (mComponentDirLocation);
118 mComponentDirLocation = NULL;
119 }
120 if (mCurrProcDirLocation)
121 {
122 RTStrFree (mCurrProcDirLocation);
123 mCurrProcDirLocation = NULL;
124 }
125}
126
127/**
128 * @param aCompRegLocation Path to compreg.dat, in Utf8.
129 * @param aXPTIDatLocation Path to xpti.data, in Utf8.
130 */
131HRESULT
132DirectoryServiceProvider::init (const char *aCompRegLocation,
133 const char *aXPTIDatLocation,
134 const char *aComponentDirLocation,
135 const char *aCurrProcDirLocation)
136{
137 AssertReturn(aCompRegLocation, NS_ERROR_INVALID_ARG);
138 AssertReturn(aXPTIDatLocation, NS_ERROR_INVALID_ARG);
139
140 int vrc = RTStrUtf8ToCurrentCP (&mCompRegLocation, aCompRegLocation);
141 if (RT_SUCCESS(vrc))
142 vrc = RTStrUtf8ToCurrentCP (&mXPTIDatLocation, aXPTIDatLocation);
143 if (RT_SUCCESS(vrc) && aComponentDirLocation)
144 vrc = RTStrUtf8ToCurrentCP (&mComponentDirLocation, aComponentDirLocation);
145 if (RT_SUCCESS(vrc) && aCurrProcDirLocation)
146 vrc = RTStrUtf8ToCurrentCP (&mCurrProcDirLocation, aCurrProcDirLocation);
147
148 return RT_SUCCESS(vrc) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
149}
150
151NS_IMETHODIMP
152DirectoryServiceProvider::GetFile (const char *aProp,
153 PRBool *aPersistent,
154 nsIFile **aRetval)
155{
156 nsCOMPtr <nsILocalFile> localFile;
157 nsresult rv = NS_ERROR_FAILURE;
158
159 *aRetval = nsnull;
160 *aPersistent = PR_TRUE;
161
162 const char *fileLocation = NULL;
163
164 if (strcmp (aProp, NS_XPCOM_COMPONENT_REGISTRY_FILE) == 0)
165 fileLocation = mCompRegLocation;
166 else if (strcmp (aProp, NS_XPCOM_XPTI_REGISTRY_FILE) == 0)
167 fileLocation = mXPTIDatLocation;
168 else if (mComponentDirLocation && strcmp (aProp, NS_XPCOM_COMPONENT_DIR) == 0)
169 fileLocation = mComponentDirLocation;
170 else if (mCurrProcDirLocation && strcmp (aProp, NS_XPCOM_CURRENT_PROCESS_DIR) == 0)
171 fileLocation = mCurrProcDirLocation;
172 else
173 return NS_ERROR_FAILURE;
174
175 rv = NS_NewNativeLocalFile (nsEmbedCString (fileLocation),
176 PR_TRUE, getter_AddRefs (localFile));
177 if (NS_FAILED(rv))
178 return rv;
179
180 return localFile->QueryInterface (NS_GET_IID (nsIFile),
181 (void **) aRetval);
182}
183
184/**
185 * Global XPCOM initialization flag (we maintain it ourselves since XPCOM
186 * doesn't provide such functionality)
187 */
188static bool volatile gIsXPCOMInitialized = false;
189
190/**
191 * Number of Initialize() calls on the main thread.
192 */
193static unsigned int gXPCOMInitCount = 0;
194
195#else /* !defined (VBOX_WITH_XPCOM) */
196
197/**
198 * The COM main thread handle. (The first caller of com::Initialize().)
199 */
200static RTTHREAD volatile gCOMMainThread = NIL_RTTHREAD;
201
202/**
203 * Number of Initialize() calls on the main thread.
204 */
205static uint32_t gCOMMainInitCount = 0;
206
207#endif /* !defined (VBOX_WITH_XPCOM) */
208
209
210/**
211 * Initializes the COM runtime.
212 *
213 * This method must be called on each thread of the client application that
214 * wants to access COM facilities. The initialization must be performed before
215 * calling any other COM method or attempting to instantiate COM objects.
216 *
217 * On platforms using XPCOM, this method uses the following scheme to search for
218 * XPCOM runtime:
219 *
220 * 1. If the VBOX_APP_HOME environment variable is set, the path it specifies
221 * is used to search XPCOM libraries and components. If this method fails to
222 * initialize XPCOM runtime using this path, it will immediately return a
223 * failure and will NOT check for other paths as described below.
224 *
225 * 2. If VBOX_APP_HOME is not set, this methods tries the following paths in the
226 * given order:
227 *
228 * a) Compiled-in application data directory (as returned by
229 * RTPathAppPrivateArch())
230 * b) "/usr/lib/virtualbox" (Linux only)
231 * c) "/opt/VirtualBox" (Linux only)
232 *
233 * The first path for which the initialization succeeds will be used.
234 *
235 * On MS COM platforms, the COM runtime is provided by the system and does not
236 * need to be searched for.
237 *
238 * Once the COM subsystem is no longer necessary on a given thread, Shutdown()
239 * must be called to free resources allocated for it. Note that a thread may
240 * call Initialize() several times but for each of tese calls there must be a
241 * corresponding Shutdown() call.
242 *
243 * @return S_OK on success and a COM result code in case of failure.
244 */
245HRESULT Initialize()
246{
247 HRESULT rc = E_FAIL;
248
249#if !defined (VBOX_WITH_XPCOM)
250
251 DWORD flags = COINIT_MULTITHREADED |
252 COINIT_DISABLE_OLE1DDE |
253 COINIT_SPEED_OVER_MEMORY;
254
255 rc = CoInitializeEx (NULL, flags);
256
257 /// @todo the below rough method of changing the aparment type doesn't
258 /// work on some systems for unknown reason (CoUninitialize() simply does
259 /// nothing there, or at least all 10 000 of subsequent CoInitializeEx()
260 /// continue to return RPC_E_CHANGED_MODE there). The problem on those
261 /// systems is related to the "Extend support for advanced text services
262 /// to all programs" checkbox in the advanced language settings dialog,
263 /// i.e. the problem appears when this checkbox is checked and disappears
264 /// if you clear it. For this reason, we disable the code below and
265 /// instead initialize COM in MTA as early as possible, before 3rd party
266 /// libraries we use have done so (i.e. Qt).
267# if 0
268 /* If we fail to set the necessary apartment model, it may mean that some
269 * DLL that was indirectly loaded by the process calling this function has
270 * already initialized COM on the given thread in an incompatible way
271 * which we can't leave with. Therefore, we try to fix this by using the
272 * brute force method: */
273
274 if (rc == RPC_E_CHANGED_MODE)
275 {
276 /* Before we use brute force, we need to check if we are in the
277 * neutral threaded apartment -- in this case there is no need to
278 * worry at all. */
279
280 rc = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
281 if (rc == RPC_E_CHANGED_MODE)
282 {
283 /* This is a neutral apartment, reset the error */
284 rc = S_OK;
285
286 LogFlowFunc (("COM is already initialized in neutral threaded "
287 "apartment mode,\nwill accept it.\n"));
288 }
289 else if (rc == S_FALSE)
290 {
291 /* balance the test CoInitializeEx above */
292 CoUninitialize();
293 rc = RPC_E_CHANGED_MODE;
294
295 LogFlowFunc (("COM is already initialized in single threaded "
296 "apartment mode,\nwill reinitialize as "
297 "multi threaded.\n"));
298
299 enum { MaxTries = 10000 };
300 int tries = MaxTries;
301 while (rc == RPC_E_CHANGED_MODE && tries --)
302 {
303 CoUninitialize();
304 rc = CoInitializeEx (NULL, flags);
305 if (rc == S_OK)
306 {
307 /* We've successfully reinitialized COM; restore the
308 * initialization reference counter */
309
310 LogFlowFunc (("Will call CoInitializeEx() %d times.\n",
311 MaxTries - tries));
312
313 while (tries ++ < MaxTries)
314 {
315 rc = CoInitializeEx (NULL, flags);
316 Assert (rc == S_FALSE);
317 }
318 }
319 }
320 }
321 else
322 AssertMsgFailed (("rc=%08X\n", rc));
323 }
324# endif
325
326 /* the overall result must be either S_OK or S_FALSE (S_FALSE means
327 * "already initialized using the same apartment model") */
328 AssertMsg (rc == S_OK || rc == S_FALSE, ("rc=%08X\n", rc));
329
330 /* To be flow compatible with the XPCOM case, we return here if this isn't
331 * the main thread or if it isn't its first initialization call.
332 * Note! CoInitializeEx and CoUninitialize does it's own reference
333 * counting, so this exercise is entirely for the EventQueue init. */
334 bool fRc;
335 RTTHREAD hSelf = RTThreadSelf (); Assert (hSelf != NIL_RTTHREAD);
336 ASMAtomicCmpXchgHandle (&gCOMMainThread, hSelf, NIL_RTTHREAD, fRc);
337 if (!fRc)
338 {
339 if ( gCOMMainThread == hSelf
340 && SUCCEEDED (rc))
341 gCOMMainInitCount++;
342
343 AssertComRC (rc);
344 return rc;
345 }
346 Assert (RTThreadIsMain (hSelf));
347
348 /* this is the first main thread initialization */
349 Assert (gCOMMainInitCount == 0);
350 if (SUCCEEDED (rc))
351 gCOMMainInitCount = 1;
352
353#else /* !defined (VBOX_WITH_XPCOM) */
354
355 if (ASMAtomicXchgBool (&gIsXPCOMInitialized, true) == true)
356 {
357 /* XPCOM is already initialized on the main thread, no special
358 * initialization is necessary on additional threads. Just increase
359 * the init counter if it's a main thread again (to correctly support
360 * nested calls to Initialize()/Shutdown() for compatibility with
361 * Win32). */
362
363 nsCOMPtr <nsIEventQueue> eventQ;
364 rc = NS_GetMainEventQ (getter_AddRefs (eventQ));
365
366 if (NS_SUCCEEDED(rc))
367 {
368 PRBool isOnMainThread = PR_FALSE;
369 rc = eventQ->IsOnCurrentThread (&isOnMainThread);
370 if (NS_SUCCEEDED(rc) && isOnMainThread)
371 ++ gXPCOMInitCount;
372 }
373
374 AssertComRC (rc);
375 return rc;
376 }
377 Assert (RTThreadIsMain (RTThreadSelf()));
378
379 /* this is the first initialization */
380 gXPCOMInitCount = 1;
381 bool const fInitEventQueues = true;
382
383 /* prepare paths for registry files */
384 char userHomeDir [RTPATH_MAX];
385 int vrc = GetVBoxUserHomeDirectory (userHomeDir, sizeof (userHomeDir));
386 AssertRCReturn (vrc, NS_ERROR_FAILURE);
387
388 char compReg [RTPATH_MAX];
389 char xptiDat [RTPATH_MAX];
390
391 /** @todo use RTPathAppend */
392 RTStrPrintf (compReg, sizeof (compReg), "%s%c%s",
393 userHomeDir, RTPATH_DELIMITER, "compreg.dat");
394 RTStrPrintf (xptiDat, sizeof (xptiDat), "%s%c%s",
395 userHomeDir, RTPATH_DELIMITER, "xpti.dat");
396
397 LogFlowFunc (("component registry : \"%s\"\n", compReg));
398 LogFlowFunc (("XPTI data file : \"%s\"\n", xptiDat));
399
400#if defined (XPCOM_GLUE)
401 XPCOMGlueStartup (nsnull);
402#endif
403
404 static const char *kAppPathsToProbe[] =
405 {
406 NULL, /* 0: will use VBOX_APP_HOME */
407 NULL, /* 1: will try RTPathAppPrivateArch() */
408#ifdef RT_OS_LINUX
409 "/usr/lib/virtualbox",
410 "/opt/VirtualBox",
411#elif RT_OS_SOLARIS
412 "/opt/VirtualBox/amd64",
413 "/opt/VirtualBox/i386",
414#elif RT_OS_DARWIN
415 "/Application/VirtualBox.app/Contents/MacOS",
416#endif
417 };
418
419 /* Find out the directory where VirtualBox binaries are located */
420 for (size_t i = 0; i < RT_ELEMENTS (kAppPathsToProbe); ++ i)
421 {
422 char appHomeDir [RTPATH_MAX];
423 appHomeDir [RTPATH_MAX - 1] = '\0';
424
425 if (i == 0)
426 {
427 /* Use VBOX_APP_HOME if present */
428 if (!RTEnvExist ("VBOX_APP_HOME"))
429 continue;
430
431 strncpy (appHomeDir, RTEnvGet ("VBOX_APP_HOME"), RTPATH_MAX - 1); /** @todo r=bird: Use RTEnvGetEx. */
432 }
433 else if (i == 1)
434 {
435 /* Use RTPathAppPrivateArch() first */
436 vrc = RTPathAppPrivateArch (appHomeDir, sizeof (appHomeDir));
437 AssertRC (vrc);
438 if (RT_FAILURE(vrc))
439 {
440 rc = NS_ERROR_FAILURE;
441 continue;
442 }
443 }
444 else
445 {
446 /* Iterate over all other paths */
447 strncpy (appHomeDir, kAppPathsToProbe [i], RTPATH_MAX - 1);
448 }
449
450 nsCOMPtr <DirectoryServiceProvider> dsProv;
451
452 char compDir [RTPATH_MAX];
453 RTStrPrintf (compDir, sizeof (compDir), "%s%c%s",
454 appHomeDir, RTPATH_DELIMITER, "components");
455 LogFlowFunc (("component directory : \"%s\"\n", compDir));
456
457 dsProv = new DirectoryServiceProvider();
458 if (dsProv)
459 rc = dsProv->init (compReg, xptiDat, compDir, appHomeDir);
460 else
461 rc = NS_ERROR_OUT_OF_MEMORY;
462 if (NS_FAILED (rc))
463 break;
464
465 /* Setup the application path for NS_InitXPCOM2. Note that we properly
466 * answer the NS_XPCOM_CURRENT_PROCESS_DIR query in our directory
467 * service provider but it seems to be activated after the directory
468 * service is used for the first time (see the source NS_InitXPCOM2). So
469 * use the same value here to be on the safe side. */
470 nsCOMPtr <nsIFile> appDir;
471 {
472 char *appDirCP = NULL;
473 vrc = RTStrUtf8ToCurrentCP (&appDirCP, appHomeDir);
474 if (RT_SUCCESS(vrc))
475 {
476 nsCOMPtr <nsILocalFile> file;
477 rc = NS_NewNativeLocalFile (nsEmbedCString (appDirCP),
478 PR_FALSE, getter_AddRefs (file));
479 if (NS_SUCCEEDED(rc))
480 appDir = do_QueryInterface (file, &rc);
481
482 RTStrFree (appDirCP);
483 }
484 else
485 rc = NS_ERROR_FAILURE;
486 }
487 if (NS_FAILED (rc))
488 break;
489
490 /* Set VBOX_XPCOM_HOME to the same app path to make XPCOM sources that
491 * still use it instead of the directory service happy */
492 {
493 char *pathCP = NULL;
494 vrc = RTStrUtf8ToCurrentCP (&pathCP, appHomeDir);
495 if (RT_SUCCESS(vrc))
496 {
497 vrc = RTEnvSet ("VBOX_XPCOM_HOME", pathCP);
498 RTStrFree (pathCP);
499 }
500 AssertRC (vrc);
501 }
502
503 /* Finally, initialize XPCOM */
504 {
505 nsCOMPtr <nsIServiceManager> serviceManager;
506 rc = NS_InitXPCOM2 (getter_AddRefs (serviceManager),
507 appDir, dsProv);
508
509 if (NS_SUCCEEDED(rc))
510 {
511 nsCOMPtr <nsIComponentRegistrar> registrar =
512 do_QueryInterface (serviceManager, &rc);
513 if (NS_SUCCEEDED(rc))
514 {
515 rc = registrar->AutoRegister (nsnull);
516 if (NS_SUCCEEDED(rc))
517 {
518 /* We succeeded, stop probing paths */
519 LogFlowFunc (("Succeeded.\n"));
520 break;
521 }
522 }
523 }
524 }
525
526 /* clean up before the new try */
527 rc = NS_ShutdownXPCOM (nsnull);
528
529 if (i == 0)
530 {
531 /* We failed with VBOX_APP_HOME, don't probe other paths */
532 break;
533 }
534 }
535
536#endif /* !defined (VBOX_WITH_XPCOM) */
537
538 AssertComRC (rc);
539
540 /*
541 * Init the main event queue (ASSUMES it cannot fail).
542 */
543 if (SUCCEEDED (rc))
544 EventQueue::init();
545
546 return rc;
547}
548
549HRESULT Shutdown()
550{
551 HRESULT rc = S_OK;
552
553#if !defined (VBOX_WITH_XPCOM)
554
555 /* EventQueue::uninit reference counting fun. */
556 RTTHREAD hSelf = RTThreadSelf();
557 if ( hSelf == gCOMMainThread
558 && hSelf != NIL_RTTHREAD)
559 {
560 if (-- gCOMMainInitCount == 0)
561 {
562 EventQueue::uninit();
563 ASMAtomicWriteHandle (&gCOMMainThread, NIL_RTTHREAD);
564 }
565 }
566
567 CoUninitialize();
568
569#else /* !defined (VBOX_WITH_XPCOM) */
570
571 nsCOMPtr <nsIEventQueue> eventQ;
572 rc = NS_GetMainEventQ (getter_AddRefs (eventQ));
573
574 if (NS_SUCCEEDED(rc) || rc == NS_ERROR_NOT_AVAILABLE)
575 {
576 /* NS_ERROR_NOT_AVAILABLE seems to mean that
577 * nsIEventQueue::StopAcceptingEvents() has been called (see
578 * nsEventQueueService.cpp). We hope that this error code always means
579 * just that in this case and assume that we're on the main thread
580 * (it's a kind of unexpected behavior if a non-main thread ever calls
581 * StopAcceptingEvents() on the main event queue). */
582
583 PRBool isOnMainThread = PR_FALSE;
584 if (NS_SUCCEEDED(rc))
585 {
586 rc = eventQ->IsOnCurrentThread (&isOnMainThread);
587 eventQ = nsnull; /* early release before shutdown */
588 }
589 else
590 {
591 isOnMainThread = PR_TRUE;
592 rc = NS_OK;
593 }
594
595 if (NS_SUCCEEDED(rc) && isOnMainThread)
596 {
597 /* only the main thread needs to uninitialize XPCOM and only if
598 * init counter drops to zero */
599 if (-- gXPCOMInitCount == 0)
600 {
601 EventQueue::uninit();
602 rc = NS_ShutdownXPCOM (nsnull);
603
604 /* This is a thread initialized XPCOM and set gIsXPCOMInitialized to
605 * true. Reset it back to false. */
606 bool wasInited = ASMAtomicXchgBool (&gIsXPCOMInitialized, false);
607 Assert (wasInited == true);
608 NOREF (wasInited);
609
610#if defined (XPCOM_GLUE)
611 XPCOMGlueShutdown();
612#endif
613 }
614 }
615 }
616
617#endif /* !defined (VBOX_WITH_XPCOM) */
618
619 AssertComRC (rc);
620
621 return rc;
622}
623
624} /* 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