Fixed #27176 -- Raised an exception for reentrant calls to apps.populate().

Thanks to Aymeric Augustin, Harry Percival, and Tim Graham.
This commit is contained in:
François Freitag 2017-02-25 12:56:24 -08:00 committed by Tim Graham
parent 12745d8a4f
commit fba4f831bc
1 changed files with 9 additions and 5 deletions

View File

@ -44,7 +44,8 @@ class Apps:
self.apps_ready = self.models_ready = self.ready = False self.apps_ready = self.models_ready = self.ready = False
# Lock for thread-safe population. # Lock for thread-safe population.
self._lock = threading.Lock() self._lock = threading.RLock()
self.loading = False
# Maps ("app_label", "modelname") tuples to lists of functions to be # Maps ("app_label", "modelname") tuples to lists of functions to be
# called when the corresponding model is ready. Used by this class's # called when the corresponding model is ready. Used by this class's
@ -72,10 +73,13 @@ class Apps:
if self.ready: if self.ready:
return return
# app_config should be pristine, otherwise the code below won't # An RLock prevents other threads from entering this section. The
# guarantee that the order matches the order in INSTALLED_APPS. # compare and set operation below is atomic.
if self.app_configs: if self.loading:
# Prevent reentrant calls to avoid running AppConfig.ready()
# methods twice.
raise RuntimeError("populate() isn't reentrant") raise RuntimeError("populate() isn't reentrant")
self.loading = True
# Phase 1: initialize app configs and import app modules. # Phase 1: initialize app configs and import app modules.
for entry in installed_apps: for entry in installed_apps:
@ -345,7 +349,7 @@ class Apps:
raise AppRegistryNotReady("App registry isn't ready yet.") raise AppRegistryNotReady("App registry isn't ready yet.")
self.stored_app_configs.append(self.app_configs) self.stored_app_configs.append(self.app_configs)
self.app_configs = OrderedDict() self.app_configs = OrderedDict()
self.apps_ready = self.models_ready = self.ready = False self.apps_ready = self.models_ready = self.loading = self.ready = False
self.clear_cache() self.clear_cache()
self.populate(installed) self.populate(installed)