Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Ticket #321 (defect)

Opened 3 years ago

Last modified 1 year ago

ctrl-c does not work after server.restart() is called (win32)

Status: closed (fixed)

Reported by: dowski Assigned to: fumanchu
Priority: normal Milestone: 2.1-rc2
Component: CherryPy code Keywords:
Cc:

A call to cherrypy.server.restart() keeps the console window on win32 from accepting ctrl-c to stop the server. If your code calls cherrypy.server.stop() from within, the server will stop ok.

Attachments

server.py.patch (3.5 kB) - added by dowski on 09/26/05 22:26:28.
patch to adjust cherrypy.server.restart() behavior and implement supend/resume
restart_test.py (0.7 kB) - added by dowski on 09/26/05 22:27:37.
small demo app for testing restart behavior
irc_092705.txt (3.2 kB) - added by Christian Wyglendowski <christian@dowski.com> on 09/27/05 15:38:15.
some good ideas - so I don't forget…
littlethreadtest.py (396 bytes) - added by dowski on 09/28/05 23:10:56.
Article A - the glove found at the defendant's house

Change History

09/26/05 14:27:42: Modified by dowski

On a related note, and something that would go some ways towards solving this issue - there should be a distinction between stopping and shutting down a CherryPy application.

Stopping should halt the app server from handling requests; essentially stopping the app server, but not ending the application.

Shutting down should take it a step further and actually end the application.

09/26/05 22:24:14: Modified by dowski

Ok, I have a patch that corrects the restart behavior on win32.

It also goes a bit further to address my comment above and implements suspend() and resume() functions that do pretty much what you would think. If I am thinking right (don't listen to speno :-) ), this should allow a separate thread (perhaps a GUI control app or something) to suspend, resume, and stop a running CP application. Anyhow..

The main changes that I made are:

  • A global "command" variable (valid values are 'start', 'stop', 'suspend')
  • start() now contains a loop that dispatches differently depending upon the value of command
  • When restart is called, the server is not restarted in another thread. Instead, the current server exits back to the loop in start(), where it, um _start()s again

The names of some of the original functions now belie their functionality somewhat. I didn't bother those sorts of tedious changes at this point because I wanted to get some feedback on my method of solving this problem.

Even though the CP test suite passes on my platform (WinXP), it would be a good idea to test it on some others. Check out the patch and the accompanying test application, and let me know what you think.

09/26/05 22:26:28: Modified by dowski

  • attachment server.py.patch added.

patch to adjust cherrypy.server.restart() behavior and implement supend/resume

09/26/05 22:27:37: Modified by dowski

  • attachment restart_test.py added.

small demo app for testing restart behavior

09/27/05 15:38:15: Modified by Christian Wyglendowski <christian@dowski.com>

  • attachment irc_092705.txt added.

some good ideas - so I don't forget...

09/28/05 17:54:41: Modified by fumanchu

  • owner changed from rdelon to fumanchu.
  • status changed from new to assigned.
  • milestone changed from 2.1-final to 2.1-rc2.

It's *possible* that this was fixed with [691], care to try it?

Regardless, much has changed (underneath this patch) with [691] (see source:/trunk/cherrypy/server.py#691). Some notes:

1. There's no more "finally" clause in server.run_server(); server.stop() was being called twice, but should only be called if KeyboardInterrupt or SystemExit is raised.

2. You don't need to "wait for worker threads to stop" after httpstop()--HTTP servers MUST stop all worker threads before their .stop() method returns. I'll try to write this fact (and some related issues) into the book.

3. I'm not a fan of putting a time.sleep() into server.py in order to implement suspend. Perhaps I'm misunderstanding your design, but I think the caller should instead write something like the current innards of restart():

server.stop()
while waiting:
    time.sleep(0.2)
server.start()
server.wait_until_ready()

4. The restart function currently knows what server to start via the local "http" variable; that should probably be changed into a "httpClass" global so the example in point 3 can actually work. ;)

5. The restart function works better if it starts a new thread, because it allows a caller function to more easily call server.wait_until_ready (a new function in [691]).

6. Plus all that stuff in the IRC note. ;)

