Fixed #18251 -- Removed a deadlock possibility in apploading

Thanks to harm for the report and comments.
This commit is contained in:
Anssi Kääriäinen 2012-05-11 19:41:47 +03:00
parent 9974069620
commit 2b9fb2e644
1 changed files with 15 additions and 4 deletions

View File

@ -6,9 +6,9 @@ from django.utils.datastructures import SortedDict
from django.utils.importlib import import_module from django.utils.importlib import import_module
from django.utils.module_loading import module_has_submodule from django.utils.module_loading import module_has_submodule
import imp
import sys import sys
import os import os
import threading
__all__ = ('get_apps', 'get_app', 'get_models', 'get_model', 'register_models', __all__ = ('get_apps', 'get_app', 'get_models', 'get_model', 'register_models',
'load_app', 'app_cache_ready') 'load_app', 'app_cache_ready')
@ -39,7 +39,6 @@ class AppCache(object):
handled = {}, handled = {},
postponed = [], postponed = [],
nesting_level = 0, nesting_level = 0,
write_lock = threading.RLock(),
_get_models_cache = {}, _get_models_cache = {},
) )
@ -54,7 +53,14 @@ class AppCache(object):
""" """
if self.loaded: if self.loaded:
return return
with self.write_lock: # Note that we want to use the import lock here - the app loading is
# in many cases initiated implicitly by importing, and thus it is
# possible to end up in deadlock when one thread initiates loading
# without holding the importer lock and another thread then tries to
# import something which also launches the app loading. For details of
# this situation see #18251.
imp.acquire_lock()
try:
if self.loaded: if self.loaded:
return return
for app_name in settings.INSTALLED_APPS: for app_name in settings.INSTALLED_APPS:
@ -65,6 +71,8 @@ class AppCache(object):
for app_name in self.postponed: for app_name in self.postponed:
self.load_app(app_name) self.load_app(app_name)
self.loaded = True self.loaded = True
finally:
imp.release_lock()
def _label_for(self, app_mod): def _label_for(self, app_mod):
""" """
@ -135,7 +143,8 @@ class AppCache(object):
the app has no models in it and 'emptyOK' is True, returns None. the app has no models in it and 'emptyOK' is True, returns None.
""" """
self._populate() self._populate()
with self.write_lock: imp.acquire_lock()
try:
for app_name in settings.INSTALLED_APPS: for app_name in settings.INSTALLED_APPS:
if app_label == app_name.split('.')[-1]: if app_label == app_name.split('.')[-1]:
mod = self.load_app(app_name, False) mod = self.load_app(app_name, False)
@ -146,6 +155,8 @@ class AppCache(object):
else: else:
return mod return mod
raise ImproperlyConfigured("App with label %s could not be found" % app_label) raise ImproperlyConfigured("App with label %s could not be found" % app_label)
finally:
imp.release_lock()
def get_app_errors(self): def get_app_errors(self):
"Returns the map of known problems with the INSTALLED_APPS." "Returns the map of known problems with the INSTALLED_APPS."