You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
90 lines
2.9 KiB
Python
90 lines
2.9 KiB
Python
import errno
|
|
import os
|
|
import sys
|
|
|
|
import gevent
|
|
import gevent.monkey
|
|
gevent.monkey.patch_all()
|
|
|
|
pid = None
|
|
awaiting_child = []
|
|
|
|
|
|
def handle_sigchld(*_args):
|
|
# Make sure we can do a blocking operation
|
|
gevent.sleep()
|
|
# Signal completion
|
|
awaiting_child.pop()
|
|
# Raise an ignored error
|
|
raise TypeError("This should be ignored but printed")
|
|
|
|
# Try to produce output compatible with unittest output so
|
|
# our status parsing functions work.
|
|
|
|
import signal
|
|
if hasattr(signal, 'SIGCHLD'):
|
|
# In Python 3.8.0 final, on both Travis CI/Linux and locally
|
|
# on macOS, the *child* process started crashing on exit with a memory
|
|
# error:
|
|
#
|
|
# Debug memory block at address p=0x7fcf5d6b5000: API ''
|
|
# 6508921152173528397 bytes originally requested
|
|
# The 7 pad bytes at p-7 are not all FORBIDDENBYTE (0xfd):
|
|
#
|
|
# When PYTHONDEVMODE is set. This happens even if we just simply fork
|
|
# the child process and don't have gevent even /imported/ in the most
|
|
# minimal test case. It's not clear what caused that.
|
|
if sys.version_info[:2] >= (3, 8) and os.environ.get("PYTHONDEVMODE"):
|
|
print("Ran 1 tests in 0.0s (skipped=1)")
|
|
sys.exit(0)
|
|
|
|
|
|
assert signal.getsignal(signal.SIGCHLD) == signal.SIG_DFL
|
|
signal.signal(signal.SIGCHLD, handle_sigchld)
|
|
handler = signal.getsignal(signal.SIGCHLD)
|
|
assert signal.getsignal(signal.SIGCHLD) is handle_sigchld, handler
|
|
|
|
if hasattr(os, 'forkpty'):
|
|
def forkpty():
|
|
# For printing in errors
|
|
return os.forkpty()[0]
|
|
funcs = (os.fork, forkpty)
|
|
else:
|
|
funcs = (os.fork,)
|
|
|
|
for func in funcs:
|
|
awaiting_child = [True]
|
|
pid = func()
|
|
if not pid:
|
|
# child
|
|
gevent.sleep(0.3)
|
|
sys.exit(0)
|
|
else:
|
|
timeout = gevent.Timeout(1)
|
|
try:
|
|
while awaiting_child:
|
|
gevent.sleep(0.01)
|
|
# We should now be able to waitpid() for an arbitrary child
|
|
wpid, status = os.waitpid(-1, os.WNOHANG)
|
|
if wpid != pid:
|
|
raise AssertionError("Failed to wait on a child pid forked with a function",
|
|
wpid, pid, func)
|
|
|
|
# And a second call should raise ECHILD
|
|
try:
|
|
wpid, status = os.waitpid(-1, os.WNOHANG)
|
|
raise AssertionError("Should not be able to wait again")
|
|
except OSError as e:
|
|
assert e.errno == errno.ECHILD
|
|
except gevent.Timeout as t:
|
|
if timeout is not t:
|
|
raise
|
|
raise AssertionError("Failed to wait using", func)
|
|
finally:
|
|
timeout.close()
|
|
print("Ran 1 tests in 0.0s")
|
|
sys.exit(0)
|
|
else:
|
|
print("No SIGCHLD, not testing")
|
|
print("Ran 1 tests in 0.0s (skipped=1)")
|