github.com/kaydxh/golang@v0.0.131/pkg/gocv/cgo/third_path/pybind11/tests/test_gil_scoped.py (about)

     1  import multiprocessing
     2  import sys
     3  import threading
     4  import time
     5  
     6  import pytest
     7  
     8  import env
     9  from pybind11_tests import gil_scoped as m
    10  
    11  
    12  class ExtendedVirtClass(m.VirtClass):
    13      def virtual_func(self):
    14          pass
    15  
    16      def pure_virtual_func(self):
    17          pass
    18  
    19  
    20  def test_callback_py_obj():
    21      m.test_callback_py_obj(lambda: None)
    22  
    23  
    24  def test_callback_std_func():
    25      m.test_callback_std_func(lambda: None)
    26  
    27  
    28  def test_callback_virtual_func():
    29      extended = ExtendedVirtClass()
    30      m.test_callback_virtual_func(extended)
    31  
    32  
    33  def test_callback_pure_virtual_func():
    34      extended = ExtendedVirtClass()
    35      m.test_callback_pure_virtual_func(extended)
    36  
    37  
    38  def test_cross_module_gil_released():
    39      """Makes sure that the GIL can be acquired by another module from a GIL-released state."""
    40      m.test_cross_module_gil_released()  # Should not raise a SIGSEGV
    41  
    42  
    43  def test_cross_module_gil_acquired():
    44      """Makes sure that the GIL can be acquired by another module from a GIL-acquired state."""
    45      m.test_cross_module_gil_acquired()  # Should not raise a SIGSEGV
    46  
    47  
    48  def test_cross_module_gil_inner_custom_released():
    49      """Makes sure that the GIL can be acquired/released by another module
    50      from a GIL-released state using custom locking logic."""
    51      m.test_cross_module_gil_inner_custom_released()
    52  
    53  
    54  def test_cross_module_gil_inner_custom_acquired():
    55      """Makes sure that the GIL can be acquired/acquired by another module
    56      from a GIL-acquired state using custom locking logic."""
    57      m.test_cross_module_gil_inner_custom_acquired()
    58  
    59  
    60  def test_cross_module_gil_inner_pybind11_released():
    61      """Makes sure that the GIL can be acquired/released by another module
    62      from a GIL-released state using pybind11 locking logic."""
    63      m.test_cross_module_gil_inner_pybind11_released()
    64  
    65  
    66  def test_cross_module_gil_inner_pybind11_acquired():
    67      """Makes sure that the GIL can be acquired/acquired by another module
    68      from a GIL-acquired state using pybind11 locking logic."""
    69      m.test_cross_module_gil_inner_pybind11_acquired()
    70  
    71  
    72  def test_cross_module_gil_nested_custom_released():
    73      """Makes sure that the GIL can be nested acquired/released by another module
    74      from a GIL-released state using custom locking logic."""
    75      m.test_cross_module_gil_nested_custom_released()
    76  
    77  
    78  def test_cross_module_gil_nested_custom_acquired():
    79      """Makes sure that the GIL can be nested acquired/acquired by another module
    80      from a GIL-acquired state using custom locking logic."""
    81      m.test_cross_module_gil_nested_custom_acquired()
    82  
    83  
    84  def test_cross_module_gil_nested_pybind11_released():
    85      """Makes sure that the GIL can be nested acquired/released by another module
    86      from a GIL-released state using pybind11 locking logic."""
    87      m.test_cross_module_gil_nested_pybind11_released()
    88  
    89  
    90  def test_cross_module_gil_nested_pybind11_acquired():
    91      """Makes sure that the GIL can be nested acquired/acquired by another module
    92      from a GIL-acquired state using pybind11 locking logic."""
    93      m.test_cross_module_gil_nested_pybind11_acquired()
    94  
    95  
    96  def test_release_acquire():
    97      assert m.test_release_acquire(0xAB) == "171"
    98  
    99  
   100  def test_nested_acquire():
   101      assert m.test_nested_acquire(0xAB) == "171"
   102  
   103  
   104  def test_multi_acquire_release_cross_module():
   105      for bits in range(16 * 8):
   106          internals_ids = m.test_multi_acquire_release_cross_module(bits)
   107          assert len(internals_ids) == 2 if bits % 8 else 1
   108  
   109  
   110  # Intentionally putting human review in the loop here, to guard against accidents.
   111  VARS_BEFORE_ALL_BASIC_TESTS = dict(vars())  # Make a copy of the dict (critical).
   112  ALL_BASIC_TESTS = (
   113      test_callback_py_obj,
   114      test_callback_std_func,
   115      test_callback_virtual_func,
   116      test_callback_pure_virtual_func,
   117      test_cross_module_gil_released,
   118      test_cross_module_gil_acquired,
   119      test_cross_module_gil_inner_custom_released,
   120      test_cross_module_gil_inner_custom_acquired,
   121      test_cross_module_gil_inner_pybind11_released,
   122      test_cross_module_gil_inner_pybind11_acquired,
   123      test_cross_module_gil_nested_custom_released,
   124      test_cross_module_gil_nested_custom_acquired,
   125      test_cross_module_gil_nested_pybind11_released,
   126      test_cross_module_gil_nested_pybind11_acquired,
   127      test_release_acquire,
   128      test_nested_acquire,
   129      test_multi_acquire_release_cross_module,
   130  )
   131  
   132  
   133  def test_all_basic_tests_completeness():
   134      num_found = 0
   135      for key, value in VARS_BEFORE_ALL_BASIC_TESTS.items():
   136          if not key.startswith("test_"):
   137              continue
   138          assert value in ALL_BASIC_TESTS
   139          num_found += 1
   140      assert len(ALL_BASIC_TESTS) == num_found
   141  
   142  
   143  def _intentional_deadlock():
   144      m.intentional_deadlock()
   145  
   146  
   147  ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK = ALL_BASIC_TESTS + (_intentional_deadlock,)
   148  
   149  
   150  def _run_in_process(target, *args, **kwargs):
   151      if len(args) == 0:
   152          test_fn = target
   153      else:
   154          test_fn = args[0]
   155      # Do not need to wait much, 10s should be more than enough.
   156      timeout = 0.1 if test_fn is _intentional_deadlock else 10
   157      process = multiprocessing.Process(target=target, args=args, kwargs=kwargs)
   158      process.daemon = True
   159      try:
   160          t_start = time.time()
   161          process.start()
   162          if timeout >= 100:  # For debugging.
   163              print(
   164                  "\nprocess.pid STARTED", process.pid, (sys.argv, target, args, kwargs)
   165              )
   166              print(f"COPY-PASTE-THIS: gdb {sys.argv[0]} -p {process.pid}", flush=True)
   167          process.join(timeout=timeout)
   168          if timeout >= 100:
   169              print("\nprocess.pid JOINED", process.pid, flush=True)
   170          t_delta = time.time() - t_start
   171          if process.exitcode == 66 and m.defined_THREAD_SANITIZER:  # Issue #2754
   172              # WOULD-BE-NICE-TO-HAVE: Check that the message below is actually in the output.
   173              # Maybe this could work:
   174              # https://gist.github.com/alexeygrigorev/01ce847f2e721b513b42ea4a6c96905e
   175              pytest.skip(
   176                  "ThreadSanitizer: starting new threads after multi-threaded fork is not supported."
   177              )
   178          elif test_fn is _intentional_deadlock:
   179              assert process.exitcode is None
   180              return 0
   181          elif process.exitcode is None:
   182              assert t_delta > 0.9 * timeout
   183              msg = "DEADLOCK, most likely, exactly what this test is meant to detect."
   184              if env.PYPY and env.WIN:
   185                  pytest.skip(msg)
   186              raise RuntimeError(msg)
   187          return process.exitcode
   188      finally:
   189          if process.is_alive():
   190              process.terminate()
   191  
   192  
   193  def _run_in_threads(test_fn, num_threads, parallel):
   194      threads = []
   195      for _ in range(num_threads):
   196          thread = threading.Thread(target=test_fn)
   197          thread.daemon = True
   198          thread.start()
   199          if parallel:
   200              threads.append(thread)
   201          else:
   202              thread.join()
   203      for thread in threads:
   204          thread.join()
   205  
   206  
   207  # TODO: FIXME, sometimes returns -11 (segfault) instead of 0 on macOS Python 3.9
   208  @pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK)
   209  def test_run_in_process_one_thread(test_fn):
   210      """Makes sure there is no GIL deadlock when running in a thread.
   211  
   212      It runs in a separate process to be able to stop and assert if it deadlocks.
   213      """
   214      assert _run_in_process(_run_in_threads, test_fn, num_threads=1, parallel=False) == 0
   215  
   216  
   217  # TODO: FIXME on macOS Python 3.9
   218  @pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK)
   219  def test_run_in_process_multiple_threads_parallel(test_fn):
   220      """Makes sure there is no GIL deadlock when running in a thread multiple times in parallel.
   221  
   222      It runs in a separate process to be able to stop and assert if it deadlocks.
   223      """
   224      assert _run_in_process(_run_in_threads, test_fn, num_threads=8, parallel=True) == 0
   225  
   226  
   227  # TODO: FIXME on macOS Python 3.9
   228  @pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK)
   229  def test_run_in_process_multiple_threads_sequential(test_fn):
   230      """Makes sure there is no GIL deadlock when running in a thread multiple times sequentially.
   231  
   232      It runs in a separate process to be able to stop and assert if it deadlocks.
   233      """
   234      assert _run_in_process(_run_in_threads, test_fn, num_threads=8, parallel=False) == 0
   235  
   236  
   237  # TODO: FIXME on macOS Python 3.9
   238  @pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK)
   239  def test_run_in_process_direct(test_fn):
   240      """Makes sure there is no GIL deadlock when using processes.
   241  
   242      This test is for completion, but it was never an issue.
   243      """
   244      assert _run_in_process(test_fn) == 0