Skip to content

Instantly share code, notes, and snippets.

Last active April 23, 2018 21:36
Show Gist options
  • Save growse/f37ca9da772d9b43b97d819e2d08eac1 to your computer and use it in GitHub Desktop.
Save growse/f37ca9da772d9b43b97d819e2d08eac1 to your computer and use it in GitHub Desktop.
Coordinating multiple python2.7 processes, IPC and signals
#!/usr/bin/env python
import logging
import os
import signal
import sys
import multiprocessing
import threading
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
magic_queue = multiprocessing.Queue()
# Quit on these things
signals = (signal.SIGINT, signal.SIGTERM, signal.SIGCHLD, signal.SIGQUIT)
# This subprocess methods doesn't need to signal to quit, it just needs to see if anyone has set my_event
def do_thing():
ppid = os.getppid() # Write down what our parent pid is
my_event = threading.Event()
def my_signal_handler(signal, _):"Do Thing Signal handler")
# Signal handlers are inherited. So we need to ignore them when sent to the child.
for signame in signals:
signal.signal(signame, my_signal_handler)
while not my_event.is_set():"Doing a thing")
if ppid != os.getppid(): # If this has happened, parent is dead and we're a zombie. Probably best to stop.
# This subprocess may need to tell everyone to quit, so needs the queue that the master is blocking on. But it also
# needs the event, to listen to other things that might want to tell it to stop.
def do_another_thing(my_queue):
ppid = os.getppid()
my_event = threading.Event()
def my_signal_handler(signal, _):"Do Another Thing Signal handler")
for signame in signals:
signal.signal(signame, my_signal_handler)
while not my_event.is_set():
if os.path.isfile("quitnow"):"Found quit file, quitting")
if ppid != os.getppid():
my_queue.put("PLS STOP")
def signal_handler(received_signal, _):"Received signal {}".format(received_signal))
if received_signal == signal.SIGCHLD:
pid, status = os.waitpid(-1, os.WNOHANG | os.WUNTRACED | os.WCONTINUED)
if os.WIFCONTINUED(status) or os.WIFSTOPPED(status):
if os.WIFSIGNALED(status) or os.WIFEXITED(status):"SIGCHLD. Something happened. Pid: {}, Status: {}".format(pid, status))"Something signalled or exited. Term sig is {}".format(os.WTERMSIG(status)))
# Let's assume any signal means "quit""Signal handler queuing a message")
for signame in signals:
signal.signal(signame, signal_handler)
joinables = []
subprocess1 = multiprocessing.Process(target=do_thing)
subprocess2 = multiprocessing.Process(target=do_another_thing, args=(magic_queue,))
subprocess2.start()"Waiting on... something?")
# At the first whiff of trouble, something sends a message to this queue and we bring the whole thing down.
# We use a queue here because inexplicably, Event.wait() deadlocks with the signal_handler.
result = magic_queue.get()"Got the queue message: {}".format(result))
signal.signal(signal.SIGCHLD, signal.SIG_DFL) # We don't care for SIGCHLDs any more."Killing children")
for joinable in joinables:
try:"Trying to terminate {} pid {}".format(joinable,
except OSError:
# Turns out the subprocess might not exist any more, even if the master thinks it does
# Wait for stuff to stop
for joinable in joinables:"joining on {}".format(joinable))
# Hurray!"exit")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment