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