MCPcopy Index your code
hub / github.com/python/cpython / test_post_fork_child_no_deadlock

Method test_post_fork_child_no_deadlock

Lib/test/test_logging.py:753–815  ·  view source on GitHub ↗

Ensure child logging locks are not held; bpo-6721 & bpo-36533.

(self)

Source from the content-addressed store, hash-verified

751 @skip_if_asan_fork
752 @skip_if_tsan_fork
753 def test_post_fork_child_no_deadlock(self):
754 """Ensure child logging locks are not held; bpo-6721 & bpo-36533."""
755 class _OurHandler(logging.Handler):
756 def __init__(self):
757 super().__init__()
758 self.sub_handler = logging.StreamHandler(
759 stream=open('/dev/null', 'wt', encoding='utf-8'))
760
761 def emit(self, record):
762 with self.sub_handler.lock:
763 self.sub_handler.emit(record)
764
765 self.assertEqual(len(logging._handlers), 0)
766 refed_h = _OurHandler()
767 self.addCleanup(refed_h.sub_handler.stream.close)
768 refed_h.name = 'because we need at least one for this test'
769 self.assertGreater(len(logging._handlers), 0)
770 self.assertGreater(len(logging._at_fork_reinit_lock_weakset), 1)
771 test_logger = logging.getLogger('test_post_fork_child_no_deadlock')
772 test_logger.addHandler(refed_h)
773 test_logger.setLevel(logging.DEBUG)
774
775 locks_held__ready_to_fork = threading.Event()
776 fork_happened__release_locks_and_end_thread = threading.Event()
777
778 def lock_holder_thread_fn():
779 with logging._lock, refed_h.lock:
780 # Tell the main thread to do the fork.
781 locks_held__ready_to_fork.set()
782
783 # If the deadlock bug exists, the fork will happen
784 # without dealing with the locks we hold, deadlocking
785 # the child.
786
787 # Wait for a successful fork or an unreasonable amount of
788 # time before releasing our locks. To avoid a timing based
789 # test we'd need communication from os.fork() as to when it
790 # has actually happened. Given this is a regression test
791 # for a fixed issue, potentially less reliably detecting
792 # regression via timing is acceptable for simplicity.
793 # The test will always take at least this long. :(
794 fork_happened__release_locks_and_end_thread.wait(0.5)
795
796 lock_holder_thread = threading.Thread(
797 target=lock_holder_thread_fn,
798 name='test_post_fork_child_no_deadlock lock holder')
799 lock_holder_thread.start()
800
801 locks_held__ready_to_fork.wait()
802 pid = os.fork()
803 if pid == 0:
804 # Child process
805 try:
806 test_logger.info(r'Child process did not deadlock. \o/')
807 finally:
808 os._exit(0)
809 else:
810 # Parent process

Callers

nothing calls this directly

Calls 14

startMethod · 0.95
waitMethod · 0.95
setMethod · 0.95
joinMethod · 0.95
_OurHandlerClass · 0.85
addCleanupMethod · 0.80
assertGreaterMethod · 0.80
getLoggerMethod · 0.80
addHandlerMethod · 0.80
EventMethod · 0.80
assertEqualMethod · 0.45
setLevelMethod · 0.45

Tested by

no test coverage detected