09/28/05 23:08:38: Modified by dowski

Well, still no-go with a restart. In fact, I get a traceback now:

  File "C:\Python24\Lib\site-packages\cherrypy\_cphttptools.py", line 272, in run
    main()
  File "C:\Python24\Lib\site-packages\cherrypy\_cphttptools.py", line 497, in main
    body = page_handler(*args, **cherrypy.request.paramMap)
  File "C:\Documents and Settings\Christian\Desktop\cp_playground\restart_test.py", line 8, in restart
    cherrypy.server.restart()
  File "C:\Python24\Lib\site-packages\cherrypy\server.py", line 239, in restart
    wait_until_ready()
  File "C:\Python24\Lib\site-packages\cherrypy\server.py", line 257, in wait_until_ready
    wait_for_occupied_port(host, port)
  File "C:\Python24\Lib\site-packages\cherrypy\server.py", line 295, in wait_for_occupied_port
    raise cherrypy.NotReady("Port not bound.")
NotReady: Port not bound.

127.0.0.1 - - [2005/09/28 22:16:09] "GET /restart HTTP/1.1" 500 1480

A few questions/observations:

On # 3 - Do you think that a suspend command should be up to the client to create? The use of "suspend" in my mind moved towards satisfying the goal of my first comment on this ticket (being able to stop the app server without stopping the app).

On # 5 - I think that starting the server in a new thread is close to the root of the problem. When server.restart() launches the appserver in a new thread, server.start() ends, thus ending the main thread of the user's application. Run the attached littlethreadtest.py to see what I mean - try and interrupt it with a Ctrl-C. If you run restart_test.py, and visit http://localhost:8080/restart at least once, you will get the same sort of behavior.

That is why I had a loop in start(). It at least allowed a "restart" without requiring a new thread. But I guess that introduces issues of its own...

09/28/05 23:10:56: Modified by dowski

  • attachment littlethreadtest.py added.

Article A - the glove found at the defendant's house

09/29/05 08:59:13: Modified by dowski

Some more observations:

I finally got around to testing the restart functionality in Linux. The rc1 code seems to shutdown, rather than restart, when server.restart() is called from within an exposed method. The code in [691] actually restarts, and you can sent a Ctrl-C to successfully stop the server after up to 2 restarts. After that, sending a Ctrl-C hangs the process and it must be killed.

I also realize that I might be wrong in my second comment above (regarding fumanchu's point 5). Either that, or at least my "littlethreadtest.py" doesn't represent the problem well at all.

In any event, the plot thickens...

09/29/05 18:38:59: Modified by fumanchu

Was this fixed in [693]?

09/29/05 20:18:06: Modified by dowski

A little closer it seems. The server does restart now, but I still get the same traceback as above (the line numbers are different, of course). Once it is restarted, a Ctrl-C still does not stop the server.

09/30/05 12:50:50: Modified by fumanchu

http://www-unix.globus.org/mail_archive/python-discuss/2002/Archive/msg00011.html Bah. So how do I write a test for Ctrl-C that doesn't actually use any Python?

09/30/05 15:52:14: Modified by fumanchu

Okay, after much review, I don't think there's a way to solve this other than forcing developers to always call server.start from the main thread, and then having that main thread block indefinitely with a time.sleep() loop; the HTTP server would always have to be started in a new thread (inside run_server).

This means that the "initOnly" arg to start() should disappear (or at least do nothing), which would be a big change to the API, and would have to be discussed in depth before implementing it in 2.1.

Christian, the issue where restart() doesn't restart is something else: as the traceback points out, your port isn't getting rebound. I had the same problem, but oddly enough, only on localhost; once I switched to a remote client it went away...not sure why.

10/01/05 13:51:03: Modified by fumanchu

  • status changed from assigned to closed.
  • resolution set to fixed.

Fixed in changeset [698].

Hosted by WebFaction

Log in as guest/cpguest to create tickets