Skip to content

Commit 50d3ccd

Browse files
committed
Allow per-interpreter internals/local_internals
1 parent d28904f commit 50d3ccd

File tree

4 files changed

+96
-9
lines changed

4 files changed

+96
-9
lines changed

Diff for: CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ option(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION
9292
"To enforce that a handle_type_name<> specialization exists" OFF)
9393
option(PYBIND11_SIMPLE_GIL_MANAGEMENT
9494
"Use simpler GIL management logic that does not support disassociation" OFF)
95+
option(PYBIND11_SUBINTERPRETER_SUPPORT "Enable support for sub-interpreters" OFF)
9596
option(PYBIND11_NUMPY_1_ONLY
9697
"Disable NumPy 2 support to avoid changes to previous pybind11 versions." OFF)
9798
set(PYBIND11_INTERNALS_VERSION
@@ -105,6 +106,9 @@ endif()
105106
if(PYBIND11_SIMPLE_GIL_MANAGEMENT)
106107
add_compile_definitions(PYBIND11_SIMPLE_GIL_MANAGEMENT)
107108
endif()
109+
if(PYBIND11_SUBINTERPRETER_SUPPORT)
110+
add_compile_definitions(PYBIND11_SUBINTERPRETER_SUPPORT)
111+
endif()
108112
if(PYBIND11_NUMPY_1_ONLY)
109113
add_compile_definitions(PYBIND11_NUMPY_1_ONLY)
110114
endif()

Diff for: include/pybind11/detail/internals.h

+79-5
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,25 @@ struct type_info {
260260
/// Each module locally stores a pointer to the `internals` data. The data
261261
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
262262
inline internals **&get_internals_pp() {
263+
#if defined(PYPY_VERSION) || defined(GRAALVM_PYTHON) || PY_VERSION_HEX < 0x030C0000 \
264+
|| !defined(PYBIND11_SUBINTERPRETER_SUPPORT)
263265
static internals **internals_pp = nullptr;
266+
#else
267+
static thread_local internals **internals_pp = nullptr;
268+
// This is one per interpreter, we cache it but if the thread changed
269+
// then we need to invalidate our cache
270+
// the caller will find the right value and set it if its null
271+
static thread_local PyThreadState *tstate_cached = nullptr;
272+
# if PY_VERSION_HEX < 0x030D0000
273+
PyThreadState *tstate = _PyThreadState_UncheckedGet();
274+
# else
275+
PyThreadState *tstate = PyThreadState_UncheckedGet();
276+
# endif
277+
if (tstate != tstate_cached) {
278+
tstate_cached = tstate;
279+
internals_pp = nullptr;
280+
}
281+
#endif
264282
return internals_pp;
265283
}
266284

@@ -427,9 +445,6 @@ PYBIND11_NOINLINE internals &get_internals() {
427445
return **internals_pp;
428446
}
429447

430-
#if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
431-
gil_scoped_acquire gil;
432-
#else
433448
// Ensure that the GIL is held since we will need to make Python calls.
434449
// Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
435450
struct gil_scoped_acquire_local {
@@ -439,7 +454,7 @@ PYBIND11_NOINLINE internals &get_internals() {
439454
~gil_scoped_acquire_local() { PyGILState_Release(state); }
440455
const PyGILState_STATE state;
441456
} gil;
442-
#endif
457+
443458
error_scope err_scope;
444459

445460
dict state_dict = get_python_state_dict();
@@ -455,7 +470,12 @@ PYBIND11_NOINLINE internals &get_internals() {
455470
// libc++ with CPython doesn't require this (types are explicitly exported)
456471
// libc++ with PyPy still need it, awaiting further investigation
457472
#if !defined(__GLIBCXX__)
458-
(*internals_pp)->registered_exception_translators.push_front(&translate_local_exception);
473+
if ((*internals_pp)->registered_exception_translators.empty()
474+
|| (*internals_pp)->registered_exception_translators.front()
475+
!= &translate_local_exception) {
476+
(*internals_pp)
477+
->registered_exception_translators.push_front(&translate_local_exception);
478+
}
459479
#endif
460480
} else {
461481
if (!internals_pp) {
@@ -515,7 +535,61 @@ inline local_internals &get_local_internals() {
515535
// static deinitialization fiasco. In order to avoid it we avoid destruction of the
516536
// local_internals static. One can read more about the problem and current solution here:
517537
// https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables
538+
539+
#if defined(PYPY_VERSION) || defined(GRAALVM_PYTHON) || PY_VERSION_HEX < 0x030C0000 \
540+
|| !defined(PYBIND11_SUBINTERPRETER_SUPPORT)
518541
static auto *locals = new local_internals();
542+
#else
543+
static thread_local local_internals *locals = nullptr;
544+
// This is one per interpreter, we cache it but if the interpreter changed
545+
// then we need to invalidate our cache and re-fetch from the state dict
546+
static thread_local PyThreadState *tstate_cached = nullptr;
547+
# if PY_VERSION_HEX < 0x030D0000
548+
PyThreadState *tstate = _PyThreadState_UncheckedGet();
549+
# else
550+
PyThreadState *tstate = PyThreadState_UncheckedGet();
551+
# endif
552+
if (!tstate) {
553+
pybind11_fail(
554+
"pybind11::detail::get_local_internals() called without a current python thread");
555+
}
556+
if (tstate != tstate_cached) {
557+
// we create a unique value at first run which is based on a pointer to
558+
// a (non-thread_local) static value in this function, then multiple
559+
// loaded modules using this code will still each have a unique key.
560+
static const std::string this_module_idstr
561+
= PYBIND11_MODULE_LOCAL_ID
562+
+ std::to_string(reinterpret_cast<uintptr_t>(&this_module_idstr));
563+
564+
// Ensure that the GIL is held since we will need to make Python calls.
565+
// Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
566+
struct gil_scoped_acquire_local {
567+
gil_scoped_acquire_local() : state(PyGILState_Ensure()) {}
568+
gil_scoped_acquire_local(const gil_scoped_acquire_local &) = delete;
569+
gil_scoped_acquire_local &operator=(const gil_scoped_acquire_local &) = delete;
570+
~gil_scoped_acquire_local() { PyGILState_Release(state); }
571+
const PyGILState_STATE state;
572+
} gil;
573+
574+
error_scope err_scope;
575+
dict state_dict = get_python_state_dict();
576+
object local_capsule = reinterpret_steal<object>(
577+
dict_getitemstringref(state_dict.ptr(), this_module_idstr.c_str()));
578+
if (!local_capsule) {
579+
locals = new local_internals();
580+
state_dict[this_module_idstr.c_str()] = capsule(reinterpret_cast<void *>(locals));
581+
} else {
582+
void *ptr = PyCapsule_GetPointer(local_capsule.ptr(), nullptr);
583+
if (!ptr) {
584+
raise_from(PyExc_SystemError, "pybind11::detail::get_local_internals() FAILED");
585+
throw error_already_set();
586+
}
587+
locals = reinterpret_cast<local_internals *>(ptr);
588+
}
589+
tstate_cached = tstate;
590+
}
591+
#endif
592+
519593
return *locals;
520594
}
521595

Diff for: tests/test_embed/external_module.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace py = pybind11;
66
* modules aren't preserved over a finalize/initialize.
77
*/
88

9-
PYBIND11_MODULE(external_module, m, py::mod_gil_not_used()) {
9+
PYBIND11_MODULE(external_module, m, py::mod_gil_not_used(), py::mod_multi_interpreter_one_gil()) {
1010
class A {
1111
public:
1212
explicit A(int value) : v{value} {};

Diff for: tests/test_embed/test_interpreter.cpp

+12-3
Original file line numberDiff line numberDiff line change
@@ -338,11 +338,16 @@ TEST_CASE("Subinterpreter") {
338338
auto *main_tstate = PyThreadState_Get();
339339
auto *sub_tstate = Py_NewInterpreter();
340340

341-
// Subinterpreters get their own copy of builtins. detail::get_internals() still
342-
// works by returning from the static variable, i.e. all interpreters share a single
343-
// global pybind11::internals;
341+
// Subinterpreters get their own copy of builtins.
344342
REQUIRE_FALSE(has_state_dict_internals_obj());
343+
344+
#ifdef PYBIND11_SUBINTERPRETER_SUPPORT
345+
// internals hasn't been populated yet, but will be different for the subinterpreter
346+
REQUIRE_FALSE(has_pybind11_internals_static());
347+
#else
348+
// This static is still defined
345349
REQUIRE(has_pybind11_internals_static());
350+
#endif
346351

347352
// Modules tags should be gone.
348353
REQUIRE_FALSE(py::hasattr(py::module_::import("__main__"), "tag"));
@@ -354,12 +359,16 @@ TEST_CASE("Subinterpreter") {
354359
REQUIRE(m.attr("add")(1, 2).cast<int>() == 3);
355360
}
356361

362+
// The subinterpreter now has internals populated since we imported a pybind11 module
363+
REQUIRE(has_pybind11_internals_static());
364+
357365
// Restore main interpreter.
358366
Py_EndInterpreter(sub_tstate);
359367
PyThreadState_Swap(main_tstate);
360368

361369
REQUIRE(py::hasattr(py::module_::import("__main__"), "main_tag"));
362370
REQUIRE(py::hasattr(py::module_::import("widget_module"), "extension_module_tag"));
371+
REQUIRE(has_state_dict_internals_obj());
363372
}
364373

365374
TEST_CASE("Execution frame") {

0 commit comments

Comments
 (0)