Skip to content

Instantly share code, notes, and snippets.

@kousu
Created February 12, 2017 15:15
Show Gist options
  • Save kousu/605b54f87b71ee9a44e2537189afb172 to your computer and use it in GitHub Desktop.
Save kousu/605b54f87b71ee9a44e2537189afb172 to your computer and use it in GitHub Desktop.
Unkillable read()s when threads are involved
#!/usr/bin/env python
"""
Unkillable read()s when threads are involved bug:
Set up:
1) run `nc -l 7654 -k` in another terminal
2) run this
3) press control-c
Results:
if thread happens (T.start() is called) then the control-c *does not* interrupt the .recv()
if the thread doessn't run, then the control-c interrupts .recv()
https://docs.python.org/2/library/signal.html does mention
> Some care must be taken if both signals and threads are used in the same program.
But it claims that only
> always perform signal() operations in the main thread of execution.
which we *do* here, so wtf?
further symptons: if you install extra signal handlers, they too will be eaten
but if you don't install them, the default (C-level) handler will kick in. e.g.:
`pkill -HUP python` will print "Hangup" and kill the program, but adding signal(SIGHUP, lambda: print("SIGHUP")) and then `pkill -HUP`ing again *will not run*
ditto for `pkill -TERM python`
this suggests that it's something to do with the python-layer signal() implementation:
something about setting a python function as a signal handler (as the language does for SIGINT by default, or as we can do ourselves)
Q: does this only happen on python2? or python2 on FreeBSD??
A: python2 and python3 show it
A: FreeBSD shows it; Linux does not.
So it's a bug in the FreeBSD implementation of CPython's signal handling when threads are involved.
Q: Does it have to be a socket? What if it's a file? What if it's just sleep() being interrupted?
A: Yes if it's file.read() instead of socket.recv();
A: Yes if it's sleep()
Hypothesis: any system call shows this. Something about the trampoline that handles signals gets gummed up when there's threads involved.
This doesn't make any sense: is the signal being sent to the spinning thread instead of the main thread?
"""
from __future__ import print_function
import sys
import threading
from time import sleep
from signal import *
def spin():
while True:
sleep(5)
T = threading.Thread(target=spin)
T.daemon = True
T.start()
#signal(SIGINT, lambda *args: (print("SIGINT happened"), sys.stdout.flush()))
signal(SIGHUP, lambda *args: (print("SIGHUP happened"), sys.stdout.flush()))
#signal(SIGTERM, lambda *args: (print("SIGTERM happened"), sys.stdout.flush()))
#import socket
#S = socket.socket()
#S.connect(("localhost", 7654))
#print(S.recv(4096))
#print(sys.stdin.read(4096))
sleep(300)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment