Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CTRL_C_EVENT sent to child process kills parent process

I'm writing a script that runs a background process in parallel. When restarting the script I want to be able to kill the background process and exit it cleanly by sending it a CTRL_C_EVENT signal. For some reason though, sending the CTRL_C_EVENT signal to the child process also causes the same signal to be sent to the parent process. I suspect that the KeyboardInterrupt exception isn't being cleaned up after the child process gets it and is then caught by the main process.

I'm using Python version 2.7.1 and running on Windows Server 2012.

import multiprocessing
import time
import signal
import os

def backgroundProcess():
    try:
        while(True):
            time.sleep(10)

    except KeyboardInterrupt:
        #exit cleanly
        return


def script():
    try:
        print "Starting function"

        #Kill all background processes
        for proc in multiprocessing.active_children():
            print "Killing " + str(proc) + " with PID " + str(proc.pid)
            os.kill(proc.pid, signal.CTRL_C_EVENT)

        print "Creating background process"
        newProc = multiprocessing.Process(target=backgroundProcess)
        print "Starting new background process"
        newProc.start()
        print "Process PID is " + str(newProc.pid)

    except KeyboardInterrupt:
        print "Unexpected keyboard interrupt"

def main():
    script()
    time.sleep(5)
    script()

I expect that the script() function should never be receiving a KeyboardInterrupt exception, but it is triggered the second time that the function is called. Why is this happening?

like image 383
werepancake Avatar asked Dec 12 '25 02:12

werepancake


1 Answers

I'm still looking for an explanation as to why the issue occurs, but I'll post my (albeit somewhat hacky) workaround here in case it helps anyone else. Since the Ctrl+C gets propagated to the parent process (still not entirely sure why this happens), I'm going to just catch the exception when it arrives and do nothing.

Eryk suggested using an extra watchdog thread to handle terminating the extra process, but for my application this introduces extra complexity and seems a bit overkill for the rare case that I actually need to kill the background process. Most of the time the background process in my application will close itself cleanly when it's done.

I'm still open to suggestions for a better implementation that doesn't add too much complexity (more processes, threads, etc.).

Modified code here:

import multiprocessing
import time
import signal
import os

def backgroundProcess():
    try:
        while(True):
            time.sleep(10)

    except KeyboardInterrupt:
        #Exit cleanly
        return


def script():
    print "Starting function"

    #Kill all background processes
    for proc in multiprocessing.active_children():
        print "Killing " + str(proc) + " with PID " + str(proc.pid)
        try:
            #Apparently sending a CTRL-C to the child also sends it to the parent??
            os.kill(proc.pid, signal.CTRL_C_EVENT)
            #Sleep until the parent receives the KeyboardInterrupt, then ignore it
            time.sleep(1)
        except KeyboardInterrupt:
            pass

    print "Creating background process"
    newProc = multiprocessing.Process(target=backgroundProcess)
    print "Starting new background process"
    newProc.start()
    print "Process PID is " + str(newProc.pid)

def main():
    script()
    time.sleep(5)
    script()
like image 126
werepancake Avatar answered Dec 13 '25 16:12

werepancake



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!