44
55import pytest
66
7- import env
87from pybind11_tests import gil_scoped as m
98
109
@@ -139,36 +138,50 @@ def test_all_basic_tests_completeness():
139138 assert len (ALL_BASIC_TESTS ) == num_found
140139
141140
142- # Issue #2754:
143- ThreadSanitizer_exitcode_66_message = (
144- "ThreadSanitizer: starting new threads after multi-threaded fork is not supported."
145- )
141+ ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK = ALL_BASIC_TESTS + (m .intentional_deadlock ,)
146142
147143
148144def _run_in_process (target , * args , ** kwargs ):
149- """Runs target in process and returns its exitcode after 10s (None if still alive)."""
145+ if len (args ) == 0 :
146+ test_fn = target
147+ else :
148+ test_fn = args [0 ]
149+ # Do not need to wait much, 10s should be more than enough.
150+ timeout = 0.1 if test_fn is m .intentional_deadlock else 10
150151 process = multiprocessing .Process (target = target , args = args , kwargs = kwargs )
151152 process .daemon = True
152153 try :
153154 t_start = time .time ()
154155 process .start ()
155- # Do not need to wait much, 10s should be more than enough.
156- process .join (timeout = 10 )
156+ if timeout >= 100 : # For debugging.
157+ print ("\n process.pid STARTED" , process .pid , flush = True )
158+ process .join (timeout = timeout )
159+ if timeout >= 100 :
160+ print ("\n process.pid JOINED" , process .pid , flush = True )
157161 t_delta = time .time () - t_start
158- if process .exitcode is None :
159- assert t_delta > 9.9
160- if process .exitcode == 66 :
161- pass # NICE-TO-HAVE: Check output for ThreadSanitizer_exitcode_66_message
162+ if process .exitcode == 66 and m .defined_THREAD_SANITIZER : # Issue #2754
163+ # WOULD-BE-NICE-TO-HAVE: Check that the message below is actually in the output.
164+ pytest .skip (
165+ "ThreadSanitizer: starting new threads after multi-threaded fork is not supported."
166+ )
167+ elif test_fn is m .intentional_deadlock :
168+ assert process .exitcode is None
169+ return 0
170+ elif process .exitcode is None :
171+ assert t_delta > 0.9 * timeout
172+ raise RuntimeError (
173+ "DEADLOCK, most likely, exactly what this test is meant to detect."
174+ )
162175 return process .exitcode
163176 finally :
164177 if process .is_alive ():
165178 process .terminate ()
166179
167180
168- def _run_in_threads (target , num_threads , parallel ):
181+ def _run_in_threads (test_fn , num_threads , parallel ):
169182 threads = []
170183 for _ in range (num_threads ):
171- thread = threading .Thread (target = target )
184+ thread = threading .Thread (target = test_fn )
172185 thread .daemon = True
173186 thread .start ()
174187 if parallel :
@@ -180,56 +193,40 @@ def _run_in_threads(target, num_threads, parallel):
180193
181194
182195# TODO: FIXME, sometimes returns -11 (segfault) instead of 0 on macOS Python 3.9
183- @pytest .mark .parametrize ("test_fn" , ALL_BASIC_TESTS )
196+ @pytest .mark .parametrize ("test_fn" , ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK )
184197def test_run_in_process_one_thread (test_fn ):
185198 """Makes sure there is no GIL deadlock when running in a thread.
186199
187200 It runs in a separate process to be able to stop and assert if it deadlocks.
188201 """
189- exitcode = _run_in_process (_run_in_threads , test_fn , num_threads = 1 , parallel = False )
190- if exitcode == 66 and m .defined_THREAD_SANITIZER :
191- pytest .skip (ThreadSanitizer_exitcode_66_message )
192- assert exitcode == 0
202+ assert _run_in_process (_run_in_threads , test_fn , num_threads = 1 , parallel = False ) == 0
193203
194204
195205# TODO: FIXME on macOS Python 3.9
196- @pytest .mark .parametrize ("test_fn" , ALL_BASIC_TESTS )
206+ @pytest .mark .parametrize ("test_fn" , ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK )
197207def test_run_in_process_multiple_threads_parallel (test_fn ):
198208 """Makes sure there is no GIL deadlock when running in a thread multiple times in parallel.
199209
200210 It runs in a separate process to be able to stop and assert if it deadlocks.
201211 """
202- exitcode = _run_in_process (_run_in_threads , test_fn , num_threads = 8 , parallel = True )
203- if exitcode is None and env .PYPY and env .WIN : # Seems to be flaky.
204- pytest .skip ("Ignoring unexpected exitcode None (PYPY WIN)" )
205- if exitcode == 66 and m .defined_THREAD_SANITIZER :
206- pytest .skip (ThreadSanitizer_exitcode_66_message )
207- assert exitcode == 0
212+ assert _run_in_process (_run_in_threads , test_fn , num_threads = 8 , parallel = True ) == 0
208213
209214
210215# TODO: FIXME on macOS Python 3.9
211- @pytest .mark .parametrize ("test_fn" , ALL_BASIC_TESTS )
216+ @pytest .mark .parametrize ("test_fn" , ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK )
212217def test_run_in_process_multiple_threads_sequential (test_fn ):
213218 """Makes sure there is no GIL deadlock when running in a thread multiple times sequentially.
214219
215220 It runs in a separate process to be able to stop and assert if it deadlocks.
216221 """
217- exitcode = _run_in_process (_run_in_threads , test_fn , num_threads = 8 , parallel = False )
218- if exitcode == 66 and m .defined_THREAD_SANITIZER :
219- pytest .skip (ThreadSanitizer_exitcode_66_message )
220- assert exitcode == 0
222+ assert _run_in_process (_run_in_threads , test_fn , num_threads = 8 , parallel = False ) == 0
221223
222224
223225# TODO: FIXME on macOS Python 3.9
224- @pytest .mark .parametrize ("test_fn" , ALL_BASIC_TESTS )
226+ @pytest .mark .parametrize ("test_fn" , ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK )
225227def test_run_in_process_direct (test_fn ):
226228 """Makes sure there is no GIL deadlock when using processes.
227229
228230 This test is for completion, but it was never an issue.
229231 """
230- exitcode = _run_in_process (test_fn )
231- if exitcode is None and env .PYPY and env .WIN : # Seems to be flaky.
232- pytest .skip ("Ignoring unexpected exitcode None (PYPY WIN)" )
233- if exitcode == 66 and m .defined_THREAD_SANITIZER :
234- pytest .skip (ThreadSanitizer_exitcode_66_message )
235- assert exitcode == 0
232+ assert _run_in_process (test_fn ) == 0
0 commit comments