Skip to content

Commit 3debb51

Browse files
committed
Organize internals-like functionality as struct native_enum_type_map_v1, including cleanup in embed.h (no more leaking).
1 parent 8737bea commit 3debb51

File tree

8 files changed

+81
-44
lines changed

8 files changed

+81
-44
lines changed

include/pybind11/cast.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class type_caster_enum_type {
5858

5959
template <typename SrcType>
6060
static handle cast(SrcType &&src, return_value_policy, handle parent) {
61-
auto const &natives = get_native_enum_type_map();
61+
auto const &natives = native_enum_type_map::get();
6262
auto found = natives.find(std::type_index(typeid(EnumType)));
6363
if (found != natives.end()) {
6464
return handle(found->second)(static_cast<Underlying>(src)).release();
@@ -71,7 +71,7 @@ class type_caster_enum_type {
7171
}
7272

7373
bool load(handle src, bool convert) {
74-
auto const &natives = get_native_enum_type_map();
74+
auto const &natives = native_enum_type_map::get();
7575
auto found = natives.find(std::type_index(typeid(EnumType)));
7676
if (found != natives.end()) {
7777
if (!isinstance(src, found->second)) {
@@ -125,7 +125,7 @@ class type_caster<EnumType,
125125

126126
template <typename T, detail::enable_if_t<std::is_enum<T>::value, int> = 0>
127127
bool isinstance_native_enum_impl(handle obj, const std::type_info &tp) {
128-
auto const &natives = get_native_enum_type_map();
128+
auto const &natives = native_enum_type_map::get();
129129
auto found = natives.find(tp);
130130
if (found == natives.end()) {
131131
return false;
@@ -1138,7 +1138,7 @@ T cast(const handle &handle) {
11381138
"Unable to cast type to reference: value is local to type caster");
11391139
#ifndef NDEBUG
11401140
if (is_enum_cast) {
1141-
auto const &natives = get_native_enum_type_map();
1141+
auto const &natives = native_enum_type_map::get();
11421142
auto found = natives.find(std::type_index(typeid(intrinsic_t<T>)));
11431143
if (found != natives.end()) {
11441144
pybind11_fail("Unable to cast native enum type to reference");

include/pybind11/detail/internals.h

+63-32
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ struct type_info {
311311
/// Each module locally stores a pointer to the `internals` data. The data
312312
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
313313
inline internals **&get_internals_pp() {
314+
// The reason for the double-indirection is documented here:
315+
// https://github.com/pybind/pybind11/pull/1092
314316
static internals **internals_pp = nullptr;
315317
return internals_pp;
316318
}
@@ -647,52 +649,81 @@ T &get_or_create_shared_data(const std::string &name) {
647649

648650
PYBIND11_NAMESPACE_BEGIN(detail)
649651

650-
#define PYBIND11_NATIVE_ENUM_TYPE_MAP_ABI_ID \
651-
"__pybind11_native_enum_type_map_v1" PYBIND11_ABI_ID "__"
652+
struct native_enum_type_map_v1 {
653+
static constexpr const char *abi_id_c_str
654+
= "__pybind11_native_enum_type_map_v1" PYBIND11_ABI_ID "__";
652655

653-
using native_enum_type_map = type_map<PyObject *>;
656+
using native_enum_type_map = type_map<PyObject *>;
654657

655-
inline native_enum_type_map **&get_native_enum_types_pp() {
656-
static native_enum_type_map **native_enum_types_pp = nullptr;
657-
return native_enum_types_pp;
658-
}
659-
660-
PYBIND11_NOINLINE native_enum_type_map &get_native_enum_type_map() {
661-
native_enum_type_map **&native_enum_type_map_pp = get_native_enum_types_pp();
662-
if (native_enum_type_map_pp && *native_enum_type_map_pp) {
663-
return **native_enum_type_map_pp;
658+
static native_enum_type_map **&native_enum_type_map_pp() {
659+
static native_enum_type_map **pp;
660+
return pp;
664661
}
665662

666-
gil_scoped_acquire_simple gil;
667-
error_scope err_scope;
663+
static native_enum_type_map *get_existing() {
664+
if (native_enum_type_map_pp() && *native_enum_type_map_pp()) {
665+
return *native_enum_type_map_pp();
666+
}
668667

669-
constexpr const char *id_cstr = PYBIND11_NATIVE_ENUM_TYPE_MAP_ABI_ID;
670-
str id(id_cstr);
668+
gil_scoped_acquire_simple gil;
669+
error_scope err_scope;
671670

672-
dict state_dict = get_python_state_dict();
671+
str abi_id_str(abi_id_c_str);
672+
dict state_dict = get_python_state_dict();
673+
if (!state_dict.contains(abi_id_str)) {
674+
return nullptr;
675+
}
673676

674-
if (state_dict.contains(id_cstr)) {
675-
void *raw_ptr = PyCapsule_GetPointer(state_dict[id].ptr(), id_cstr);
677+
void *raw_ptr = PyCapsule_GetPointer(state_dict[abi_id_str].ptr(), abi_id_c_str);
676678
if (raw_ptr == nullptr) {
677679
raise_from(PyExc_SystemError,
678-
"pybind11::detail::get_native_enum_type_map(): Retrieve "
679-
"native_enum_type_map** from capsule FAILED");
680+
"pybind11::detail::native_enum_type_map::get_existing():"
681+
" Retrieve native_enum_type_map** from capsule FAILED");
680682
}
681-
native_enum_type_map_pp = static_cast<native_enum_type_map **>(raw_ptr);
683+
native_enum_type_map_pp() = static_cast<native_enum_type_map **>(raw_ptr);
684+
return *native_enum_type_map_pp();
682685
}
683686

684-
if (native_enum_type_map_pp && *native_enum_type_map_pp) {
685-
return **native_enum_type_map_pp;
687+
static native_enum_type_map &get() {
688+
if (get_existing() != nullptr) {
689+
return **native_enum_type_map_pp();
690+
}
691+
if (native_enum_type_map_pp() == nullptr) {
692+
native_enum_type_map_pp() = new native_enum_type_map *();
693+
}
694+
*native_enum_type_map_pp() = new native_enum_type_map();
695+
get_python_state_dict()[abi_id_c_str] = capsule(native_enum_type_map_pp(), abi_id_c_str);
696+
return **native_enum_type_map_pp();
686697
}
687698

688-
if (!native_enum_type_map_pp) {
689-
native_enum_type_map_pp = new native_enum_type_map *();
690-
}
691-
auto *&native_enum_type_map_ptr = *native_enum_type_map_pp;
692-
native_enum_type_map_ptr = new native_enum_type_map();
693-
state_dict[id] = capsule(native_enum_type_map_pp, id_cstr);
694-
return **native_enum_type_map_pp;
695-
}
699+
struct scoped_clear {
700+
// To be called BEFORE Py_Finalize().
701+
scoped_clear() {
702+
if (get_existing() != nullptr) {
703+
for (auto it : **native_enum_type_map_pp()) {
704+
Py_DECREF(it.second);
705+
}
706+
(*native_enum_type_map_pp())->clear();
707+
arm_dtor = true;
708+
}
709+
}
710+
711+
// To be called AFTER Py_Finalize().
712+
~scoped_clear() {
713+
if (arm_dtor) {
714+
delete *native_enum_type_map_pp();
715+
*native_enum_type_map_pp() = nullptr;
716+
}
717+
}
718+
719+
scoped_clear(const scoped_clear &) = delete;
720+
scoped_clear &operator=(const scoped_clear &) = delete;
721+
722+
bool arm_dtor = false;
723+
};
724+
};
725+
726+
using native_enum_type_map = native_enum_type_map_v1;
696727

697728
PYBIND11_NAMESPACE_END(detail)
698729

include/pybind11/embed.h

+2
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
213213
214214
\endrst */
215215
inline void finalize_interpreter() {
216+
detail::native_enum_type_map::scoped_clear native_enum_type_map_clear;
217+
216218
handle builtins(PyEval_GetBuiltins());
217219
const char *id = PYBIND11_INTERNALS_ID;
218220

include/pybind11/native_enum.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class native_enum : public detail::native_enum_data {
3232
"pybind11::native_enum<...>(\"" + enum_name_encoded
3333
+ "\") is already registered as a `pybind11::enum_` or `pybind11::class_`!");
3434
}
35-
if (detail::get_native_enum_type_map().count(enum_type_index)) {
35+
if (detail::native_enum_type_map::get().count(enum_type_index)) {
3636
pybind11_fail("pybind11::native_enum<...>(\"" + enum_name_encoded
3737
+ "\") is already registered!");
3838
}

include/pybind11/pybind11.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -1295,8 +1295,7 @@ class module_ : public object {
12951295
for (auto doc : data.docs) {
12961296
py_enum[doc[int_(0)]].attr("__doc__") = doc[int_(1)];
12971297
}
1298-
// Intentionally leak Python reference.
1299-
detail::get_native_enum_type_map()[data.enum_type_index] = py_enum.release().ptr();
1298+
detail::native_enum_type_map::get()[data.enum_type_index] = py_enum.release().ptr();
13001299
return *this;
13011300
}
13021301
};
@@ -2204,7 +2203,7 @@ class enum_ : public class_<Type> {
22042203
enum_(const handle &scope, const char *name, const Extra &...extra)
22052204
: class_<Type>(scope, name, extra...), m_base(*this, scope) {
22062205
{
2207-
auto const &natives = detail::get_native_enum_type_map();
2206+
auto const &natives = detail::native_enum_type_map::get();
22082207
auto found = natives.find(std::type_index(typeid(Type)));
22092208
if (found != natives.end()) {
22102209
pybind11_fail("pybind11::enum_ \"" + std::string(name)

tests/conftest.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,17 @@
99
import gc
1010
import re
1111
import textwrap
12+
import traceback
1213

1314
import pytest
1415

1516
# Early diagnostic for failed imports
16-
import pybind11_tests
17+
try:
18+
import pybind11_tests
19+
except Exception:
20+
# pytest does not show the traceback without this.
21+
traceback.print_exc()
22+
raise
1723

1824
_long_marker = re.compile(r"([0-9])L")
1925
_hexadecimal = re.compile(r"0x[0-9a-fA-F]+")

tests/test_native_enum.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
6767
TEST_SUBMODULE(native_enum, m) {
6868
using namespace test_native_enum;
6969

70-
m.attr("PYBIND11_NATIVE_ENUM_TYPE_MAP_ABI_ID") = PYBIND11_NATIVE_ENUM_TYPE_MAP_ABI_ID;
70+
m.attr("native_enum_type_map_abi_id_c_str") = py::detail::native_enum_type_map::abi_id_c_str;
7171

7272
m += py::native_enum<smallenum>("smallenum")
7373
.value("a", smallenum::a)

tests/test_native_enum.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88

99
def test_abi_id():
1010
assert re.match(
11-
"__pybind11_native_enum_type_map_v1_.*__$",
12-
m.PYBIND11_NATIVE_ENUM_TYPE_MAP_ABI_ID,
11+
"__pybind11_native_enum_type_map_v1_.*__$", m.native_enum_type_map_abi_id_c_str
1312
)
1413

1514

0 commit comments

Comments
 (0)