4
4
5
5
import pytest
6
6
7
- import env
8
7
from pybind11_tests import gil_scoped as m
9
8
10
9
@@ -139,36 +138,50 @@ def test_all_basic_tests_completeness():
139
138
assert len (ALL_BASIC_TESTS ) == num_found
140
139
141
140
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 ,)
146
142
147
143
148
144
def _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
150
151
process = multiprocessing .Process (target = target , args = args , kwargs = kwargs )
151
152
process .daemon = True
152
153
try :
153
154
t_start = time .time ()
154
155
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 )
157
161
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
+ )
162
175
return process .exitcode
163
176
finally :
164
177
if process .is_alive ():
165
178
process .terminate ()
166
179
167
180
168
- def _run_in_threads (target , num_threads , parallel ):
181
+ def _run_in_threads (test_fn , num_threads , parallel ):
169
182
threads = []
170
183
for _ in range (num_threads ):
171
- thread = threading .Thread (target = target )
184
+ thread = threading .Thread (target = test_fn )
172
185
thread .daemon = True
173
186
thread .start ()
174
187
if parallel :
@@ -180,56 +193,40 @@ def _run_in_threads(target, num_threads, parallel):
180
193
181
194
182
195
# 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 )
184
197
def test_run_in_process_one_thread (test_fn ):
185
198
"""Makes sure there is no GIL deadlock when running in a thread.
186
199
187
200
It runs in a separate process to be able to stop and assert if it deadlocks.
188
201
"""
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
193
203
194
204
195
205
# 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 )
197
207
def test_run_in_process_multiple_threads_parallel (test_fn ):
198
208
"""Makes sure there is no GIL deadlock when running in a thread multiple times in parallel.
199
209
200
210
It runs in a separate process to be able to stop and assert if it deadlocks.
201
211
"""
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
208
213
209
214
210
215
# 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 )
212
217
def test_run_in_process_multiple_threads_sequential (test_fn ):
213
218
"""Makes sure there is no GIL deadlock when running in a thread multiple times sequentially.
214
219
215
220
It runs in a separate process to be able to stop and assert if it deadlocks.
216
221
"""
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
221
223
222
224
223
225
# 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 )
225
227
def test_run_in_process_direct (test_fn ):
226
228
"""Makes sure there is no GIL deadlock when using processes.
227
229
228
230
This test is for completion, but it was never an issue.
229
231
"""
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