Skip to content

Commit c78c613

Browse files
committed
All bells & whistles.
1 parent f3cee36 commit c78c613

File tree

7 files changed

+72
-27
lines changed

7 files changed

+72
-27
lines changed

.github/workflows/ci.yml

+4
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,12 @@ jobs:
102102
run: python -m pip install pytest-github-actions-annotate-failures
103103

104104
# First build - C++11 mode and inplace
105+
# More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON here.
105106
- name: Configure C++11 ${{ matrix.args }}
106107
run: >
107108
cmake -S . -B .
108109
-DPYBIND11_WERROR=ON
110+
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON
109111
-DDOWNLOAD_CATCH=ON
110112
-DDOWNLOAD_EIGEN=ON
111113
-DCMAKE_CXX_STANDARD=11
@@ -129,10 +131,12 @@ jobs:
129131
run: git clean -fdx
130132

131133
# Second build - C++17 mode and in a build directory
134+
# More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF here.
132135
- name: Configure C++17
133136
run: >
134137
cmake -S . -B build2
135138
-DPYBIND11_WERROR=ON
139+
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
136140
-DDOWNLOAD_CATCH=ON
137141
-DDOWNLOAD_EIGEN=ON
138142
-DCMAKE_CXX_STANDARD=17

CMakeLists.txt

