How to process SIGTERM signal gracefully?
A class based clean to use solution:
import signal
import time
class GracefulKiller:
kill_now = False
def __init__(self):
signal.signal(signal.SIGINT, self.exit_gracefully)
signal.signal(signal.SIGTERM, self.exit_gracefully)
def exit_gracefully(self, *args):
self.kill_now = True
if __name__ == '__main__':
killer = GracefulKiller()
while not killer.kill_now:
time.sleep(1)
print("doing something in a loop ...")
print("End of the program. I was killed gracefully :)")
How to process SIGTERM signal gracefully in Java?
I rewritten the registerShutdownHook()
method and now it works as I wanted.
private static void registerShutdownHook() {
final Thread mainThread = Thread.currentThread();
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
System.out.println("Tralala");
Hellow.setShutdownProcess();
mainThread.join();
} catch (InterruptedException ex) {
System.out.println(ex);
}
}
});
}
How to process SIGTERM and still have a working process.terminate()
The solution very much depends on what platform you are running on as is often the case for Python questions tagged with [multiprocessing]
and it is for that reason one is supposed also tag such questions with the specific platform, such as [linux]
, too. I am inferring that your platform is not Windows since signal.SIGQUIT
is not defined for that platform. So I will go with Linux.
- For Linux you do not want your subprocesses to handle the signals at all (and it's sort of nonsensical for them to be calling function
term
on an Ctrl-C interrupt, for example). For Windows, however, you want your subprocesses to ignore these interrupts. That means you want your main process to callsignal
only after it has created the subprocesses. - Instead of using
FLAG
to indicate that the main process should terminate and have to have the main process loop testing this value periodically, it is simpler, cleaner and more efficient to have the main process just wait on athreading.Event
instance,done_event
. Although. for some reason, this does not seem to work on Windows; the main processwait
call does not get satisfied immediately. - You would like some provision to terminate gracefully if and when your processes complete normally and there has been so signal triggered. The easiest way to accomplish all your goals including this is to make your subprocesses daemon processes that will terminate when the main process terminates. Then create a daemon thread that simply waits for the subprocesses to normally terminate and sets
done_event
when that occurs. So the main process will fall through on the call todone_event.wait()
on either an interrupt of some sort or normal completion. All it has to do now is just end normally; there is no need to callterminate
against the subprocesses since they will end when the main process ends.
import multiprocessing as mp
from threading import Thread, Event
import signal
import time
import sys
IS_WINDOWS = sys.platform == 'win32'
def f(x):
if IS_WINDOWS:
signal.signal(signal.SIGTERM, signal.SIG_IGN)
signal.signal(signal.SIGINT, signal.SIG_IGN)
signal.signal(signal.SIGABRT, signal.SIG_IGN)
time.sleep(5)
print(x)
return x * x
def term(signum, frame):
print(f'Received Signal {signum}')
if IS_WINDOWS:
globals()['FLAG'] = True
else:
done_event.set()
def process_wait_thread():
"""
wait for processes to finish normally and set done_event
"""
for process in workers:
process.join()
if IS_WINDOWS:
globals()['FLAG'] = True
else:
done_event.set()
if __name__ == '__main__':
if IS_WINDOWS:
globals()['FLAG'] = False
else:
done_event = Event()
workers = [
mp.Process(
target=f,
args=(i,),
daemon=True
) for i in range(4)
]
for process in workers:
process.start()
# We don't want subprocesses to inherit these so
# call signal after we start the processes:
signal.signal(signal.SIGTERM, term)
signal.signal(signal.SIGINT, term)
if not IS_WINDOWS:
signal.signal(signal.SIGQUIT, term) # Not supported by Windows at all
signal.signal(signal.SIGABRT, term)
Thread(target=process_wait_thread, daemon=True).start()
if IS_WINDOWS:
while not globals()['FLAG']:
time.sleep(0.1)
else:
done_event.wait()
print('Done')
In what order should I send signals to gracefully shutdown processes?
SIGTERM tells an application to terminate. The other signals tell the application other things which are unrelated to shutdown but may sometimes have the same result. Don't use those. If you want an application to shut down, tell it to. Don't give it misleading signals.
Some people believe the smart standard way of terminating a process is by sending it a slew of signals, such as HUP, INT, TERM and finally KILL. This is ridiculous. The right signal for termination is SIGTERM and if SIGTERM doesn't terminate the process instantly, as you might prefer, it's because the application has chosen to handle the signal. Which means it has a very good reason to not terminate immediately: It's got cleanup work to do. If you interrupt that cleanup work with other signals, there's no telling what data from memory it hasn't yet saved to disk, what client applications are left hanging or whether you're interrupting it "mid-sentence" which is effectively data corruption.
For more information on what the real meaning of the signals is, see sigaction(2). Don't confuse "Default Action" with "Description", they are not the same thing.
SIGINT
is used to signal an interactive "keyboard interrupt" of the process. Some programs may handle the situation in a special way for the purpose of terminal users.
SIGHUP
is used to signal that the terminal has disappeared and is no longer looking at the process. That is all. Some processes choose to shut down in response, generally because their operation makes no sense without a terminal, some choose to do other things such as recheck configuration files.
SIGKILL
is used to forcefully remove the process from the kernel. It is special in the sense that it's not actually a signal to the process but rather gets interpreted by the kernel directly.
Don't send SIGKILL. - SIGKILL
should certainly never be sent by scripts. If the application handles the SIGTERM
, it can take it a second to cleanup, it can take a minute, it can take an hour. Depending on what the application has to get done before it's ready to end. Any logic that "assumes" an application's cleanup sequence has taken long enough and needs to be shortcut or SIGKILLed after X seconds is just plain wrong.
The only reason why an application would need a SIGKILL
to terminate, is if something bugged out during its cleanup sequence. In which case you can open a terminal and SIGKILL
it manually. Aside from that, the only one other reason why you'd SIGKILL
something is because you WANT to prevent it from cleaning itself up.
Even though half the world blindly sends SIGKILL
after 5 seconds it's still horribly wrong thing to do.
How does a python process exit gracefully after receiving SIGTERM while waiting on a semaphore?
You can install a signal handler which throws an exception which is then caught in the subprocess to handle exits gracefully.
Here is an example of a script which waits in a semaphore in a subprocess and terminates gracefully when sent a SIGTERM
.
#!/usr/bin/env python
import signal
import time
import multiprocessing
class GracefulExit(Exception):
pass
def signal_handler(signum, frame):
raise GracefulExit()
def subprocess_function():
try:
sem = multiprocessing.Semaphore()
print "Acquiring semaphore"
sem.acquire()
print "Semaphore acquired"
print "Blocking on semaphore - waiting for SIGTERM"
sem.acquire()
except GracefulExit:
print "Subprocess exiting gracefully"
if __name__ == "__main__":
# Use signal handler to throw exception which can be caught to allow
# graceful exit.
signal.signal(signal.SIGTERM, signal_handler)
# Start a subprocess and wait for it to terminate.
p = multiprocessing.Process(target=subprocess_function)
p.start()
print "Subprocess pid: %d" % p.pid
p.join()
An example run of this script is as follows:
$ ./test.py
Subprocess pid: 7546
Acquiring semaphore
Semaphore acquired
Blocking on semaphore - waiting for SIGTERM
----> Use another shell to kill -TERM 7546
Subprocess exiting gracefully
There is no traceback from the subprocess and the flow shows that the subprocess exits in a graceful manner. This is because the SIGTERM is caught by the subprocess signal handler which throws a normal Python exception which can be handled inside the process.
Allow process to finish rather than be interrupted when SIGTERM is used in Python 3
The answer is in the original question. I am just leaving this here for future Google searchers.
I never had an issue in the first place, my terminal was just having a problem printing 'ending' following the kill
command.
Related Topics
What Are the Differences Between Numpy Arrays and Matrices? Which One Should I Use
What's the Best Way to Parse a JSON Response from the Requests Library
Create Pandas Dataframe from a String
Why Does This Code for Initializing a List of Lists Apparently Link the Lists Together
Check If a Word Is in a String in Python
Permutations Between Two Lists of Unequal Length
How to Keep a Python Script Output Window Open
Find the Column Name Which Has the Maximum Value for Each Row
Class Method Differences in Python: Bound, Unbound and Static
How to Make a Timezone Aware Datetime Object
Matplotlib Different Size Subplots
Typeerror: Unsupported Operand Type(S) for -: 'Str' and 'Int'
Why Does Adding a Trailing Comma After a Variable Name Make It a Tuple
Running a Bash Script from Python
How to Ignore the First Line of Data When Processing CSV Data