test_fork1.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. """This test checks for correct fork() behavior.
  2. """
  3. import _imp as imp
  4. import os
  5. import signal
  6. import sys
  7. import threading
  8. import time
  9. import unittest
  10. from test.fork_wait import ForkWait
  11. from test import support
  12. # Skip test if fork does not exist.
  13. if not support.has_fork_support:
  14. raise unittest.SkipTest("test module requires working os.fork")
  15. class ForkTest(ForkWait):
  16. def test_threaded_import_lock_fork(self):
  17. """Check fork() in main thread works while a subthread is doing an import"""
  18. import_started = threading.Event()
  19. fake_module_name = "fake test module"
  20. partial_module = "partial"
  21. complete_module = "complete"
  22. def importer():
  23. imp.acquire_lock()
  24. sys.modules[fake_module_name] = partial_module
  25. import_started.set()
  26. time.sleep(0.01) # Give the other thread time to try and acquire.
  27. sys.modules[fake_module_name] = complete_module
  28. imp.release_lock()
  29. t = threading.Thread(target=importer)
  30. t.start()
  31. import_started.wait()
  32. exitcode = 42
  33. pid = os.fork()
  34. try:
  35. # PyOS_BeforeFork should have waited for the import to complete
  36. # before forking, so the child can recreate the import lock
  37. # correctly, but also won't see a partially initialised module
  38. if not pid:
  39. m = __import__(fake_module_name)
  40. if m == complete_module:
  41. os._exit(exitcode)
  42. else:
  43. if support.verbose > 1:
  44. print("Child encountered partial module")
  45. os._exit(1)
  46. else:
  47. t.join()
  48. # Exitcode 1 means the child got a partial module (bad.) No
  49. # exitcode (but a hang, which manifests as 'got pid 0')
  50. # means the child deadlocked (also bad.)
  51. self.wait_impl(pid, exitcode=exitcode)
  52. finally:
  53. try:
  54. os.kill(pid, signal.SIGKILL)
  55. except OSError:
  56. pass
  57. def test_nested_import_lock_fork(self):
  58. """Check fork() in main thread works while the main thread is doing an import"""
  59. exitcode = 42
  60. # Issue 9573: this used to trigger RuntimeError in the child process
  61. def fork_with_import_lock(level):
  62. release = 0
  63. in_child = False
  64. try:
  65. try:
  66. for i in range(level):
  67. imp.acquire_lock()
  68. release += 1
  69. pid = os.fork()
  70. in_child = not pid
  71. finally:
  72. for i in range(release):
  73. imp.release_lock()
  74. except RuntimeError:
  75. if in_child:
  76. if support.verbose > 1:
  77. print("RuntimeError in child")
  78. os._exit(1)
  79. raise
  80. if in_child:
  81. os._exit(exitcode)
  82. self.wait_impl(pid, exitcode=exitcode)
  83. # Check this works with various levels of nested
  84. # import in the main thread
  85. for level in range(5):
  86. fork_with_import_lock(level)
  87. def tearDownModule():
  88. support.reap_children()
  89. if __name__ == "__main__":
  90. unittest.main()