Some programs are meant to run for indefinite amount of time. They don’t stop by themselves. Operators generally stop then for maintenance. Most common way to stop these types of programs is by pressing Ctr+C if the program is running in foreground or by sending SIGINT signal.
Here is a simple Python program that does not stop by itself.
import time
print ("Long duration task")
while True:
time.sleep(1)
We have a sleep statement – to avoid CPU hogging – inside an infinite loop. If we press Ctr+C to stop this program, we’ll get an output like this.
$ python3 test.py
Long duration task
^CTraceback (most recent call last):
File "test.py", line 5, in
time.sleep(1)
KeyboardInterrupt
The program terminated abruptly. You might think that it’s not a big deal as the program is going to die anyway. But abrupt termination of a sufficiently complex program can leave a system inconsistent. Like the program might be doing some database transaction in time of termination. Terminating that program can team a database in a bad state. We might also want to log status in time of exiting. In other words, we need exit control to do some final tasks before exiting.
We can improve the program by capturing the KeyboardInterrupt exception.
import time, sys
print ("Long duration task")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print ("Exiting")
sys.exit()
If we press Ctr+C or send the SIGINT signal via “kill -s INT <pid>” command, the control will come to the exception handler. We can do our final stuff there before exit.
$ python3 test.py
Long duration task
^CExiting
This solution looks good for a simple program like this. But if we have multiple infinite loops in multiple threads, then the handling with KeyboardInterrupt will be difficult. We might need to put the exception handler in multiple places in a complex manner.
The better way to tackle this situation is to register a SIGINT signal handler only once in the whole program.
Here is the example.
import signal, time, sys
def signal_handler(sig, frame):
print('Exiting…')
sys.exit()
signal.signal(signal.SIGINT, signal_handler)
print ("Long duration task")
while True:
time.sleep(1)
If Ctr+C is pressed or SIGINT is sent to the process, the control will come the signal handler, signal_handler(), even if we have multiple infinite loops in multiple threads.