Make initialization and logging more reliable. Get building again on

Linux.  Not part of the build.
This commit is contained in:
mhammond%skippinet.com.au 2006-04-20 07:19:42 +00:00
parent 43a7c5e950
commit 931aace7b2
6 changed files with 160 additions and 201 deletions

View File

@ -100,6 +100,32 @@ void DoLogMessage(const char *methodName, const char *pszMessageText)
// But this also means we need a clear error state...
PyObject *exc_typ = NULL, *exc_val = NULL, *exc_tb = NULL;
PyErr_Fetch(&exc_typ, &exc_val, &exc_tb);
// Only use the logging module if someone has successfully
// initialized it for us! In practice, this means 'does our
// log have any handlers?'. It is a little yucky that we reach into
// implementation knowledge, but it would be far worse to have some
// obscure problem initializing the logging package cause all future
// messages to be discarded.
static PRBool initializedForLogging = PR_FALSE;
if (!initializedForLogging) {
PyObject *mod = PyImport_ImportModule("logging");
PyObject *logger = mod ?
PyObject_CallMethod(mod, "getLogger", "s", "xpcom") :
NULL;
PyObject *handlers = PyObject_GetAttrString(logger, "handlers");
if (handlers)
initializedForLogging = PySequence_Check(handlers) &&
PySequence_Length(handlers) > 0;
Py_XDECREF(mod);
Py_XDECREF(logger);
Py_XDECREF(handlers);
PyErr_Clear();
if (!initializedForLogging) {
_PanicErrorWrite(pszMessageText);
return;
}
}
// We will execute:
// import logging
// logging.getLogger('xpcom').{warning/error/etc}("%s", {msg_text})

View File

@ -85,6 +85,7 @@ CPPSRCS= \
include $(topsrcdir)/config/config.mk
include $(topsrcdir)/config/rules.mk
CXXFLAGS += -DPYTHON_SO=\"libpython$(MOZ_PYTHON_VER_DOTTED).so\"
EXTRA_DSO_LDOPTS += $(MOZ_COMPONENT_LIBS)
clobber::

View File

@ -622,13 +622,18 @@ public:
~CEnterLeaveXPCOMFramework() {PyXPCOM_ReleaseGlobalLock();}
};
// Initialize Python and do anything else necessary to get a function
// Initialize Python and do anything else necessary to get a functioning
// Python environment going...
PYXPCOM_EXPORT void PyXPCOM_EnsurePythonEnvironment(void);
// Python thread-lock stuff. Free-threading patches use different semantics, but
// these are abstracted away here...
//#include <threadstate.h>
PYXPCOM_EXPORT void PyXPCOM_MakePendingCalls();
// PyXPCOM_Globals_Ensure is deprecated - use PyXPCOM_EnsurePythonEnvironment
// which sets up globals, but also a whole lot more...
inline PRBool PyXPCOM_Globals_Ensure() {
PyXPCOM_EnsurePythonEnvironment();
return PR_TRUE;
}
// Helper class for Enter/Leave Python
//
@ -639,15 +644,6 @@ PYXPCOM_EXPORT void PyXPCOM_EnsurePythonEnvironment(void);
// NEVER new one of these objects - only use on the stack!
PYXPCOM_EXPORT void PyXPCOM_MakePendingCalls();
PYXPCOM_EXPORT PRBool PyXPCOM_Globals_Ensure();
// For 2.3, use the PyGILState_ calls
#if (PY_VERSION_HEX >= 0x02030000)
#define PYXPCOM_USE_PYGILSTATE
#endif
#ifdef PYXPCOM_USE_PYGILSTATE
class CEnterLeavePython {
public:
CEnterLeavePython() {
@ -663,54 +659,6 @@ public:
}
PyGILState_STATE state;
};
#else
PYXPCOM_EXPORT PyInterpreterState *PyXPCOM_InterpreterState;
PYXPCOM_EXPORT PRBool PyXPCOM_ThreadState_Ensure();
PYXPCOM_EXPORT void PyXPCOM_ThreadState_Free();
PYXPCOM_EXPORT void PyXPCOM_ThreadState_Clear();
PYXPCOM_EXPORT void PyXPCOM_InterpreterLock_Acquire();
PYXPCOM_EXPORT void PyXPCOM_InterpreterLock_Release();
// Pre 2.3 thread-state dances.
class CEnterLeavePython {
public:
CEnterLeavePython() {
created = PyXPCOM_ThreadState_Ensure();
PyXPCOM_InterpreterLock_Acquire();
if (created) {
// If pending python calls are waiting as we enter Python,
// it will generally mean an asynch signal handler, etc.
// We can either call it here, or wait for Python to call it
// as part of its "even 'n' opcodes" check. If we wait for
// Python to check it and the pending call raises an exception,
// then it is _our_ code that will fail - this is unfair,
// as the signal was raised before we were entered - indeed,
// we may be directly responding to the signal!
// Thus, we flush all the pending calls here, and report any
// exceptions via our normal exception reporting mechanism.
// We can then execute our code in the knowledge that only
// signals raised _while_ we are executing will cause exceptions.
PyXPCOM_MakePendingCalls();
}
}
~CEnterLeavePython() {
// The interpreter state must be cleared
// _before_ we release the lock, as some of
// the sys. attributes cleared (eg, the current exception)
// may need the lock to invoke their destructors -
// specifically, when exc_value is a class instance, and
// the exception holds the last reference!
if ( created )
PyXPCOM_ThreadState_Clear();
PyXPCOM_InterpreterLock_Release();
if ( created )
PyXPCOM_ThreadState_Free();
}
private:
PRBool created;
};
#endif // PYXPCOM_USE_PYGILSTATE
// Our classes.
// Hrm - So we can't have templates, eh??

