Opened 8 years ago
Closed 8 years ago
#27176 closed Bug (fixed)
django.setup() should raise an exception instead of hanging on re-entrant calls
Reported by: | Aymeric Augustin | Owned by: | nobody |
---|---|---|---|
Component: | Core (Other) | Version: | dev |
Severity: | Normal | Keywords: | app-loading |
Cc: | Triage Stage: | Ready for checkin | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
#25864 and #26152 discuss issues with calling django.setup()
in a file that is imported during the app-loading process. This is actually about apps.populate()
-- I wrote django.setup()
in the title because it's the public API people are familiar with.
Actually there are three cases.
- If
django.setup()
has already completed successfully, calling it again returns immediately.
if self.ready: return
- If
django.setup()
is running in another thread, calling it again returns as soon as that other thread has completed the process.
# populate() might be called by two threads in parallel on servers # that create threads before initializing the WSGI callable. with self._lock: if self.ready: return
- If
django.setup()
is running in the same thread, calling it must crash. Indeed,django.setup()
isn't allowed to return until app-loading has completed. The first call cannot complete without going through the second one, which cannot return, so there's a deadlock. The code tries to detect this condition:
# app_config should be pristine, otherwise the code below won't # guarantee that the order matches the order in INSTALLED_APPS. if self.app_configs: raise RuntimeError("populate() isn't reentrant")
#26152 suggest that the code hangs instead of raising an exception. Indeed, the second call must block on with self._lock:
, since self._lock
isn't reentrant. Making it a RLock
instead of a Lock
should allow the second thread to proceed and to hit that exception.
Also the error condition is incorrect: it won't trigger if the reentrant call happens while importing the first app. (We're reasoning in the single threaded case, which is easy.) Instead populate()
should set a private attribute before it starts importing apps and raise the exception if that attribute is set. Alternatively it could check if the RLock
is being taken for the second time, but no public API for this appears to exist.
Change History (9)
comment:1 by , 8 years ago
comment:2 by , 8 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:5 by , 8 years ago
Patch needs improvement: | unset |
---|
comment:6 by , 8 years ago
Patch needs improvement: | set |
---|
comment:7 by , 8 years ago
Patch needs improvement: | unset |
---|
comment:8 by , 8 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
The ticket description is purely based #26152 and on code analysis. I didn't test my theory.
I'd like to investigate further and submit a patch, but I'm not sure when I'll have time for that, so feel free to assign the ticket and work on it if you're interested.