+4-3
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,14 @@ endif()
9191
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT})
9292
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT})
9393
option(PYBIND11_NOPYTHON "Disable search for Python" OFF)
94-
option(PYBIND11_SIMPLE_GIL "Use simpler GIL access logic that does not support disassociation" OFF)
94+
option(PYBIND11_SIMPLE_GIL_MANAGEMENT
95+
"Use simpler GIL management logic that does not support disassociation" OFF)
9596
set(PYBIND11_INTERNALS_VERSION
9697
""
9798
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.")
9899

99-
if(PYBIND11_SIMPLE_GIL)
100-
add_compile_definitions(PYBIND11_SIMPLE_GIL)
100+
if(PYBIND11_SIMPLE_GIL_MANAGEMENT)
101+
add_compile_definitions(PYBIND11_SIMPLE_GIL_MANAGEMENT)
101102
endif()
102103

103104
cmake_dependent_option(

docs/upgrade.rst

+8-10
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,14 @@ modernization and other useful information.
1313
v2.10
1414
=====
1515

16-
The current scoped GIL implementation doesn't support nested access. In pybind11
17-
In 2.10.1, a configuration option ``PYBIND11_SIMPLE_GIL`` was added, defaulting
18-
to OFF; the simpler GIL implementation supports nested access, but does not
19-
support dissociation (the ``true`` parameter of ``gil_scope_release``). In
20-
pybind11 2.11, we plan to change the default to ON. If you need the old
21-
behavior, please set ``PYBIND11_SIMPLE_GIL`` to OFF. We plan to have an example
22-
for manually supporting dissociation.
23-
24-
There may be an unconfirmed ABI breakage between 2.9 and 2.10. We plan to bump
25-
the internals number in 2.11.
16+
``py::gil_scoped_acquire`` & ``py::gil_scoped_release`` in pybind11 versions
17+
< v2.10.1 do not support nested access. In v2.10.1, a configuration option
18+
``PYBIND11_SIMPLE_GIL_MANAGEMENT`` was added, defaulting to ``OFF``; the
19+
simpler implementations support nested access, but do not support dissociation
20+
(``py::gil_scoped_release(true)``). In pybind11 2.11, we plan to change the
21+
default to ``ON``, to avoid pitfalls of the implementations with dissociation
22+
(see #4216 for more information). Note that the dissociation feature is very
23+
rarely used and not exercised in any pybind11 unit tests.
2624

2725
.. _upgrade-guide-2.9:
2826

include/pybind11/detail/common.h

+4
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@
229229
# undef copysign
230230
#endif
231231

232+
#if defined(PYPY_VERSION) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
233+
# define PYBIND11_SIMPLE_GIL_MANAGEMENT
234+
#endif
235+
232236
#if defined(_MSC_VER)
233237
# if defined(PYBIND11_DEBUG_MARKER)
234238
# define _DEBUG

include/pybind11/detail/internals.h

+12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99

1010
#pragma once
1111

12+
#if defined(WITH_THREAD) && defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
13+
# include "../gil.h"
14+
#endif
15+
1216
#include "../pytypes.h"
1317

1418
#include <exception>
@@ -169,10 +173,12 @@ struct internals {
169173
PyTypeObject *default_metaclass;
170174
PyObject *instance_base;
171175
#if defined(WITH_THREAD)
176+
// Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined:
172177
PYBIND11_TLS_KEY_INIT(tstate)
173178
# if PYBIND11_INTERNALS_VERSION > 4
174179
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
175180
# endif // PYBIND11_INTERNALS_VERSION > 4
181+
// Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined:
176182
PyInterpreterState *istate = nullptr;
177183
~internals() {
178184
# if PYBIND11_INTERNALS_VERSION > 4
@@ -408,6 +414,10 @@ PYBIND11_NOINLINE internals &get_internals() {
408414
return **internals_pp;
409415
}
410416

417+
#if defined(WITH_THREAD)
418+
# if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
419+
gil_scoped_acquire gil;
420+
# else
411421
// Ensure that the GIL is held since we will need to make Python calls.
412422
// Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
413423
struct gil_scoped_acquire_local {
@@ -417,6 +427,8 @@ PYBIND11_NOINLINE internals &get_internals() {
417427
~gil_scoped_acquire_local() { PyGILState_Release(state); }
418428
const PyGILState_STATE state;
419429
} gil;
430+
# endif
431+
#endif
420432
error_scope err_scope;
421433

422434
PYBIND11_STR_TYPE id(PYBIND11_INTERNALS_ID);

include/pybind11/gil.h

+40-13
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
#pragma once
1111

1212
#include "detail/common.h"
13-
#include "detail/internals.h"
13+
14+
#if defined(WITH_THREAD) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
15+
# include "detail/internals.h"
16+
#endif
1417

1518
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
1619

@@ -21,7 +24,9 @@ PyThreadState *get_thread_state_unchecked();
2124

2225
PYBIND11_NAMESPACE_END(detail)
2326

24-
#if defined(WITH_THREAD) && !defined(PYPY_VERSION) && !defined(PYBIND11_SIMPLE_GIL)
27+
#if defined(WITH_THREAD)
28+
29+
# if !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
2530

2631
/* The functions below essentially reproduce the PyGILState_* API using a RAII
2732
* pattern, but there are a few important differences:
@@ -62,11 +67,11 @@ class gil_scoped_acquire {
6267

6368
if (!tstate) {
6469
tstate = PyThreadState_New(internals.istate);
65-
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
70+
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
6671
if (!tstate) {
6772
pybind11_fail("scoped_acquire: could not create thread state!");
6873
}
69-
# endif
74+
# endif
7075
tstate->gilstate_counter = 0;
7176
PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
7277
} else {
@@ -87,20 +92,20 @@ class gil_scoped_acquire {
8792

8893
PYBIND11_NOINLINE void dec_ref() {
8994
--tstate->gilstate_counter;
90-
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
95+
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
9196
if (detail::get_thread_state_unchecked() != tstate) {
9297
pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
9398
}
9499
if (tstate->gilstate_counter < 0) {
95100
pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
96101
}
97-
# endif
102+
# endif
98103
if (tstate->gilstate_counter == 0) {
99-
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
104+
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
100105
if (!release) {
101106
pybind11_fail("scoped_acquire::dec_ref(): internal error!");
102107
}
103-
# endif
108+
# endif
104109
PyThreadState_Clear(tstate);
105110
if (active) {
106111
PyThreadState_DeleteCurrent();
@@ -178,12 +183,14 @@ class gil_scoped_release {
178183
bool disassoc;
179184
bool active = true;
180185
};
181-
#elif defined(PYPY_VERSION) || defined(PYBIND11_SIMPLE_GIL)
186+
187+
# else // PYBIND11_SIMPLE_GIL_MANAGEMENT
188+
182189
class gil_scoped_acquire {
183190
PyGILState_STATE state;
184191

185192
public:
186-
gil_scoped_acquire() { state = PyGILState_Ensure(); }
193+
gil_scoped_acquire() : state{PyGILState_Ensure()} {}
187194
gil_scoped_acquire(const gil_scoped_acquire &) = delete;
188195
gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete;
189196
~gil_scoped_acquire() { PyGILState_Release(state); }
@@ -194,19 +201,39 @@ class gil_scoped_release {
194201
PyThreadState *state;
195202

196203
public:
197-
gil_scoped_release() { state = PyEval_SaveThread(); }
204+
gil_scoped_release() : state{PyEval_SaveThread()} {}
198205
gil_scoped_release(const gil_scoped_release &) = delete;
199206
gil_scoped_release &operator=(const gil_scoped_acquire &) = delete;
200207
~gil_scoped_release() { PyEval_RestoreThread(state); }
201208
void disarm() {}
202209
};
203-
#else
210+
211+
# endif // PYBIND11_SIMPLE_GIL_MANAGEMENT
212+
213+
#else // WITH_THREAD
214+
204215
class gil_scoped_acquire {
216+
public:
217+
gil_scoped_acquire() {
218+
// Trick to suppress `unused variable` error messages (at call sites).
219+
(void) (this != (this + 1));
220+
}
221+
gil_scoped_acquire(const gil_scoped_acquire &) = delete;
222+
gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete;
205223
void disarm() {}
206224
};
225+
207226
class gil_scoped_release {
227+
public:
228+
gil_scoped_release() {
229+
// Trick to suppress `unused variable` error messages (at call sites).
230+
(void) (this != (this + 1));
231+
}
232+
gil_scoped_release(const gil_scoped_release &) = delete;
233+
gil_scoped_release &operator=(const gil_scoped_acquire &) = delete;
208234
void disarm() {}
209235
};
210-
#endif
236+
237+
#endif // WITH_THREAD
211238

212239
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

tests/test_embed/test_interpreter.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,6 @@ TEST_CASE("Threads") {
293293

294294
{
295295
py::gil_scoped_release gil_release{};
296-
REQUIRE(has_pybind11_internals_static());
297296

298297
auto threads = std::vector<std::thread>();
299298
for (auto i = 0; i < num_threads; ++i) {

0 commit comments

Comments
 (0)