test_gil_scoped.py 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. # -*- coding: utf-8 -*-
  2. import multiprocessing
  3. import threading
  4. from pybind11_tests import gil_scoped as m
  5. def _run_in_process(target, *args, **kwargs):
  6. """Runs target in process and returns its exitcode after 10s (None if still alive)."""
  7. process = multiprocessing.Process(target=target, args=args, kwargs=kwargs)
  8. process.daemon = True
  9. try:
  10. process.start()
  11. # Do not need to wait much, 10s should be more than enough.
  12. process.join(timeout=10)
  13. return process.exitcode
  14. finally:
  15. if process.is_alive():
  16. process.terminate()
  17. def _python_to_cpp_to_python():
  18. """Calls different C++ functions that come back to Python."""
  19. class ExtendedVirtClass(m.VirtClass):
  20. def virtual_func(self):
  21. pass
  22. def pure_virtual_func(self):
  23. pass
  24. extended = ExtendedVirtClass()
  25. m.test_callback_py_obj(lambda: None)
  26. m.test_callback_std_func(lambda: None)
  27. m.test_callback_virtual_func(extended)
  28. m.test_callback_pure_virtual_func(extended)
  29. def _python_to_cpp_to_python_from_threads(num_threads, parallel=False):
  30. """Calls different C++ functions that come back to Python, from Python threads."""
  31. threads = []
  32. for _ in range(num_threads):
  33. thread = threading.Thread(target=_python_to_cpp_to_python)
  34. thread.daemon = True
  35. thread.start()
  36. if parallel:
  37. threads.append(thread)
  38. else:
  39. thread.join()
  40. for thread in threads:
  41. thread.join()
  42. # TODO: FIXME, sometimes returns -11 (segfault) instead of 0 on macOS Python 3.9
  43. def test_python_to_cpp_to_python_from_thread():
  44. """Makes sure there is no GIL deadlock when running in a thread.
  45. It runs in a separate process to be able to stop and assert if it deadlocks.
  46. """
  47. assert _run_in_process(_python_to_cpp_to_python_from_threads, 1) == 0
  48. # TODO: FIXME on macOS Python 3.9
  49. def test_python_to_cpp_to_python_from_thread_multiple_parallel():
  50. """Makes sure there is no GIL deadlock when running in a thread multiple times in parallel.
  51. It runs in a separate process to be able to stop and assert if it deadlocks.
  52. """
  53. assert _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=True) == 0
  54. # TODO: FIXME on macOS Python 3.9
  55. def test_python_to_cpp_to_python_from_thread_multiple_sequential():
  56. """Makes sure there is no GIL deadlock when running in a thread multiple times sequentially.
  57. It runs in a separate process to be able to stop and assert if it deadlocks.
  58. """
  59. assert (
  60. _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=False) == 0
  61. )
  62. # TODO: FIXME on macOS Python 3.9
  63. def test_python_to_cpp_to_python_from_process():
  64. """Makes sure there is no GIL deadlock when using processes.
  65. This test is for completion, but it was never an issue.
  66. """
  67. assert _run_in_process(_python_to_cpp_to_python) == 0
  68. def test_cross_module_gil():
  69. """Makes sure that the GIL can be acquired by another module from a GIL-released state."""
  70. m.test_cross_module_gil() # Should not raise a SIGSEGV