Description

While the standalone server is processing a request, it cannot be killed with ordinary signals (e.g. SIGINT (Ctrl-C) or SIGTERM). Only SIGKILL ("kill -9") seems to work. This is because server_standalone spawns non-daemon threads, which block the process from terminating until they exit (see http://docs.python.org/lib/thread-objects.html, under setDaemon()).

Steps to reproduce

  1. run moin ... server standalone in the foreground (attached to a terminal where you can use Ctrl-C to signal the process)

  2. start a request that takes a while (e.g. full-text search without indexes, or linkto: search)
  3. while the request is still running, hit Ctrl-C in the terminal where the server process is running
  4. note that moin may print "Thanks for using MoinMoin!" to stdout (or its log file), but it does *not* terminate

  5. when the request finishes and moin sends the response to the browser, then it terminates

The last two symptoms indicate that moin is receiving the SIGINT signal, but still keeps running.

This is particularly bad if running moin on underpowered hardware where you really did not mean to run that unindexed full-text search.

Example

See reproduction.

Component selection

Note: a comment in server_standalone.py says that the twisted server has the same problem. I have not attempted to reproduce, investigate, or fix the problem in the twisted server. I suspect the problem and the fix are similar, though.

Details

MoinMoin Version

1.7.1

OS and Version

Ubuntu 7.04 (feisty fawn), Linux 2.6.20

Python Version

2.5.1

Server Setup

standalone

Server Details

Language you are using the wiki in (set in the browser/UserPreferences)

en

Workaround

kill -9 on the server process sometimes works, but not always. (This confuses me.)

Discussion

The fix is quite easy: threads created by the server process should be daemon threads. Here is a patch for server_standalone:

--- a/MoinMoin/server/server_standalone.py      Mon Aug 18 14:49:18 2008 -0400
+++ b/MoinMoin/server/server_standalone.py      Tue Aug 19 17:43:40 2008 -0400
@@ -133,6 +133,7 @@ class ThreadingServer(SimpleServer):
                 return
             t = Thread(target=self.process_request_thread,
                        args=(request, client_address))
+            t.setDaemon(True)
             t.start()
         finally:
             self.lock.release()
@@ -186,6 +187,7 @@ class ThreadPoolServer(SimpleServer):
         from threading import Thread
         for dummy in range(self.poolSize):
             t = Thread(target=self.serve_forever_thread)
+            t.setDaemon(True)
             t.start()
         SimpleServer.serve_forever(self)

One catch: after adding this, killing the server process works. However, I am seeing weird errors from doing that. Example:

2008-08-20 11:49:42,834 INFO MoinMoin.server.server_standalone:498 Thanks for using MoinMoin!
Exception in thread Thread-4 (most likely raised during interpreter shutdown):
Traceback (most recent call last):
  File "threading.py", line 460, in __bootstrap
  File "threading.py", line 440, in run
  File "/src/wiki/moin-1.7.1/lib/MoinMoin/server/server_standalone.py", line 220, in serve_forever_thread
  File "SocketServer.py", line 254, in finish_request
  File "/src/wiki/moin-1.7.1/lib/MoinMoin/server/server_standalone.py", line 290, in __init__
  File "SocketServer.py", line 525, in __init__
<type 'exceptions.AttributeError'>: 'NoneType' object has no attribute 'exc_traceback'
Unhandled exception in thread started by
Error in sys.excepthook:

Original exception was:

About half the time, moin seems to shut down cleanly on Ctrl-C (the client waiting for a response gets nothing, of course), and half the time I get that error. It always shuts down quite quickly after Ctrl-C, though, and that IMHO is the important thing.

Follow-up patch is to remove the comment and debug prints from the last person to look at this:

--- a/MoinMoin/server/server_standalone.py      Tue Aug 19 17:43:40 2008 -0400
+++ b/MoinMoin/server/server_standalone.py      Wed Aug 20 11:34:25 2008 -0400
@@ -165,11 +165,6 @@ class ThreadPoolServer(SimpleServer):

     This server is 5 times faster than ThreadingServer for static
     files, and about the same for wiki pages.
-
-    TODO: sometimes the server won't exit on Conrol-C, and continue to
-    run with few threads (you can kill it with kill -9). Same problem
-    exist with the twisted server. When the problem is finally solved,
-    remove the commented debug prints.
     """
     use_threads = True

@@ -221,7 +216,6 @@ class ThreadPoolServer(SimpleServer):
             except:
                 self.handle_error(request, client_address)
             self.close_request(request)
-        # sys.stderr.write('thread exiting...\n')

     def pop_request(self):
         """ Pop a request from the queue
@@ -242,7 +236,6 @@ class ThreadPoolServer(SimpleServer):
                     self.lock.wait()
         finally:
             self.lock.release()
-        # sys.stderr.write('thread exiting...\n')
         sys.exit()

     def die(self):
@@ -258,7 +251,6 @@ class ThreadPoolServer(SimpleServer):
     def wake_all_threads(self):
         self.lock.acquire()
         try:
-            # sys.stderr.write('waking up all threads...\n')
             self.lock.notifyAll()
         finally:
             self.lock.release()

Plan


CategoryMoinMoinBugFixed

MoinMoin: MoinMoinBugs/CannotKillServerProcess (last edited 2008-08-31 21:11:57 by ThomasWaldmann)