Skip to content

Commit f2bd883

Browse files
Merge pull request #27 from EricCousineau-TRI/feature/merge_20190122
Merge `upstream/master` (085a294) from previous merge-base (6420514)
2 parents 5501496 + e736467 commit f2bd883

File tree

8 files changed

+167
-32
lines changed

8 files changed

+167
-32
lines changed

.travis.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@ matrix:
1010
# - Makes sure that everything still works without optional deps (numpy/scipy/eigen) and
1111
# also tests the automatic discovery functions in CMake (Python version, C++ standard).
1212
- os: linux
13+
dist: xenial # Necessary to run doxygen 1.8.15
1314
env: STYLE DOCS PIP
1415
cache: false
1516
before_install:
1617
- pyenv global $(pyenv whence 2to3) # activate all python versions
1718
- PY_CMD=python3
18-
- $PY_CMD -m pip install --user --upgrade pip wheel
19+
- $PY_CMD -m pip install --user --upgrade pip wheel setuptools
1920
install:
2021
- $PY_CMD -m pip install --user --upgrade sphinx sphinx_rtd_theme breathe flake8 pep8-naming pytest
21-
- curl -fsSL ftp://ftp.stack.nl/pub/users/dimitri/doxygen-1.8.12.linux.bin.tar.gz | tar xz
22-
- export PATH="$PWD/doxygen-1.8.12/bin:$PATH"
22+
- curl -fsSL https://sourceforge.net/projects/doxygen/files/rel-1.8.15/doxygen-1.8.15.linux.bin.tar.gz/download | tar xz
23+
- export PATH="$PWD/doxygen-1.8.15/bin:$PATH"
2324
script:
2425
- tools/check-style.sh
2526
- flake8
@@ -32,7 +33,7 @@ matrix:
3233
diff -rq $installed ./include/pybind11
3334
- |
3435
# Barebones build
35-
cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON
36+
cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(which $PY_CMD)
3637
make pytest -j 2
3738
make cpptest -j 2
3839
# The following are regular test configurations, including optional dependencies.