View File

@ -46,15 +46,10 @@
// (c) 2000, ActiveState corp.
#include "PyXPCOM_std.h"
#include <prthread.h>
#include "nsIThread.h"
#include "nsILocalFile.h"
#include "nsTraceRefcntImpl.h"
#include "nsDirectoryServiceDefs.h"
#include "nsILocalFile.h"
#include "nsITimelineService.h"
#include <nsIConsoleService.h>
#include "nspr.h" // PR_fprintf
#ifdef XP_WIN
@ -99,36 +94,6 @@ PyXPCOM_ReleaseGlobalLock(void)
PR_Unlock(g_lockMain);
}
// Note we can't use the PyXPCOM_Log* functions as we are still booting
// up the xpcom support, which is what sets up the logger etc. So we
// just write directly to the console service and to stderr.
static void DoLogStartupMessage(const char *prefix, const char *fmt, va_list argptr)
{
char buff[2048];
PR_vsnprintf(buff, sizeof(buff), fmt, argptr);
nsCOMPtr<nsIConsoleService> consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
if (consoleService)
consoleService->LogStringMessage(NS_ConvertASCIItoUTF16(buff).get());
PR_fprintf(PR_STDERR,"%s\n", buff);
}
static void LogStartupError(const char *fmt, ...)
{
va_list marker;
va_start(marker, fmt);
DoLogStartupMessage("PyXPCOM Startup Error:", fmt, marker);
}
static void LogStartupDebug(const char *fmt, ...)
{
#ifdef NS_DEBUG
va_list marker;
va_start(marker, fmt);
DoLogStartupMessage("", fmt, marker);
#endif
}
// Ensure that any paths guaranteed by this package exist on sys.path
// Only called once as we are first loaded into the process.
void AddStandardPaths()
@ -136,9 +101,14 @@ void AddStandardPaths()
// Put {bin}\Python on the path if it exists.
nsresult rv;
nsCOMPtr<nsIFile> aFile;
rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR, getter_AddRefs(aFile));
// XXX - this needs more thought for XULRunner - we want to stick the global
// 'python' dir on sys.path (for xpcom etc), but also want to support a way
// of adding a local application directory (in the case of xulrunner) too.
// NS_XPCOM_CURRENT_PROCESS_DIR is the XULRunner app dir (ie, where application.ini lives)
// NS_GRE_DIR is the 'bin' dir for XULRunner itself.
rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(aFile));
if (NS_FAILED(rv)) {
LogStartupError("The Python XPCOM loader could not locate the 'bin' directory");
PyXPCOM_LogError("The Python XPCOM loader could not locate the 'bin' directory");
return;
}
aFile->Append(NS_LITERAL_STRING("python"));
@ -146,14 +116,15 @@ void AddStandardPaths()
aFile->GetPath(pathBuf);
PyObject *obPath = PySys_GetObject("path");
if (!obPath) {
LogStartupError("The Python XPCOM loader could not get the Python sys.path variable");
PyXPCOM_LogError("The Python XPCOM loader could not get the Python sys.path variable");
return;
}
// XXX - this should use the file-system encoding...
NS_LossyConvertUTF16toASCII pathCBuf(pathBuf);
// This is too early for effective LogDebug
LogStartupDebug("The Python XPCOM loader is adding '%s' to sys.path",
pathCBuf.get());
#ifdef NS_DEBUG
PR_fprintf(PR_STDERR,"The Python XPCOM loader is adding '%s' to sys.path\n",
pathCBuf.get());
#endif
PyObject *newStr = PyString_FromString(pathCBuf.get());
PyList_Insert(obPath, 0, newStr);
Py_XDECREF(newStr);
@ -161,26 +132,33 @@ void AddStandardPaths()
// - ie, look for .pth files, etc
nsCAutoString cmdBuf(NS_LITERAL_CSTRING("import site;site.addsitedir(r'") + pathCBuf + NS_LITERAL_CSTRING("')\n"));
if (0 != PyRun_SimpleString((char *)cmdBuf.get())) {
LogStartupError("The directory '%s' could not be added as a site directory", pathCBuf.get());
PyXPCOM_LogError("The directory '%s' could not be added as a site directory", pathCBuf.get());
PyErr_Clear();
}
// and somewhat like Python itself (site, citecustomize), we attempt
// to import "sitepyxpcom" ignoring ImportError
if (NULL==PyImport_ImportModule("sitepyxpcom")) {
PyObject *mod = PyImport_ImportModule("sitepyxpcom");
if (NULL==mod) {
if (!PyErr_ExceptionMatches(PyExc_ImportError))
LogStartupError("Failed to import 'sitepyxpcom'");
PyXPCOM_LogError("Failed to import 'sitepyxpcom'");
PyErr_Clear();
}
} else
Py_DECREF(mod);
}
static PRBool bIsInitialized = PR_FALSE;
// Our 'entry point' into initialization - just call this any time you
// like, and the world will be setup!
PYXPCOM_EXPORT void
PyXPCOM_EnsurePythonEnvironment(void)
{
static PRBool bIsInitialized = PR_FALSE;
// Must be thread-safe
CEnterLeaveXPCOMFramework _celf;
// Must be thread-safe - but only while set to FALSE - so check for
// set before getting the lock - then check again after!
if (bIsInitialized)
return;
CEnterLeaveXPCOMFramework _celf;
if (bIsInitialized)
return; // another thread beat us to the init.
#if defined(XP_UNIX) && !defined(XP_MACOSX)
/* *sob* - seems necessary to open the .so as RTLD_GLOBAL. Without
@ -238,6 +216,36 @@ PyXPCOM_EnsurePythonEnvironment(void)
// Add the standard extra paths we assume
AddStandardPaths();
// The exception object pyxpcom uses.
if (PyXPCOM_Error == NULL) {
PRBool rc = PR_FALSE;
PyObject *mod = NULL;
mod = PyImport_ImportModule("xpcom");
if (mod!=NULL) {
PyXPCOM_Error = PyObject_GetAttrString(mod, "Exception");
Py_DECREF(mod);
}
rc = (PyXPCOM_Error != NULL);
}
// Register our custom interfaces.
Py_nsISupports::InitType();
Py_nsIComponentManager::InitType();
Py_nsIInterfaceInfoManager::InitType();
Py_nsIEnumerator::InitType();
Py_nsISimpleEnumerator::InitType();
Py_nsIInterfaceInfo::InitType();
Py_nsIInputStream::InitType();
Py_nsIClassInfo::InitType();
Py_nsIVariant::InitType();
bIsInitialized = PR_TRUE;
// import the xpcom module itself to setup the loggers etc.
// We must do this after setting bIsInitialized, as it too tries to
// initialize!
PyImport_ImportModule("xpcom");
// If we initialized Python, then we will also have acquired the thread
// lock. In that case, we want to leave it unlocked, so other threads
// are free to run, even if they aren't running Python code.
@ -245,7 +253,6 @@ PyXPCOM_EnsurePythonEnvironment(void)
NS_TIMELINE_STOP_TIMER("PyXPCOM: Python threadstate setup");
NS_TIMELINE_MARK_TIMER("PyXPCOM: Python threadstate setup");
bIsInitialized = PR_TRUE;
}
void pyxpcom_construct(void)
@ -270,92 +277,3 @@ struct DllInitializer {
pyxpcom_destruct();
}
} dll_initializer;
////////////////////////////////////////////////////////////
// Other helpers/global functions.
//
PYXPCOM_EXPORT PRBool
PyXPCOM_Globals_Ensure()
{
PRBool rc = PR_TRUE;
// The exception object - we load it from .py code!
if (PyXPCOM_Error == NULL) {
rc = PR_FALSE;
PyObject *mod = NULL;
mod = PyImport_ImportModule("xpcom");
if (mod!=NULL) {
PyXPCOM_Error = PyObject_GetAttrString(mod, "Exception");
Py_DECREF(mod);
}
rc = (PyXPCOM_Error != NULL);
}
if (!rc)
return rc;
static PRBool bHaveInitXPCOM = PR_FALSE;
if (!bHaveInitXPCOM) {
nsCOMPtr<nsIThread> thread_check;
// xpcom appears to assert if already initialized
// Is there an official way to determine this?
if (NS_FAILED(nsIThread::GetMainThread(getter_AddRefs(thread_check)))) {
// not already initialized.
#ifdef XP_WIN
// On Windows, we need to locate the Mozilla bin
// directory. This by using locating a Moz DLL we depend
// on, and assume it lives in that bin dir. Different
// moz build types (eg, xulrunner, suite) package
// XPCOM itself differently - but all appear to require
// nspr4.dll - so this is what we use.
char landmark[MAX_PATH+1];
HMODULE hmod = GetModuleHandle("nspr4.dll");
if (hmod==NULL) {
PyErr_SetString(PyExc_RuntimeError, "We dont appear to be linked against nspr4.dll.");
return PR_FALSE;
}
GetModuleFileName(hmod, landmark, sizeof(landmark)/sizeof(landmark[0]));
char *end = landmark + (strlen(landmark)-1);
while (end > landmark && *end != '\\')
end--;
if (end > landmark) *end = '\0';
nsCOMPtr<nsILocalFile> ns_bin_dir;
NS_ConvertASCIItoUTF16 strLandmark(landmark);
#ifdef NS_BUILD_REFCNT_LOGGING
// In an interesting chicken-and-egg problem, we
// throw assertions in creating the nsILocalFile
// we need to pass to InitXPCOM!
nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
#endif
NS_NewLocalFile(strLandmark, PR_FALSE, getter_AddRefs(ns_bin_dir));
#ifdef NS_BUILD_REFCNT_LOGGING
nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
#endif
nsresult rv = NS_InitXPCOM2(nsnull, ns_bin_dir, nsnull);
#else
// Elsewhere, Mozilla can find it itself (we hope!)
nsresult rv = NS_InitXPCOM2(nsnull, nsnull, nsnull);
#endif // XP_WIN
if (NS_FAILED(rv)) {
PyErr_SetString(PyExc_RuntimeError, "The XPCOM subsystem could not be initialized");
return PR_FALSE;
}
}
// Even if xpcom was already init, we want to flag it as init!
bHaveInitXPCOM = PR_TRUE;
// Register our custom interfaces.
Py_nsISupports::InitType();
Py_nsIComponentManager::InitType();
Py_nsIInterfaceInfoManager::InitType();
Py_nsIEnumerator::InitType();
Py_nsISimpleEnumerator::InitType();
Py_nsIInterfaceInfo::InitType();
Py_nsIInputStream::InitType();
Py_nsIClassInfo::InitType();
Py_nsIVariant::InitType();
}
return rc;
}

View File

@ -69,7 +69,6 @@ CPPSRCS = \
include $(topsrcdir)/config/config.mk
include $(topsrcdir)/config/rules.mk
CXXFLAGS += -DPYTHON_SO=\"libpython$(MOZ_PYTHON_VER_DOTTED).so\"
EXTRA_DSO_LDOPTS += $(MOZ_COMPONENT_LIBS)
clobber::

View File

@ -51,7 +51,10 @@
#include "nsIFile.h"
#include "nsIComponentRegistrar.h"
#include "nsIConsoleService.h"
#include "nspr.h" // PR_fprintf
#include "nsIThread.h"
#include "nsILocalFile.h"
#include "nsTraceRefcntImpl.h"
#ifdef XP_WIN
#ifndef WIN32_LEAN_AND_MEAN
@ -466,6 +469,66 @@ static struct PyMethodDef xpcom_methods[]=
}
// local helper to check that xpcom itself has been initialized.
// Theoretically this should only happen when a standard python program
// (ie, hosted by python itself) imports the xpcom module (ie, as part of
// the pyxpcom test suite), hence it lives here...
static PRBool EnsureXPCOM()
{
static PRBool bHaveInitXPCOM = PR_FALSE;
if (!bHaveInitXPCOM) {
nsCOMPtr<nsIThread> thread_check;
// xpcom appears to assert if already initialized
// Is there an official way to determine this?
if (NS_FAILED(nsIThread::GetMainThread(getter_AddRefs(thread_check)))) {
// not already initialized.
#ifdef XP_WIN
// On Windows, we need to locate the Mozilla bin
// directory. This by using locating a Moz DLL we depend
// on, and assume it lives in that bin dir. Different
// moz build types (eg, xulrunner, suite) package
// XPCOM itself differently - but all appear to require
// nspr4.dll - so this is what we use.
char landmark[MAX_PATH+1];
HMODULE hmod = GetModuleHandle("nspr4.dll");
if (hmod==NULL) {
PyErr_SetString(PyExc_RuntimeError, "We dont appear to be linked against nspr4.dll.");
return PR_FALSE;
}
GetModuleFileName(hmod, landmark, sizeof(landmark)/sizeof(landmark[0]));
char *end = landmark + (strlen(landmark)-1);
while (end > landmark && *end != '\\')
end--;
if (end > landmark) *end = '\0';
nsCOMPtr<nsILocalFile> ns_bin_dir;
NS_ConvertASCIItoUTF16 strLandmark(landmark);
#ifdef NS_BUILD_REFCNT_LOGGING
// In an interesting chicken-and-egg problem, we
// throw assertions in creating the nsILocalFile
// we need to pass to InitXPCOM!
nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
#endif
NS_NewLocalFile(strLandmark, PR_FALSE, getter_AddRefs(ns_bin_dir));
#ifdef NS_BUILD_REFCNT_LOGGING
nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
#endif
nsresult rv = NS_InitXPCOM2(nsnull, ns_bin_dir, nsnull);
#else
// Elsewhere, Mozilla can find it itself (we hope!)
nsresult rv = NS_InitXPCOM2(nsnull, nsnull, nsnull);
#endif // XP_WIN
if (NS_FAILED(rv)) {
PyErr_SetString(PyExc_RuntimeError, "The XPCOM subsystem could not be initialized");
return PR_FALSE;
}
}
// Even if xpcom was already init, we want to flag it as init!
bHaveInitXPCOM = PR_TRUE;
}
return PR_TRUE;
}
////////////////////////////////////////////////////////////
// The module init code.
//
@ -474,6 +537,10 @@ void
init_xpcom() {
PyObject *oModule;
// ensure xpcom already init
if (!EnsureXPCOM())
return;
// ensure the framework has valid state to work with.
if (!PyXPCOM_Globals_Ensure())
return;