2

I have a simple Python app that runs in two threads. One is SMTP server, the other is HTTP server. When I start it in terminal it does not react on Ctrl+C. Here is the code:

import asyncore
import threading
import SimpleHTTPServer
import SocketServer

from smtpd import SMTPServer


class MailHoleSMTP(SMTPServer):
    def process_message(self, peer, mailfrom, rcpttos, data):
        pass


def run_smtp():
    MailHoleSMTP(('localhost', 1025), None)
    asyncore.loop()


def run_http():
    handler = SimpleHTTPServer.SimpleHTTPRequestHandler
    httpd = SocketServer.TCPServer(('localhost', 1080), handler)
    httpd.serve_forever()


if __name__ == '__main__':
    http_thread = threading.Thread(target=run_http)
    smtp_thread = threading.Thread(target=run_smtp)
    http_thread.start()
    smtp_thread.start()

    http_thread.join()
    smtp_thread.join()

I suspect that something might be wrong with that serve_forever() call, maybe it does not play well with threads or something. What can I do to make it react on Ctrl+C?

UPD: It does not work (for both threads) even if I run only one of the two threads.

4
  • Do you see any output, possibly a traceback? Commented Sep 1, 2014 at 18:39
  • Nope, nothing, just ^C^C^C^C^C^C^C^C :) Commented Sep 1, 2014 at 18:43
  • 3 threads, not 2. You're not counting the main thread, which is the one that catches the keyboard interrupt. Commented Sep 1, 2014 at 19:05
  • Related question: python - threading ignores KeyboardInterrupt exception - Stack Overflow Commented Dec 13, 2023 at 7:31

1 Answer 1

3

So there are two problems to address here. First is that a joined thread will not respond to any signals. Fix that by looping to check for a signal once every second:

while http_thread.is_alive():
    http_thread.join(1)
#similar for smtp_thread

This will make your program respond to KeyboardInterrupt signals, but now you will notice that the whole thing doesn't shut down cleanly. The reason: signals are not propagated to other threads, and the whole process is not allowed to shut down until your worker threads finish (because they are non-daemonic). To fix, the simplest way is to just make them daemon threads:

http_thread = threading.Thread(target=run_http)
http_thread.daemon = True
smtp_thread = threading.Thread(target=run_smtp)
smtp_thread.daemon = True
...

Signaling is a pretty common topic in multithreaded applications, so you may want to do more reading.

Sign up to request clarification or add additional context in comments.

1 Comment

I've tried this but it looks that thread.join() blocks processing signals. But try to replace it with while thread.is_alive(): time.sleep(1) - works for me

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.