include/pybind11/numpy.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1484,7 +1484,10 @@ struct vectorize_helper {
14841484
private:
14851485
remove_reference_t<Func> f;
14861486

1487-
template <size_t Index> using param_n_t = typename pack_element<Index, typename vectorize_arg<Args>::call_type...>::type;
1487+
// Internal compiler error in MSVC 19.16.27025.1 (Visual Studio 2017 15.9.4), when compiling with "/permissive-" flag
1488+
// when arg_call_types is manually inlined.
1489+
using arg_call_types = std::tuple<typename vectorize_arg<Args>::call_type...>;
1490+
template <size_t Index> using param_n_t = typename std::tuple_element<Index, arg_call_types>::type;
14881491

14891492
// Runs a vectorized function given arguments tuple and three index sequences:
14901493
// - Index is the full set of 0 ... (N-1) argument indices;

include/pybind11/pybind11.h

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,17 @@
1010

1111
#pragma once
1212

13-
#if defined(_MSC_VER)
13+
#if defined(__INTEL_COMPILER)
14+
# pragma warning push
15+
# pragma warning disable 68 // integer conversion resulted in a change of sign
16+
# pragma warning disable 186 // pointless comparison of unsigned integer with zero
17+
# pragma warning disable 878 // incompatible exception specifications
18+
# pragma warning disable 1334 // the "template" keyword used for syntactic disambiguation may only be used within a template
19+
# pragma warning disable 1682 // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem)
20+
# pragma warning disable 1786 // function "strdup" was declared deprecated
21+
# pragma warning disable 1875 // offsetof applied to non-POD (Plain Old Data) types is nonstandard
22+
# pragma warning disable 2196 // warning #2196: routine is both "inline" and "noinline"
23+
#elif defined(_MSC_VER)
1424
# pragma warning(push)
1525
# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter
1626
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
@@ -19,15 +29,6 @@
1929
# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
2030
# pragma warning(disable: 4702) // warning C4702: unreachable code
2131
# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified
22-
#elif defined(__INTEL_COMPILER)
23-
# pragma warning(push)
24-
# pragma warning(disable: 68) // integer conversion resulted in a change of sign
25-
# pragma warning(disable: 186) // pointless comparison of unsigned integer with zero
26-
# pragma warning(disable: 878) // incompatible exception specifications
27-
# pragma warning(disable: 1334) // the "template" keyword used for syntactic disambiguation may only be used within a template
28-
# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem)
29-
# pragma warning(disable: 1875) // offsetof applied to non-POD (Plain Old Data) types is nonstandard
30-
# pragma warning(disable: 2196) // warning #2196: routine is both "inline" and "noinline"
3132
#elif defined(__GNUG__) && !defined(__clang__)
3233
# pragma GCC diagnostic push
3334
# pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
@@ -2263,6 +2264,15 @@ class gil_scoped_acquire {
22632264
auto const &internals = detail::get_internals();
22642265
tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate);
22652266

2267+
if (!tstate) {
2268+
/* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if
2269+
calling from a Python thread). Since we use a different key, this ensures
2270+
we don't create a new thread state and deadlock in PyEval_AcquireThread
2271+
below. Note we don't save this state with internals.tstate, since we don't
2272+
create it we would fail to clear it (its reference count should be > 0). */
2273+
tstate = PyGILState_GetThisThreadState();
2274+
}
2275+
22662276
if (!tstate) {
22672277
tstate = PyThreadState_New(internals.istate);
22682278
#if !defined(NDEBUG)
@@ -2470,10 +2480,8 @@ template <class T> function get_overload(const T *this_ptr, const char *name) {
24702480

24712481
NAMESPACE_END(PYBIND11_NAMESPACE)
24722482

2473-
#if defined(_MSC_VER)
2483+
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
24742484
# pragma warning(pop)
2475-
#elif defined(__INTEL_COMPILER)
2476-
/* Leave ignored warnings on */
24772485
#elif defined(__GNUG__) && !defined(__clang__)
24782486
# pragma GCC diagnostic pop
24792487
#endif

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ set(PYBIND11_TEST_FILES
4040
test_eval.cpp
4141
test_exceptions.cpp
4242
test_factory_constructors.cpp
43+
test_gil_scoped.cpp
4344
test_iostream.cpp
4445
test_kwargs_and_defaults.cpp
4546
test_local_bindings.cpp

tests/conftest.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def gc_collect():
185185
gc.collect()
186186

187187

188-
def pytest_namespace():
188+
def pytest_configure():
189189
"""Add import suppression and test requirements to `pytest` namespace"""
190190
try:
191191
import numpy as np
@@ -202,19 +202,17 @@ def pytest_namespace():
202202
pypy = platform.python_implementation() == "PyPy"
203203

204204
skipif = pytest.mark.skipif
205-
return {
206-
'suppress': suppress,
207-
'requires_numpy': skipif(not np, reason="numpy is not installed"),
208-
'requires_scipy': skipif(not np, reason="scipy is not installed"),
209-
'requires_eigen_and_numpy': skipif(not have_eigen or not np,
210-
reason="eigen and/or numpy are not installed"),
211-
'requires_eigen_and_scipy': skipif(not have_eigen or not scipy,
212-
reason="eigen and/or scipy are not installed"),
213-
'unsupported_on_pypy': skipif(pypy, reason="unsupported on PyPy"),
214-
'unsupported_on_py2': skipif(sys.version_info.major < 3,
215-
reason="unsupported on Python 2.x"),
216-
'gc_collect': gc_collect
217-
}
205+
pytest.suppress = suppress
206+
pytest.requires_numpy = skipif(not np, reason="numpy is not installed")
207+
pytest.requires_scipy = skipif(not np, reason="scipy is not installed")
208+
pytest.requires_eigen_and_numpy = skipif(not have_eigen or not np,
209+
reason="eigen and/or numpy are not installed")
210+
pytest.requires_eigen_and_scipy = skipif(
211+
not have_eigen or not scipy, reason="eigen and/or scipy are not installed")
212+
pytest.unsupported_on_pypy = skipif(pypy, reason="unsupported on PyPy")
213+
pytest.unsupported_on_py2 = skipif(sys.version_info.major < 3,
214+
reason="unsupported on Python 2.x")
215+
pytest.gc_collect = gc_collect
218216

219217

220218
def _test_import_pybind11():

tests/pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ filterwarnings =
1313
ignore::ImportWarning
1414
# bogus numpy ABI warning (see numpy/#432)
1515
ignore:.*numpy.dtype size changed.*:RuntimeWarning
16+
ignore:.*numpy.ufunc size changed.*:RuntimeWarning

tests/test_gil_scoped.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
tests/test_gil_scoped.cpp -- acquire and release gil
3+
4+
Copyright (c) 2017 Borja Zarco (Google LLC) <[email protected]>
5+
6+
All rights reserved. Use of this source code is governed by a
7+
BSD-style license that can be found in the LICENSE file.
8+
*/
9+
10+
#include "pybind11_tests.h"
11+
#include <pybind11/functional.h>
12+
13+
14+
class VirtClass {
15+
public:
16+
virtual void virtual_func() {}
17+
virtual void pure_virtual_func() = 0;
18+
};
19+
20+
class PyVirtClass : public VirtClass {
21+
void virtual_func() override {
22+
PYBIND11_OVERLOAD(void, VirtClass, virtual_func,);
23+
}
24+
void pure_virtual_func() override {
25+
PYBIND11_OVERLOAD_PURE(void, VirtClass, pure_virtual_func,);
26+
}
27+
};
28+
29+
TEST_SUBMODULE(gil_scoped, m) {
30+
py::class_<VirtClass, PyVirtClass>(m, "VirtClass")
31+
.def(py::init<>())
32+
.def("virtual_func", &VirtClass::virtual_func)
33+
.def("pure_virtual_func", &VirtClass::pure_virtual_func);
34+
35+
m.def("test_callback_py_obj",
36+
[](py::object func) { func(); });
37+
m.def("test_callback_std_func",
38+
[](const std::function<void()> &func) { func(); });
39+
m.def("test_callback_virtual_func",
40+
[](VirtClass &virt) { virt.virtual_func(); });
41+
m.def("test_callback_pure_virtual_func",
42+
[](VirtClass &virt) { virt.pure_virtual_func(); });
43+
}

tests/test_gil_scoped.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import multiprocessing
2+
import threading
3+
from pybind11_tests import gil_scoped as m
4+
5+
6+
def _run_in_process(target, *args, **kwargs):
7+
"""Runs target in process and returns its exitcode after 10s (None if still alive)."""
8+
process = multiprocessing.Process(target=target, args=args, kwargs=kwargs)
9+
process.daemon = True
10+
try:
11+
process.start()
12+
# Do not need to wait much, 10s should be more than enough.
13+
process.join(timeout=10)
14+
return process.exitcode
15+
finally:
16+
if process.is_alive():
17+
process.terminate()
18+
19+
20+
def _python_to_cpp_to_python():
21+
"""Calls different C++ functions that come back to Python."""
22+
class ExtendedVirtClass(m.VirtClass):
23+
def virtual_func(self):
24+
pass
25+
26+
def pure_virtual_func(self):
27+
pass
28+
29+
extended = ExtendedVirtClass()
30+
m.test_callback_py_obj(lambda: None)
31+
m.test_callback_std_func(lambda: None)
32+
m.test_callback_virtual_func(extended)
33+
m.test_callback_pure_virtual_func(extended)
34+
35+
36+
def _python_to_cpp_to_python_from_threads(num_threads, parallel=False):
37+
"""Calls different C++ functions that come back to Python, from Python threads."""
38+
threads = []
39+
for _ in range(num_threads):
40+
thread = threading.Thread(target=_python_to_cpp_to_python)
41+
thread.daemon = True
42+
thread.start()
43+
if parallel:
44+
threads.append(thread)
45+
else:
46+
thread.join()
47+
for thread in threads:
48+
thread.join()
49+
50+
51+
def test_python_to_cpp_to_python_from_thread():
52+
"""Makes sure there is no GIL deadlock when running in a thread.
53+
54+
It runs in a separate process to be able to stop and assert if it deadlocks.
55+
"""
56+
assert _run_in_process(_python_to_cpp_to_python_from_threads, 1) == 0
57+
58+
59+
def test_python_to_cpp_to_python_from_thread_multiple_parallel():
60+
"""Makes sure there is no GIL deadlock when running in a thread multiple times in parallel.
61+
62+
It runs in a separate process to be able to stop and assert if it deadlocks.
63+
"""
64+
assert _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=True) == 0
65+
66+
67+
def test_python_to_cpp_to_python_from_thread_multiple_sequential():
68+
"""Makes sure there is no GIL deadlock when running in a thread multiple times sequentially.
69+
70+
It runs in a separate process to be able to stop and assert if it deadlocks.
71+
"""
72+
assert _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=False) == 0
73+
74+
75+
def test_python_to_cpp_to_python_from_process():
76+
"""Makes sure there is no GIL deadlock when using processes.
77+
78+
This test is for completion, but it was never an issue.
79+
"""
80+
assert _run_in_process(_python_to_cpp_to_python) == 0

0 commit comments

Comments
 (0)