WSGI handler not properly handling lock
For this code in wsgi.py, seen in the latest release and the [source:django/trunk/django/core/handlers/wsgi.py@9996#L226 latest repository revision]:
if self._request_middleware is None:
self.initLock.acquire()
# Check that middleware is still uninitialised.
if self._request_middleware is None:
self.load_middleware()
self.initLock.release()
I noticed the issue of thread safety in this handler came up in #10470, but this code is still not thread safe because it it can be put into a deadlock state. It will deadlock after any of the middleware loading fails- for example, when the module pointed to by DJANGO_SETTINGS_MODULE isn't found (which is how I came to notice it). After the failure, initLock is held and so this can deadlock on initLock.acquire().
The fix is very simple, and should absolutely be done anywhere locking is done:
if self._request_middleware is None:
self.initLock.acquire()
try:
# Check that middleware is still uninitialised.
if self._request_middleware is None:
self.load_middleware()
finally:
self.initLock.release()
Of course using with keyword is an option as well. Also, I'm not sure how you would handle an indeterminate state between initialized and not initialized (e.g. some middleware loaded), but it might be a good idea to set self._request_middleware back to None before releasing the lock in case of an exception:
if self._request_middleware is None:
self.initLock.acquire()
try:
# Check that middleware is still uninitialised.
if self._request_middleware is None:
self.load_middleware()
except:
# possibly unload whatever middleware didn't fail here
self._request_middleware = None
raise
finally:
self.initLock.release()
Change History
(8)
Triage Stage: |
Unreviewed → Accepted
|
Resolution: |
→ fixed
|
Status: |
new → closed
|
Sorry, I forgot to point out a few things: