2

I am developing some code which I need to gracefully shutdown when a sigterm signal is sent from the command line in unix. I found this example https://stackoverflow.com/a/31464349/7019148 which works great, but there's one problem with it.

Code:

import signal
import time


class GracefulKiller:

    def __init__(self):
        signal.signal(signal.SIGTERM, self.exit_gracefully)
        self.kill_now = False

    def exit_gracefully(self, signum, frame):
        self.kill_now = True

    def run_something(self):
        print("starting")
        time.sleep(5)
        print("ending")


if __name__ == '__main__':
    killer = GracefulKiller()
    print(os.getpid())
    while True:
        killer.run_something()
        if killer.kill_now:
            break

    print("End of the program. I was killed gracefully :)")

When you pass the kill command kill -15 <pid>, the run_something method is interrupted and the process killed, gracefully. However, is there a way to do this so that the run_something method can complete before the process is killed? I.e. prevent the interruption?

Desired output:

>>> starting
*kill executed during the middle sleep*
>>> ending
>>> End of the program. I was killed gracefully :)

My use case is that this will be turned into a download script and if I want to terminate the process, I would like the process to finish downloading before terminating...

tda
  • 2,045
  • 1
  • 17
  • 42
  • It seem to be working exactly like you are asking for, i cannot recreate your issue. I run the program in one terminal, call kill 15 PID, the loop ends fine and then exits. Im using Python 3.6.3 – Tobias Apr 12 '18 at 11:33
  • Interesting... so if you kill the pid during the time.sleep command in the run_something() method, it will continue to print 'ending' and then the 'End...' statement? (as per my desired outcome) – tda Apr 12 '18 at 12:09
  • 1
    Yes it does what you describe, after running your script ive tried both via keyboard, ctrl+c, and "kill 2 and 15" sigterm/sigint. Ofc if you send kill -9 (sigkill) you will kill the program since you dont interrupt that signal. – Tobias Apr 12 '18 at 12:25
  • Hi Tobias... I restarted my terminal and am now getting the same as my desired output... some weird terminal error not printing everything?! Anyway - thanks for the confirmation but seems I never had a question in the first place! – tda Apr 12 '18 at 12:45

2 Answers2

0

thread.join() waits till the thread finishes even if an exit signal was caught.

import threading
import Queue
import time

def download_for(seconds=5):
    for i in range(seconds):
        print("downloading...")
        time.sleep(1)

    print("finished download")


download_thread = threading.Thread(target=download_for, args=(3,))

download_thread.start()
# this waits till the thread finishes even if an exit signal was received
download_thread.join()

# this would just stop the download midway
# download_for(seconds=5)
Hadus
  • 1,551
  • 11
  • 22
  • Thanks for the reply but I don't think this will not work... The ctr+c interrupts the while loop and skips to the finally and runs this. It therefore doesn't allow the while loop to finish first. – tda Apr 12 '18 at 11:11
  • I understand. Maybe you should start a thread in the try and wait for it in the finally. I will edit my answer... – Hadus Apr 12 '18 at 11:13
  • Hi Hadus, I still can't get this to work using a terminate signal. The kill command interrupts the download_for() method and runs whatever handler is specified in the signal.signal line. Maybe the way keyboard interrupts are received is different to a sigterm kill?! – tda Apr 12 '18 at 11:48
0

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.

tda
  • 2,045
  • 1
  • 17
  • 42