Skip to content

Commit

Permalink
Removed KI test that is no longer required
Browse files Browse the repository at this point in the history
  • Loading branch information
agronholm committed Jan 22, 2022
1 parent c023ad0 commit d130f12
Showing 1 changed file with 0 additions and 112 deletions.
112 changes: 0 additions & 112 deletions trio/_core/tests/test_ki.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,115 +499,3 @@ async def inner():
_core.run(inner)
finally:
threading._active[thread.ident] = original


# For details on why this test is non-trivial, see:
# https://github.com/python-trio/trio/issues/42
# https://github.com/python-trio/trio/issues/109
@slow
def test_ki_wakes_us_up():
assert is_main_thread()

# This test is flaky due to a race condition on Windows; see:
# https://github.com/python-trio/trio/issues/119
# https://bugs.python.org/issue30038
# I think the only fix is to wait for fixed CPython to be released, so in
# the mean time, on affected versions we send two signals (equivalent to
# hitting control-C twice). This works because the problem is that the C
# level signal handler does
#
# write-to-fd -> set-flags
#
# and we need
#
# set-flags -> write-to-fd
#
# so running the C level signal handler twice does
#
# write-to-fd -> set-flags -> write-to-fd -> set-flags
#
# which contains the desired sequence.
#
# Affected version of CPython include 3.6.1 and earlier.
# It's fixed in 3.6.2 and 3.7+
#
# PyPy was never affected.
#
# The problem technically can occur on Unix as well, if a signal is
# delivered to a non-main thread, though we haven't observed this in
# practice.
#
# There's also this theoretical problem, but hopefully it won't actually
# bite us in practice:
# https://bugs.python.org/issue31119
# https://bitbucket.org/pypy/pypy/issues/2623
import platform

# lock is only needed to avoid an annoying race condition where the
# *second* ki_self() call arrives *after* the first one woke us up and its
# KeyboardInterrupt was caught, and then generates a second
# KeyboardInterrupt that aborts the test run. The kill_soon thread holds
# the lock while doing the calls to ki_self, which means that it holds it
# while the C-level signal handler is running. Then in the main thread,
# when we're woken up we know that ki_self() has been run at least once;
# if we then take the lock it guaranteeds that ki_self() has been run
# twice, so if a second KeyboardInterrupt is going to arrive it should
# arrive by the time we've acquired the lock. This lets us force it to
# happen inside the pytest.raises block.
#
# It will be very nice when the buggy_wakeup_fd bug is fixed.
lock = threading.Lock()

def kill_soon():
# We want the signal to be raised after the main thread has entered
# the IO manager blocking primitive. There really is no way to
# deterministically interlock with that, so we have to use sleep and
# hope it's long enough.
time.sleep(1.1)
with lock:
print("thread doing ki_self()")
ki_self()

async def main():
thread = threading.Thread(target=kill_soon)
print("Starting thread")
thread.start()
try:
with pytest.raises(KeyboardInterrupt):
# To limit the damage on CI if this does get broken (as
# compared to sleep_forever())
print("Going to sleep")
try:
await sleep(20)
print("Woke without raising?!") # pragma: no cover
# The only purpose of this finally: block is to soak up the
# second KeyboardInterrupt that might arrive on
# buggy_wakeup_fd platforms. So it might get aborted at any
# moment randomly on some runs, so pragma: no cover avoids
# coverage flapping:
finally: # pragma: no cover
print("waiting for lock")
with lock:
print("got lock")
# And then we want to force a PyErr_CheckSignals. Which is
# not so easy on Windows. Weird kluge: builtin_repr calls
# PyObject_Repr, which does an unconditional
# PyErr_CheckSignals for some reason.
print(repr(None))
# And finally, it's possible that the signal was delivered
# but at a moment when we had KI protection enabled, so we
# need to execute a checkpoint to ensure it's delivered
# before we exit main().
await _core.checkpoint()
finally:
print("joining thread", sys.exc_info())
thread.join()

start = time.perf_counter()
try:
_core.run(main)
finally:
end = time.perf_counter()
print("duration", end - start)
print("sys.exc_info", sys.exc_info())
assert 1.0 <= (end - start) < 2

0 comments on commit d130f12

Please sign in to comment.