114299Sbbruce@ucdavis.eduimport multiprocessing
214299Sbbruce@ucdavis.eduimport threading
314299Sbbruce@ucdavis.edufrom pybind11_tests import gil_scoped as m
414299Sbbruce@ucdavis.edu
514299Sbbruce@ucdavis.edu
614299Sbbruce@ucdavis.edudef _run_in_process(target, *args, **kwargs):
714299Sbbruce@ucdavis.edu    """Runs target in process and returns its exitcode after 10s (None if still alive)."""
814299Sbbruce@ucdavis.edu    process = multiprocessing.Process(target=target, args=args, kwargs=kwargs)
914299Sbbruce@ucdavis.edu    process.daemon = True
1014299Sbbruce@ucdavis.edu    try:
1114299Sbbruce@ucdavis.edu        process.start()
1214299Sbbruce@ucdavis.edu        # Do not need to wait much, 10s should be more than enough.
1314299Sbbruce@ucdavis.edu        process.join(timeout=10)
1414299Sbbruce@ucdavis.edu        return process.exitcode
1514299Sbbruce@ucdavis.edu    finally:
1614299Sbbruce@ucdavis.edu        if process.is_alive():
1714299Sbbruce@ucdavis.edu            process.terminate()
1814299Sbbruce@ucdavis.edu
1914299Sbbruce@ucdavis.edu
2014299Sbbruce@ucdavis.edudef _python_to_cpp_to_python():
2114299Sbbruce@ucdavis.edu    """Calls different C++ functions that come back to Python."""
2214299Sbbruce@ucdavis.edu    class ExtendedVirtClass(m.VirtClass):
2314299Sbbruce@ucdavis.edu        def virtual_func(self):
2414299Sbbruce@ucdavis.edu            pass
2514299Sbbruce@ucdavis.edu
2614299Sbbruce@ucdavis.edu        def pure_virtual_func(self):
2714299Sbbruce@ucdavis.edu            pass
2814299Sbbruce@ucdavis.edu
2914299Sbbruce@ucdavis.edu    extended = ExtendedVirtClass()
3014299Sbbruce@ucdavis.edu    m.test_callback_py_obj(lambda: None)
3114299Sbbruce@ucdavis.edu    m.test_callback_std_func(lambda: None)
3214299Sbbruce@ucdavis.edu    m.test_callback_virtual_func(extended)
3314299Sbbruce@ucdavis.edu    m.test_callback_pure_virtual_func(extended)
3414299Sbbruce@ucdavis.edu
3514299Sbbruce@ucdavis.edu
3614299Sbbruce@ucdavis.edudef _python_to_cpp_to_python_from_threads(num_threads, parallel=False):
3714299Sbbruce@ucdavis.edu    """Calls different C++ functions that come back to Python, from Python threads."""
3814299Sbbruce@ucdavis.edu    threads = []
3914299Sbbruce@ucdavis.edu    for _ in range(num_threads):
4014299Sbbruce@ucdavis.edu        thread = threading.Thread(target=_python_to_cpp_to_python)
4114299Sbbruce@ucdavis.edu        thread.daemon = True
4214299Sbbruce@ucdavis.edu        thread.start()
4314299Sbbruce@ucdavis.edu        if parallel:
4414299Sbbruce@ucdavis.edu            threads.append(thread)
4514299Sbbruce@ucdavis.edu        else:
4614299Sbbruce@ucdavis.edu            thread.join()
4714299Sbbruce@ucdavis.edu    for thread in threads:
4814299Sbbruce@ucdavis.edu        thread.join()
4914299Sbbruce@ucdavis.edu
5014299Sbbruce@ucdavis.edu
5114299Sbbruce@ucdavis.edudef test_python_to_cpp_to_python_from_thread():
5214299Sbbruce@ucdavis.edu    """Makes sure there is no GIL deadlock when running in a thread.
5314299Sbbruce@ucdavis.edu
5414299Sbbruce@ucdavis.edu    It runs in a separate process to be able to stop and assert if it deadlocks.
5514299Sbbruce@ucdavis.edu    """
5614299Sbbruce@ucdavis.edu    assert _run_in_process(_python_to_cpp_to_python_from_threads, 1) == 0
5714299Sbbruce@ucdavis.edu
5814299Sbbruce@ucdavis.edu
5914299Sbbruce@ucdavis.edudef test_python_to_cpp_to_python_from_thread_multiple_parallel():
6014299Sbbruce@ucdavis.edu    """Makes sure there is no GIL deadlock when running in a thread multiple times in parallel.
6114299Sbbruce@ucdavis.edu
6214299Sbbruce@ucdavis.edu    It runs in a separate process to be able to stop and assert if it deadlocks.
6314299Sbbruce@ucdavis.edu    """
6414299Sbbruce@ucdavis.edu    assert _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=True) == 0
6514299Sbbruce@ucdavis.edu
6614299Sbbruce@ucdavis.edu
6714299Sbbruce@ucdavis.edudef test_python_to_cpp_to_python_from_thread_multiple_sequential():
6814299Sbbruce@ucdavis.edu    """Makes sure there is no GIL deadlock when running in a thread multiple times sequentially.
6914299Sbbruce@ucdavis.edu
7014299Sbbruce@ucdavis.edu    It runs in a separate process to be able to stop and assert if it deadlocks.
7114299Sbbruce@ucdavis.edu    """
7214299Sbbruce@ucdavis.edu    assert _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=False) == 0
7314299Sbbruce@ucdavis.edu
7414299Sbbruce@ucdavis.edu
7514299Sbbruce@ucdavis.edudef test_python_to_cpp_to_python_from_process():
7614299Sbbruce@ucdavis.edu    """Makes sure there is no GIL deadlock when using processes.
7714299Sbbruce@ucdavis.edu
7814299Sbbruce@ucdavis.edu    This test is for completion, but it was never an issue.
7914299Sbbruce@ucdavis.edu    """
8014299Sbbruce@ucdavis.edu    assert _run_in_process(_python_to_cpp_to_python) == 0
8114299Sbbruce@ucdavis.edu
8214299Sbbruce@ucdavis.edu
8314299Sbbruce@ucdavis.edudef test_cross_module_gil():
8414299Sbbruce@ucdavis.edu    """Makes sure that the GIL can be acquired by another module from a GIL-released state."""
8514299Sbbruce@ucdavis.edu    m.test_cross_module_gil()  # Should not raise a SIGSEGV
86