You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mod_python-commits@quetz.apache.org by nl...@apache.org on 2004/12/19 20:28:59 UTC
svn commit: r122779 - /httpd/mod_python/trunk/lib/python/mod_python/apache.py /httpd/mod_python/trunk/src/mod_python.c
Author: nlehuen
Date: Sun Dec 19 11:28:58 2004
New Revision: 122779
URL: http://svn.apache.org/viewcvs?view=rev&rev=122779
Log:
Fix for bug #2 "multiple/redundant interpreter creation" (according to the JIRA repository) :
- in mod_python.c, an APR mutex is used to make sure that multiple interpreter are not created
- in apache.py, the import lock is used to make sure that the same module is not loaded multiple times
Modified:
httpd/mod_python/trunk/lib/python/mod_python/apache.py
httpd/mod_python/trunk/src/mod_python.c
Modified: httpd/mod_python/trunk/lib/python/mod_python/apache.py
Url: http://svn.apache.org/viewcvs/httpd/mod_python/trunk/lib/python/mod_python/apache.py?view=diff&rev=122779&p1=httpd/mod_python/trunk/lib/python/mod_python/apache.py&r1=122778&p2=httpd/mod_python/trunk/lib/python/mod_python/apache.py&r2=122779
==============================================================================
--- httpd/mod_python/trunk/lib/python/mod_python/apache.py (original)
+++ httpd/mod_python/trunk/lib/python/mod_python/apache.py Sun Dec 19 11:28:58 2004
@@ -1,6 +1,6 @@
#
- # Copyright 2004 Apache Software Foundation
- #
+ # Copyright 2004 Apache Software Foundation
+ #
# Licensed under the Apache License, Version 2.0 (the "License"); you
# may not use this file except in compliance with the License. You
# may obtain a copy of the License at
@@ -41,7 +41,7 @@
The actual stack string lives in the request object so
it can be manipulated by both apache.py and mod_python.c
"""
-
+
def __init__(self, req):
self.req = req
@@ -128,7 +128,7 @@
finally:
exc_traceback = None
- return result
+ return result
def FilterDispatch(self, filter):
@@ -154,7 +154,7 @@
# add the directory to pythonpath if
# not there yet, or pythonpath specified
-
+
if config.has_key("PythonPath"):
# we want to do as little evaling as possible,
# so we remember the path in un-evaled form and
@@ -201,7 +201,7 @@
req.status = status
else:
result = value.args[0]
-
+
if type(result) != type(7):
s = "Value raised with SERVER_RETURN is invalid. It is a "
s = s + "%s, but it must be a tuple or an int." % type(result)
@@ -234,7 +234,7 @@
finally:
exc_traceback = None
- return OK
+ return OK
def HandlerDispatch(self, req):
"""
@@ -304,7 +304,7 @@
# stop cycling through handlers
if result != OK:
break
-
+
elif hlist.silent:
result = DECLINED
@@ -320,7 +320,7 @@
req.status = status
else:
result = value.args[0]
-
+
if type(result) != type(7):
s = "Value raised with SERVER_RETURN is invalid. It is a "
s = s + "%s, but it must be a tuple or an int." % type(result)
@@ -349,16 +349,16 @@
finally:
exc_traceback = None
- return result
+ return result
def ReportError(self, etype, evalue, etb, req=None, filter=None, srv=None,
phase="N/A", hname="N/A", debug=0):
- """
- This function is only used when debugging is on.
- It sends the output similar to what you'd see
- when using Python interactively to the browser
- """
+ """
+ This function is only used when debugging is on.
+ It sends the output similar to what you'd see
+ when using Python interactively to the browser
+ """
try: # try/finally
try: # try/except
@@ -388,7 +388,7 @@
for e in traceback.format_exception(etype, evalue, etb):
s = s + e + '\n'
s = s + "</pre>\n"
-
+
if filter:
filter.write(s)
filter.flush()
@@ -412,68 +412,74 @@
if it has changed since the last import.
"""
- # (Re)import
- if sys.modules.has_key(module_name):
-
- # The module has been imported already
- module = sys.modules[module_name]
-
- # but is it in the path?
- file = module.__dict__.get("__file__")
-
- # the "and not" part of this condition is to prevent execution
- # of arbitrary already imported modules, such as os. The
- # reason we use startswith as opposed to exact match is that
- # modules inside packages are actually in subdirectories.
-
- if not file or (path and not filter(file.startswith, path)):
- # there is a script by this name already imported, but it's in
- # a different directory, therefore it's a different script
- mtime, oldmtime = 0, -1
- elif autoreload:
- oldmtime = module.__dict__.get("__mtime__", 0)
- mtime = module_mtime(module)
- else:
- mtime, oldmtime = 0, 0
-
- else:
- mtime, oldmtime = 0, -1
-
- if mtime > oldmtime:
-
- # Import the module
- if log:
- if path:
- s = "mod_python: (Re)importing module '%s' with path set to '%s'" % (module_name, path)
+ # nlehuen: this is a big lock, we'll have to refine it later to get better performance.
+ # For now, we'll concentrate on thread-safety.
+ imp.acquire_lock()
+ try:
+ # (Re)import
+ if sys.modules.has_key(module_name):
+
+ # The module has been imported already
+ module = sys.modules[module_name]
+
+ # but is it in the path?
+ file = module.__dict__.get("__file__")
+
+ # the "and not" part of this condition is to prevent execution
+ # of arbitrary already imported modules, such as os. The
+ # reason we use startswith as opposed to exact match is that
+ # modules inside packages are actually in subdirectories.
+
+ if not file or (path and not filter(file.startswith, path)):
+ # there is a script by this name already imported, but it's in
+ # a different directory, therefore it's a different script
+ mtime, oldmtime = 0, -1
+ elif autoreload:
+ oldmtime = module.__dict__.get("__mtime__", 0)
+ mtime = module_mtime(module)
else:
- s = "mod_python: (Re)importing module '%s'" % module_name
- _apache.log_error(s, APLOG_NOERRNO|APLOG_NOTICE)
+ mtime, oldmtime = 0, 0
- parts = module_name.split('.')
- for i in range(len(parts)):
- f, p, d = imp.find_module(parts[i], path)
- try:
- mname = ".".join(parts[:i+1])
- module = imp.load_module(mname, f, p, d)
- finally:
- if f: f.close()
- if hasattr(module, "__path__"):
- path = module.__path__
+ else:
+ mtime, oldmtime = 0, -1
- if mtime == 0:
- mtime = module_mtime(module)
+ if mtime > oldmtime:
- module.__mtime__ = mtime
+ # Import the module
+ if log:
+ if path:
+ s = "mod_python: (Re)importing module '%s' with path set to '%s'" % (module_name, path)
+ else:
+ s = "mod_python: (Re)importing module '%s'" % module_name
+ _apache.log_error(s, APLOG_NOERRNO|APLOG_NOTICE)
- return module
+ parts = module_name.split('.')
+ for i in range(len(parts)):
+ f, p, d = imp.find_module(parts[i], path)
+ try:
+ mname = ".".join(parts[:i+1])
+ module = imp.load_module(mname, f, p, d)
+ finally:
+ if f: f.close()
+ if hasattr(module, "__path__"):
+ path = module.__path__
+
+ if mtime == 0:
+ mtime = module_mtime(module)
+
+ module.__mtime__ = mtime
+
+ return module
+ finally:
+ imp.release_lock()
def module_mtime(module):
"""Get modification time of module"""
mtime = 0
if module.__dict__.has_key("__file__"):
-
+
filepath = module.__file__
-
+
try:
# this try/except block is a workaround for a Python bug in
# 2.0, 2.1 and 2.1.1. See
@@ -537,7 +543,7 @@
req.add_common_vars()
env = req.subprocess_env.copy()
-
+
if req.path_info and len(req.path_info) > 0:
env["SCRIPT_NAME"] = req.uri[:-len(req.path_info)]
else:
@@ -620,28 +626,28 @@
self.buf = self.buf + self.req.read(self.BLOCK)
if len(self.buf) == x: # nothing read, eof
i = x - 1
- break
+ break
i = self.buf.find('\n', x)
-
+
# carve out the piece, then shorten the buffer
result = self.buf[:i+1]
self.buf = self.buf[i+1:]
self.pos = self.pos + len(result)
return result
-
+
class CGIStdout(NullIO):
"""
Class that allows writing to the socket directly for CGI.
"""
-
+
def __init__(self, req):
self.pos = 0
self.req = req
self.headers_sent = 0
self.headers = ""
-
+
def write(self, s):
if not s: return
@@ -661,7 +667,7 @@
headers_over = 1
else:
headers_over = 1
-
+
if headers_over:
# headers done, process them
ss[0] = ss[0].replace('\r\n', '\n')
@@ -684,7 +690,7 @@
self.req.write(ss[1])
else:
self.req.write(str(s))
-
+
self.pos = self.pos + len(s)
def tell(self): return self.pos
@@ -699,19 +705,19 @@
# save env
save_env = os.environ.copy()
-
+
si = sys.stdin
so = sys.stdout
os.environ.update(build_cgi_env(req))
-
+
sys.stdout = CGIStdout(req)
sys.stdin = CGIStdin(req)
sys.argv = [] # keeps cgi.py happy
return save_env, si, so
-
+
def restore_nocgi(sav_env, si, so):
""" see setup_cgi() """
@@ -727,7 +733,7 @@
sys.stdin = so
def init():
- """
+ """
This function is called by the server at startup time
"""
@@ -800,7 +806,7 @@
APLOG_EMERG = syslog.LOG_EMERG # system is unusable
APLOG_ALERT = syslog.LOG_ALERT # action must be taken immediately
APLOG_CRIT = syslog.LOG_CRIT # critical conditions
- APLOG_ERR = syslog.LOG_ERR # error conditions
+ APLOG_ERR = syslog.LOG_ERR # error conditions
APLOG_WARNING = syslog.LOG_WARNING # warning conditions
APLOG_NOTICE = syslog.LOG_NOTICE # normal but significant condition
APLOG_INFO = syslog.LOG_INFO # informational
@@ -814,7 +820,7 @@
APLOG_NOTICE = 5
APLOG_INFO = 6
APLOG_DEBUG = 7
-
+
APLOG_NOERRNO = 8
OK = REQ_PROCEED = 0
@@ -829,7 +835,7 @@
# legacy/mod_python things
REQ_ABORTED = HTTP_INTERNAL_SERVER_ERROR
-REQ_EXIT = "REQ_EXIT"
+REQ_EXIT = "REQ_EXIT"
SERVER_RETURN = _apache.SERVER_RETURN
PROG_TRACEBACK = "PROG_TRACEBACK"
@@ -861,8 +867,8 @@
# for req.proxyreq
PROXYREQ_NONE = 0 # No proxy
-PROXYREQ_PROXY = 1 # Standard proxy
-PROXYREQ_REVERSE = 2 # Reverse proxy
+PROXYREQ_PROXY = 1 # Standard proxy
+PROXYREQ_REVERSE = 2 # Reverse proxy
# methods for req.allow_method()
M_GET = 0 # RFC 2616: HTTP
@@ -872,9 +878,9 @@
M_CONNECT = 4
M_OPTIONS = 5
M_TRACE = 6 # RFC 2616: HTTP
-M_PATCH = 7
+M_PATCH = 7
M_PROPFIND = 8 # RFC 2518: WebDAV
-M_PROPPATCH = 9
+M_PROPPATCH = 9
M_MKCOL = 10
M_COPY = 11
M_MOVE = 12
@@ -900,26 +906,26 @@
# for mpm_query
-AP_MPMQ_NOT_SUPPORTED = 0 # This value specifies whether
- # an MPM is capable of
- # threading or forking.
-AP_MPMQ_STATIC = 1 # This value specifies whether
+AP_MPMQ_NOT_SUPPORTED = 0 # This value specifies whether
+ # an MPM is capable of
+ # threading or forking.
+AP_MPMQ_STATIC = 1 # This value specifies whether
# an MPM is using a static # of
- # threads or daemons.
-AP_MPMQ_DYNAMIC = 2 # This value specifies whether
+ # threads or daemons.
+AP_MPMQ_DYNAMIC = 2 # This value specifies whether
# an MPM is using a dynamic # of
- # threads or daemons.
+ # threads or daemons.
+
+AP_MPMQ_MAX_DAEMON_USED = 1 # Max # of daemons used so far
+AP_MPMQ_IS_THREADED = 2 # MPM can do threading
+AP_MPMQ_IS_FORKED = 3 # MPM can do forking
+AP_MPMQ_HARD_LIMIT_DAEMONS = 4 # The compiled max # daemons
+AP_MPMQ_HARD_LIMIT_THREADS = 5 # The compiled max # threads
+AP_MPMQ_MAX_THREADS = 6 # # of threads/child by config
+AP_MPMQ_MIN_SPARE_DAEMONS = 7 # Min # of spare daemons
+AP_MPMQ_MIN_SPARE_THREADS = 8 # Min # of spare threads
+AP_MPMQ_MAX_SPARE_DAEMONS = 9 # Max # of spare daemons
+AP_MPMQ_MAX_SPARE_THREADS = 10 # Max # of spare threads
+AP_MPMQ_MAX_REQUESTS_DAEMON= 11 # Max # of requests per daemon
+AP_MPMQ_MAX_DAEMONS = 12 # Max # of daemons by config
-AP_MPMQ_MAX_DAEMON_USED = 1 # Max # of daemons used so far
-AP_MPMQ_IS_THREADED = 2 # MPM can do threading
-AP_MPMQ_IS_FORKED = 3 # MPM can do forking
-AP_MPMQ_HARD_LIMIT_DAEMONS = 4 # The compiled max # daemons
-AP_MPMQ_HARD_LIMIT_THREADS = 5 # The compiled max # threads
-AP_MPMQ_MAX_THREADS = 6 # # of threads/child by config
-AP_MPMQ_MIN_SPARE_DAEMONS = 7 # Min # of spare daemons
-AP_MPMQ_MIN_SPARE_THREADS = 8 # Min # of spare threads
-AP_MPMQ_MAX_SPARE_DAEMONS = 9 # Max # of spare daemons
-AP_MPMQ_MAX_SPARE_THREADS = 10 # Max # of spare threads
-AP_MPMQ_MAX_REQUESTS_DAEMON= 11 # Max # of requests per daemon
-AP_MPMQ_MAX_DAEMONS = 12 # Max # of daemons by config
-
Modified: httpd/mod_python/trunk/src/mod_python.c
Url: http://svn.apache.org/viewcvs/httpd/mod_python/trunk/src/mod_python.c?view=diff&rev=122779&p1=httpd/mod_python/trunk/src/mod_python.c&r1=122778&p2=httpd/mod_python/trunk/src/mod_python.c&r2=122779
==============================================================================
--- httpd/mod_python/trunk/src/mod_python.c (original)
+++ httpd/mod_python/trunk/src/mod_python.c Sun Dec 19 11:28:58 2004
@@ -31,6 +31,8 @@
* (In a Python dictionary) */
static PyObject * interpreters = NULL;
+static apr_thread_mutex_t* interpreters_lock = 0;
+
apr_pool_t *child_init_pool = NULL;
/**
@@ -124,6 +126,7 @@
name = MAIN_INTERPRETER;
#ifdef WITH_THREAD
+ apr_thread_mutex_lock(interpreters_lock);
PyEval_AcquireLock();
#endif
@@ -149,6 +152,7 @@
#ifdef WITH_THREAD
PyEval_ReleaseLock();
+ apr_thread_mutex_unlock(interpreters_lock);
#endif
if (! idata) {
@@ -470,6 +474,9 @@
const char *userdata_key = "python_init";
apr_status_t rc;
+ /* fudge for Mac OS X with Apache 1.3 where Py_IsInitialized() broke */
+ static int initialized = 0;
+
apr_pool_userdata_get(&data, userdata_key, s->process->pool);
if (!data) {
apr_pool_userdata_set((const void *)1, userdata_key,
@@ -491,13 +498,15 @@
}
/* initialize global Python interpreter if necessary */
- if (! Py_IsInitialized())
+ if (initialized == 0 || !Py_IsInitialized())
{
-
+ initialized = 1;
+
/* initialze the interpreter */
Py_Initialize();
#ifdef WITH_THREAD
+ apr_thread_mutex_create(&interpreters_lock,APR_THREAD_MUTEX_UNNESTED,p);
/* create and acquire the interpreter lock */
PyEval_InitThreads();
#endif