test_gil_scoped.py 3.03 KB
Newer Older
1
2
import multiprocessing
import threading
3

4
5
6
7
from pybind11_tests import gil_scoped as m


def _run_in_process(target, *args, **kwargs):
8
    """Runs target in process and returns its exitcode after 10s (None if still alive)."""
9
10
11
12
    process = multiprocessing.Process(target=target, args=args, kwargs=kwargs)
    process.daemon = True
    try:
        process.start()
13
14
        # Do not need to wait much, 10s should be more than enough.
        process.join(timeout=10)
15
16
17
18
19
20
21
22
        return process.exitcode
    finally:
        if process.is_alive():
            process.terminate()


def _python_to_cpp_to_python():
    """Calls different C++ functions that come back to Python."""
23

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    class ExtendedVirtClass(m.VirtClass):
        def virtual_func(self):
            pass

        def pure_virtual_func(self):
            pass

    extended = ExtendedVirtClass()
    m.test_callback_py_obj(lambda: None)
    m.test_callback_std_func(lambda: None)
    m.test_callback_virtual_func(extended)
    m.test_callback_pure_virtual_func(extended)


def _python_to_cpp_to_python_from_threads(num_threads, parallel=False):
    """Calls different C++ functions that come back to Python, from Python threads."""
    threads = []
    for _ in range(num_threads):
        thread = threading.Thread(target=_python_to_cpp_to_python)
        thread.daemon = True
        thread.start()
        if parallel:
            threads.append(thread)
        else:
            thread.join()
    for thread in threads:
        thread.join()


Henry Schreiner's avatar
Henry Schreiner committed
53
# TODO: FIXME, sometimes returns -11 (segfault) instead of 0 on macOS Python 3.9
54
55
56
57
58
59
60
61
def test_python_to_cpp_to_python_from_thread():
    """Makes sure there is no GIL deadlock when running in a thread.

    It runs in a separate process to be able to stop and assert if it deadlocks.
    """
    assert _run_in_process(_python_to_cpp_to_python_from_threads, 1) == 0


Henry Schreiner's avatar
Henry Schreiner committed
62
# TODO: FIXME on macOS Python 3.9
63
64
65
66
67
68
69
70
def test_python_to_cpp_to_python_from_thread_multiple_parallel():
    """Makes sure there is no GIL deadlock when running in a thread multiple times in parallel.

    It runs in a separate process to be able to stop and assert if it deadlocks.
    """
    assert _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=True) == 0


Henry Schreiner's avatar
Henry Schreiner committed
71
# TODO: FIXME on macOS Python 3.9
72
73
74
75
76
def test_python_to_cpp_to_python_from_thread_multiple_sequential():
    """Makes sure there is no GIL deadlock when running in a thread multiple times sequentially.

    It runs in a separate process to be able to stop and assert if it deadlocks.
    """
77
78
79
    assert (
        _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=False) == 0
    )
80
81


Henry Schreiner's avatar
Henry Schreiner committed
82
# TODO: FIXME on macOS Python 3.9
83
84
85
86
87
88
def test_python_to_cpp_to_python_from_process():
    """Makes sure there is no GIL deadlock when using processes.

    This test is for completion, but it was never an issue.
    """
    assert _run_in_process(_python_to_cpp_to_python) == 0
89
90
91
92
93


def test_cross_module_gil():
    """Makes sure that the GIL can be acquired by another module from a GIL-released state."""
    m.test_cross_module_gil()  # Should not raise a SIGSEGV