http://code.google.com/p/modwsgi/source/detail?r=637ee0510a49f5bf80fe49b2ee27158cf01855c6 http://code.google.com/p/modwsgi/source/detail?r=d40a1bf6b9ad9cacc50caf0b3806b9a231e0d4d7 --- mod_wsgi.c +++ mod_wsgi.c @@ -1,7 +1,7 @@ /* vim: set sw=4 expandtab : */ /* - * Copyright 2007-2010 GRAHAM DUMPLETON + * Copyright 2007-2011 GRAHAM DUMPLETON * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -386,6 +386,10 @@ #define WSGI_RELOAD_MODULE 0 #define WSGI_RELOAD_PROCESS 1 +/* Python interpreter state. */ + +static PyThreadState *wsgi_main_tstate = NULL; + /* Base server object. */ static server_rec *wsgi_server = NULL; @@ -3598,7 +3602,11 @@ */ if (!wsgi_daemon_pool && self->config->pass_apache_request) { +#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2 + object = PyCapsule_New(self->r, 0, 0); +#else object = PyCObject_FromVoidPtr(self->r, 0); +#endif PyDict_SetItemString(vars, "apache.request_rec", object); Py_DECREF(object); } @@ -5147,15 +5155,22 @@ PyObject *exitfunc = NULL; PyObject *module = NULL; + PyThreadState *tstate_enter = NULL; + /* - * We should always enter here with the Python GIL held, but - * there will be no active thread state. Note that it should - * be safe to always assume that the simplified GIL state - * API lock was originally unlocked as always calling in - * from an Apache thread outside of Python. + * We should always enter here with the Python GIL + * held and an active thread state. This should only + * now occur when shutting down interpreter and not + * when releasing interpreter as don't support + * recyling of interpreters within the process. Thus + * the thread state should be that for the main + * Python interpreter. Where dealing with a named + * sub interpreter, we need to change the thread + * state to that which was originally used to create + * that sub interpreter before doing anything. */ - PyEval_ReleaseLock(); + tstate_enter = PyThreadState_Get(); if (*self->name) { #if APR_HAS_THREADS @@ -5194,10 +5209,13 @@ tstate = self->tstate; #endif - PyEval_AcquireThread(tstate); + /* + * Swap to interpreter thread state that was used when + * the sub interpreter was created. + */ + + PyThreadState_Swap(tstate); } - else - PyGILState_Ensure(); if (self->owner) { Py_BEGIN_ALLOW_THREADS @@ -5492,20 +5510,7 @@ /* If we own it, we destroy it. */ - if (!self->owner) { - if (*self->name) { - tstate = PyThreadState_Get(); - - PyThreadState_Clear(tstate); - PyEval_ReleaseThread(tstate); - PyThreadState_Delete(tstate); - } - else - PyGILState_Release(PyGILState_UNLOCKED); - - PyEval_AcquireLock(); - } - else { + if (self->owner) { /* * We need to destroy all the thread state objects * associated with the interpreter. If there are @@ -5539,6 +5544,8 @@ /* Can now destroy the interpreter. */ Py_EndInterpreter(tstate); + + PyThreadState_Swap(tstate_enter); } free(self->name); @@ -5647,7 +5654,14 @@ ap_log_error(APLOG_MARK, WSGI_LOG_INFO(0), wsgi_server, "mod_wsgi (pid=%d): Terminating Python.", getpid()); - PyGILState_Ensure(); + /* + * We should be executing in the main thread again at this + * point but without the GIL, so simply restore the original + * thread state for that thread that we remembered when we + * initialised the interpreter. + */ + + PyEval_AcquireThread(wsgi_main_tstate); /* * Work around bug in Python 3.X whereby it will crash if @@ -5822,15 +5836,24 @@ /* Initialise threading. */ PyEval_InitThreads(); - PyThreadState_Swap(NULL); - PyEval_ReleaseLock(); + + /* + * We now want to release the GIL. Before we do that + * though we remember what the current thread state is. + * We will use that later to restore the main thread + * state when we want to cleanup interpreters on + * shutdown. + */ + + wsgi_main_tstate = PyThreadState_Get(); + PyEval_ReleaseThread(wsgi_main_tstate); wsgi_python_initialized = 1; - /* - * Register cleanups to be performed on parent restart - * or shutdown. This will destroy Python itself. - */ + /* + * Register cleanups to be performed on parent restart + * or shutdown. This will destroy Python itself. + */ #if AP_SERVER_MAJORVERSION_NUMBER < 2 ap_register_cleanup(p, NULL, wsgi_python_parent_cleanup, @@ -5879,7 +5902,11 @@ /* * This function should never be called when the - * Python GIL is held, so need to acquire it. + * Python GIL is held, so need to acquire it. Even + * though we may need to work with a sub + * interpreter, we need to acquire GIL against main + * interpreter first to work with interpreter + * dictionary. */ state = PyGILState_Ensure(); @@ -5998,6 +6025,8 @@ { PyThreadState *tstate = NULL; + PyGILState_STATE state; + /* * Need to release and destroy the thread state that * was created against the interpreter. This will @@ -6023,11 +6052,11 @@ * in its destruction if its the last reference. */ - PyEval_AcquireLock(); + state = PyGILState_Ensure(); Py_DECREF(handle); - PyEval_ReleaseLock(); + PyGILState_Release(state); } /* @@ -6630,7 +6659,14 @@ apr_thread_mutex_lock(wsgi_interp_lock); #endif - PyEval_AcquireLock(); + /* + * We should be executing in the main thread again at this + * point but without the GIL, so simply restore the original + * thread state for that thread that we remembered when we + * initialised the interpreter. + */ + + PyEval_AcquireThread(wsgi_main_tstate); /* * Extract a handle to the main Python interpreter from @@ -6668,7 +6704,13 @@ Py_DECREF(interp); - PyEval_ReleaseLock(); + /* + * The code which performs actual shutdown of the main + * interpreter expects to be called without the GIL, so + * we release it here again. + */ + + PyEval_ReleaseThread(wsgi_main_tstate); /* * Destroy Python itself including the main interpreter. @@ -8369,7 +8411,11 @@ */ if (!wsgi_daemon_pool && self->config->pass_apache_request) { +#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2 + object = PyCapsule_New(self->r, 0, 0); +#else object = PyCObject_FromVoidPtr(self->r, 0); +#endif PyDict_SetItemString(vars, "apache.request_rec", object); Py_DECREF(object); } @@ -10509,6 +10555,8 @@ { WSGIDaemonProcess *daemon = data; + PyGILState_STATE gilstate; + if (wsgi_server_config->verbose_debugging) { ap_log_error(APLOG_MARK, WSGI_LOG_DEBUG(0), wsgi_server, "mod_wsgi (pid=%d): Enable deadlock thread in " @@ -10523,8 +10571,8 @@ while (1) { apr_sleep(apr_time_from_sec(1)); - PyEval_AcquireLock(); - PyEval_ReleaseLock(); + gilstate = PyGILState_Ensure(); + PyGILState_Release(gilstate); apr_thread_mutex_lock(wsgi_shutdown_lock); wsgi_deadlock_shutdown_time = apr_time_now(); @@ -11101,6 +11149,7 @@ if (wsgi_python_after_fork) wsgi_python_init(p); +#if PY_MAJOR_VERSION < 3 /* * If mod_python is also being loaded and thus it was * responsible for initialising Python it can leave in @@ -11110,7 +11159,9 @@ * initialisation but in daemon process we skip the * mod_python child initialisation so the active thread * state still exists. Thus need to do a bit of a fiddle - * to ensure there is no active thread state. + * to ensure there is no active thread state. Don't need + * to worry about this with Python 3.X as mod_python + * only supports Python 2.X. */ if (!wsgi_python_initialized) { @@ -11126,6 +11177,7 @@ PyEval_ReleaseLock(); } +#endif /* * If the daemon is associated with a virtual host then @@ -13372,7 +13424,11 @@ */ if (!wsgi_daemon_pool && self->config->pass_apache_request) { +#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2 + object = PyCapsule_New(self->r, 0, 0); +#else object = PyCObject_FromVoidPtr(self->r, 0); +#endif PyDict_SetItemString(vars, "apache.request_rec", object); Py_DECREF(object); }