tags:

views:

873

answers:

2

I've written a very simple python class which waits for connections on a socket. The intention is to stick this class into an existing app and asyncronously send data to connecting clients.

The problem is that when waiting on an socket.accept(), I cannot end my application by pressing ctrl-c. Neither can I detect when my class goes out of scope and notify it to end.

Ideally the application below should quit after the time.sleep(4) expires. As you can see below, I tried using select, but this also prevents the app from responding to ctrl-c. If I could detect that the variable 'a' has gone out of scope in the main method, I could set the quitting flag (and reduce the timeout on select to make it responsive).

Any ideas?

thanks


import sys
import socket
import threading
import time
import select

class Server( threading.Thread ):
    def __init__( self, i_port ):
        threading.Thread.__init__( self )
        self.quitting = False
        self.serversocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
        self.serversocket.bind( (socket.gethostname(), i_port ) )
        self.serversocket.listen(5)
        self.start()

    def run( self ):
        # Wait for connection
        while not self.quitting:
            rr,rw,err = select.select( [self.serversocket],[],[], 20 )
            if rr:
                (clientsocket, address) = self.serversocket.accept()
                clientsocket.close()

def main():
    a = Server( 6543 )
    time.sleep(4)

if __name__=='__main__':
  main()


+3  A: 

Add self.setDaemon(True) to the __init__ before self.start().

(In Python 2.6 and later, self.daemon = True is preferred).

The key idea is explained here:

The entire Python program exits when no alive non-daemon threads are left.

So, you need to make "daemons" of those threads who should not keep the whole process alive just by being alive themselves. The main thread is always non-daemon, by the way.

Alex Martelli
Thanks for that, it did the trick. I tried self.daemon = Truebut that did not do anything (I'm running python 2.5 on Win32). Changing it to self.setDaemon(True) did the trick though!thanks!
Brian O'Kennedy
+1  A: 

I don't recommend the setDaemon feature for normal shutdown. It's sloppy; instead of having a clean shutdown path for threads, it simply kills the thread with no chance for cleanup. It's good to set it, so your program doesn't get stuck if the main thread exits unexpectedly, but it's not a good normal shutdown path except for quick hacks.

import sys, os, socket, threading, time, select

class Server(threading.Thread):
    def __init__(self, i_port):
        threading.Thread.__init__(self)
        self.setDaemon(True)
        self.quitting = False
        self.serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.serversocket.bind((socket.gethostname(), i_port))
        self.serversocket.listen(5)
        self.start()

    def shutdown(self):
        if self.quitting:
            return

        self.quitting = True
        self.join()

    def run(self):
        # Wait for connection
        while not self.quitting:
            rr,rw,err = select.select([self.serversocket],[],[], 1)
            print rr
            if rr:
                (clientsocket, address) = self.serversocket.accept()
                clientsocket.close()

        print "shutting down"
        self.serversocket.close()

def main():
    a = Server(6543)
    try:
        time.sleep(4)
    finally:
        a.shutdown()

if __name__=='__main__':
  main()

Note that this will delay for up to a second after calling shutdown(), which is poor behavior. This is normally easy to fix: create a wakeup pipe() that you can write to, and include it in the select; but although this is very basic, I couldn't find any way to do this in Python. (os.pipe() returns file descriptors, not file objects that we can write to.) I havn't dig deeper, since it's tangental to the question.

Glenn Maynard
You can wrap a filedescriptor into a Python file object with os.fdopen. But I disagree that anything more than setDaemon is warranted when there's no particular cleanup needed in the thread -- it's just a small but dispensable complication.
Alex Martelli
I tried an approach very similar to this - my ShutDown method simply set a IsQuitting flag and connected to the socket in question, thereby unblocking the accept() call, allowing the thread to quit. It does however rely on the user calling ShutDown(). Thanks for the suggestion though.
Brian O'Kennedy
For anything bigger than a simple hack, you're going to need to be able to shut down a thread without exiting the entire process. Even a simple servers wanting to log "successful shut down" will need to do this, or it'll still be accepting connections after logging shutdown.
Glenn Maynard
select takes anything that is a filedescriptor *or* has a .fileno() to extract it. fdopen is useless.
FrozenFire