Opened 3 years ago

Closed 10 months ago

#19137 closed Bug (fixed)

runserver child started via autoreload won't always exit cleanly

Reported by: santtu Owned by: nobody
Component: Core (Management commands) Version: 1.4
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: yes Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

When using django.utils.autoreload (for example, runserver with use_reloader option enabled) it is possible to get into a situation where you might think the server has been killed, but actually is not and is listening to the port.

Steps to reproduce:

  1. Start application, when use_reloader is enabled (manage.py runserver)
  2. Search for a pair of the manage.py processes (ps -aef --forest | grep runserver etc.), make a note of which is the parent and which is the child
  3. Kill the parent using command-line kill "kill -INT <parent pid>"
  4. Look at ps output, and you'll find the child still alive, and via lsof -i | grep LISTEN you can see it is still listening on the server port.

Normally if you use runserver from a terminal, pressing control-C will send SIGINT to the whole foreground process group. Both the parent and child will thus receive SIGINT and will exit cleanly.

If you however kill the *parent* only, the child will not receive any signal, and thus will happily keep running.

This is a bit of a bummer when doing test automation, which tries to clean up the test environment by killing the parent -- but the child won't go away, and keeping server port reserved.

It is possible to do a semi-work-around by spawning runserver in its own process group, but it requires some careful environment crafting (and I haven't been able to get it reliably working on macos -- there are apparently some semantic differences in setpgrp wrt/ linux and macos). And of course is hardly obvious for anyone encountering this problem for the first time.

I have a patch for this and I'll make a pull request and link to that when I have a ticket id..

Change History (8)

comment:1 Changed 3 years ago by anonymous

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

BTW, there is also another obvious work-around when doing test automation: disable autoreloader.

comment:2 Changed 3 years ago by santtu

comment:3 Changed 3 years ago by lrekucki

  • Has patch set
  • Needs tests set

comment:4 Changed 2 years ago by aaugustin

  • Triage Stage changed from Unreviewed to Accepted
  • Type changed from Uncategorized to Bug

comment:5 Changed 2 years ago by ramiro

#16982 was closed as duplicate of this one.

Feedback about how things work on Mac OS X and Windows once the PR has been applied is welcome.

comment:6 Changed 2 years ago by fabiofz@…

This patch doesn't seem to work on windows.

On a quick search I found: http://danielkaes.wordpress.com/2009/06/04/how-to-catch-kill-events-with-python/ (the last comment implies that killing a process with the task manager in windows will kill all threads, giving no further chance to actually treat the exception, in which case, a signal-based approach won't work on windows).

comment:7 Changed 21 months ago by timo

  • Patch needs improvement set

Setting patch needs improvement since it doesn't work on Windows according to the previous comment.

comment:8 Changed 10 months ago by timo

  • Resolution set to fixed
  • Status changed from new to closed

I think we should focus on gunicorn integration (#21978) rather than what seems to be an obscure issue.

Actually, I think this may be fixed by #21773.

Note: See TracTickets for help on using tickets.
Back to Top