Skip to content

Commit 7c83fc9

Browse files
authored
Add detail/cross_extension_shared_state.h (#30008)
* Transfer PR #4329 from master to smart_holder branch, STEP 1. The patch .rej below are resolved, but THIS STATE DOES NOT BUILD: ``` clang++ -o pybind11/tests/test_constants_and_functions.os -c -std=c++17 -fPIC -fvisibility=hidden -O0 -g -Wall -Wextra -Wconversion -Wcast-qual -Wdeprecated -Wundef -Wnon-virtual-dtor -Wunused-result -Werror -isystem /usr/include/python3.10 -isystem /usr/include/eigen3 -DPYBIND11_STRICT_ASSERTS_CLASS_HOLDER_VS_TYPE_CASTER_MIX -DPYBIND11_ENABLE_TYPE_CASTER_ODR_GUARD_IF_AVAILABLE -DPYBIND11_TEST_BOOST -Ipybind11/include -I/usr/local/google/home/rwgk/forked/pybind11/include -I/usr/local/google/home/rwgk/clone/pybind11/include /usr/local/google/home/rwgk/forked/pybind11/tests/test_constants_and_functions.cpp In file included from /usr/local/google/home/rwgk/forked/pybind11/tests/test_constants_and_functions.cpp:11: In file included from /usr/local/google/home/rwgk/forked/pybind11/tests/pybind11_tests.h:3: In file included from /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/eval.h:14: /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/pybind11.h:1781:9: error: static_assert failed due to requirement '!holder_is_smart_holder == type_caster_type_is_type_caster_base_subtype' "py::class_ holder vs type_caster mismatch: missing PYBIND11_TYPE_CASTER_BASE_HOLDER(T, ...) or collision with custom py::detail::type_caster<T>?" static_assert(!holder_is_smart_holder == type_caster_type_is_type_caster_base_subtype, ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/pybind11.h:2460:11: note: in instantiation of function template specialization 'pybind11::class_<MyEnum>::class_<>' requested here : class_<Type>(scope, name, extra...), m_base(*this, scope) { ^ /usr/local/google/home/rwgk/forked/pybind11/tests/test_constants_and_functions.cpp:106:5: note: in instantiation of function template specialization 'pybind11::enum_<MyEnum>::enum_<>' requested here py::enum_<MyEnum>(m, "MyEnum") ^ 1 error generated. ``` ________ ``` rwgk.c.googlers.com:~/forked/pybind11 $ patch -p 1 < ~/native_enum_git_diff_master_2022-11-19+131942.patch patching file .github/workflows/python312.yml patching file CMakeLists.txt Hunk #1 FAILED at 111. Hunk #2 succeeded at 138 (offset 5 lines). 1 out of 2 hunks FAILED -- saving rejects to file CMakeLists.txt.rej patching file include/pybind11/cast.h Hunk #1 FAILED at 12. Hunk #2 succeeded at 78 (offset 30 lines). Hunk #3 succeeded at 1173 (offset 41 lines). 1 out of 3 hunks FAILED -- saving rejects to file include/pybind11/cast.h.rej patching file include/pybind11/detail/abi_platform_id.h patching file include/pybind11/detail/cross_extension_shared_state.h patching file include/pybind11/detail/internals.h Hunk #1 FAILED at 16. Hunk #2 succeeded at 109 (offset 1 line). Hunk #3 FAILED at 203. Hunk #4 succeeded at 398 (offset 11 lines). Hunk #5 succeeded at 457 (offset 11 lines). 2 out of 5 hunks FAILED -- saving rejects to file include/pybind11/detail/internals.h.rej patching file include/pybind11/detail/native_enum_data.h patching file include/pybind11/detail/type_map.h patching file include/pybind11/embed.h patching file include/pybind11/native_enum.h patching file include/pybind11/pybind11.h Hunk #1 FAILED at 12. Hunk #2 succeeded at 1269 (offset 1 line). Hunk #3 succeeded at 2457 (offset 255 lines). 1 out of 3 hunks FAILED -- saving rejects to file include/pybind11/pybind11.h.rej patching file include/pybind11/pytypes.h patching file tests/CMakeLists.txt Hunk #1 succeeded at 160 (offset 18 lines). patching file tests/conftest.py patching file tests/extra_python_package/test_files.py Hunk #2 FAILED at 47. 1 out of 2 hunks FAILED -- saving rejects to file tests/extra_python_package/test_files.py.rej patching file tests/test_embed/test_interpreter.cpp patching file tests/test_enum.cpp patching file tests/test_enum.py patching file tests/test_native_enum.cpp patching file tests/test_native_enum.py ``` * Make `smart_holder` code compatible with `type_caster_enum_type` * Fix `if` condition guarding `Unable to cast native enum type to reference` * WIP native_enum_add_to_parent * PYBIND11_SILENCE_MSVC_C4127 * Transfer upstream.yml/python312.yml changes from PR #4397 * clang-tidy (clang 15) auto-fix * Remove python312.yml: this is handled separately under PR #30006 * Fixes for ruff * Replace `PyEval_GetBuiltins()` in `finalize_interpreter()` with `get_python_state_dict()` * Bug fix: Ensure `state_dict` is destroyed before `Py_Finalize()` * Restore tests/test_embed/test_interpreter.cpp from google_pywrapcc_main * Restore include/pybind11/embed.h from google_pywrapcc_main * Back out all native_enum changes, leaving only the cross_extension_shared_state changes. * Undo unrelated one-line change (from `auto` to `internals`).
1 parent 19054b5 commit 7c83fc9

File tree

5 files changed

+337
-168
lines changed

5 files changed

+337
-168
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) 2022 The pybind Community.
2+
// All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
#pragma once
6+
7+
#include "common.h"
8+
9+
/// On MSVC, debug and release builds are not ABI-compatible!
10+
#if defined(_MSC_VER) && defined(_DEBUG)
11+
# define PYBIND11_BUILD_TYPE "_debug"
12+
#else
13+
# define PYBIND11_BUILD_TYPE ""
14+
#endif
15+
16+
/// Let's assume that different compilers are ABI-incompatible.
17+
/// A user can manually set this string if they know their
18+
/// compiler is compatible.
19+
#ifndef PYBIND11_COMPILER_TYPE
20+
# if defined(_MSC_VER)
21+
# define PYBIND11_COMPILER_TYPE "_msvc"
22+
# elif defined(__INTEL_COMPILER)
23+
# define PYBIND11_COMPILER_TYPE "_icc"
24+
# elif defined(__clang__)
25+
# define PYBIND11_COMPILER_TYPE "_clang"
26+
# elif defined(__PGI)
27+
# define PYBIND11_COMPILER_TYPE "_pgi"
28+
# elif defined(__MINGW32__)
29+
# define PYBIND11_COMPILER_TYPE "_mingw"
30+
# elif defined(__CYGWIN__)
31+
# define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
32+
# elif defined(__GNUC__)
33+
# define PYBIND11_COMPILER_TYPE "_gcc"
34+
# else
35+
# define PYBIND11_COMPILER_TYPE "_unknown"
36+
# endif
37+
#endif
38+
39+
/// Also standard libs
40+
#ifndef PYBIND11_STDLIB
41+
# if defined(_LIBCPP_VERSION)
42+
# define PYBIND11_STDLIB "_libcpp"
43+
# elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
44+
# define PYBIND11_STDLIB "_libstdcpp"
45+
# else
46+
# define PYBIND11_STDLIB ""
47+
# endif
48+
#endif
49+
50+
/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility.
51+
#ifndef PYBIND11_BUILD_ABI
52+
# if defined(__GXX_ABI_VERSION)
53+
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
54+
# else
55+
# define PYBIND11_BUILD_ABI ""
56+
# endif
57+
#endif
58+
59+
#ifndef PYBIND11_INTERNALS_KIND
60+
# if defined(WITH_THREAD)
61+
# define PYBIND11_INTERNALS_KIND ""
62+
# else
63+
# define PYBIND11_INTERNALS_KIND "_without_thread"
64+
# endif
65+
#endif
66+
67+
/// See README_smart_holder.rst:
68+
/// Classic / Conservative / Progressive cross-module compatibility
69+
#ifndef PYBIND11_INTERNALS_SH_DEF
70+
# if defined(PYBIND11_USE_SMART_HOLDER_AS_DEFAULT)
71+
# define PYBIND11_INTERNALS_SH_DEF ""
72+
# else
73+
# define PYBIND11_INTERNALS_SH_DEF "_sh_def"
74+
# endif
75+
#endif
76+
77+
/* NOTE - ATTENTION - WARNING - EXTREME CAUTION
78+
Changing this will break compatibility with `PYBIND11_INTERNALS_VERSION 4`
79+
See pybind11/detail/type_map.h for more information.
80+
*/
81+
#define PYBIND11_PLATFORM_ABI_ID_V4 \
82+
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \
83+
PYBIND11_BUILD_TYPE PYBIND11_INTERNALS_SH_DEF
84+
85+
/// LEGACY "ABI-breaking" APPROACH, ORIGINAL COMMENT
86+
/// ------------------------------------------------
87+
/// Tracks the `internals` and `type_info` ABI version independent of the main library version.
88+
///
89+
/// Some portions of the code use an ABI that is conditional depending on this
90+
/// version number. That allows ABI-breaking changes to be "pre-implemented".
91+
/// Once the default version number is incremented, the conditional logic that
92+
/// no longer applies can be removed. Additionally, users that need not
93+
/// maintain ABI compatibility can increase the version number in order to take
94+
/// advantage of any functionality/efficiency improvements that depend on the
95+
/// newer ABI.
96+
///
97+
/// WARNING: If you choose to manually increase the ABI version, note that
98+
/// pybind11 may not be tested as thoroughly with a non-default ABI version, and
99+
/// further ABI-incompatible changes may be made before the ABI is officially
100+
/// changed to the new version.
101+
#ifndef PYBIND11_INTERNALS_VERSION
102+
# define PYBIND11_INTERNALS_VERSION 4
103+
#endif
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// Copyright (c) 2022 The pybind Community.
2+
// All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
#pragma once
6+
7+
#include "common.h"
8+
9+
#if defined(WITH_THREAD) && defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
10+
# include "../gil.h"
11+
#endif
12+
13+
#include "../pytypes.h"
14+
#include "abi_platform_id.h"
15+
16+
#include <string>
17+
18+
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
19+
PYBIND11_NAMESPACE_BEGIN(detail)
20+
21+
inline object get_python_state_dict() {
22+
object state_dict;
23+
#if (PYBIND11_INTERNALS_VERSION <= 4 && PY_VERSION_HEX < 0x030C0000) \
24+
|| PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION)
25+
state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins());
26+
#else
27+
# if PY_VERSION_HEX < 0x03090000
28+
PyInterpreterState *istate = _PyInterpreterState_Get();
29+
# else
30+
PyInterpreterState *istate = PyInterpreterState_Get();
31+
# endif
32+
if (istate) {
33+
state_dict = reinterpret_borrow<object>(PyInterpreterState_GetDict(istate));
34+
}
35+
#endif
36+
if (!state_dict) {
37+
raise_from(PyExc_SystemError, "pybind11::detail::get_python_state_dict() FAILED");
38+
}
39+
return state_dict;
40+
}
41+
42+
#if defined(WITH_THREAD)
43+
# if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
44+
using gil_scoped_acquire_simple = gil_scoped_acquire;
45+
# else
46+
// Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
47+
struct gil_scoped_acquire_simple {
48+
gil_scoped_acquire_simple() : state(PyGILState_Ensure()) {}
49+
gil_scoped_acquire_simple(const gil_scoped_acquire_simple &) = delete;
50+
gil_scoped_acquire_simple &operator=(const gil_scoped_acquire_simple &) = delete;
51+
~gil_scoped_acquire_simple() { PyGILState_Release(state); }
52+
const PyGILState_STATE state;
53+
};
54+
# endif
55+
#endif
56+
57+
/* NOTE: struct cross_extension_shared_state is in
58+
namespace pybind11::detail
59+
but all types using this struct are meant to live in
60+
namespace pybind11::cross_extension_shared_states
61+
to make them easy to discover and reason about.
62+
*/
63+
template <typename AdapterType>
64+
struct cross_extension_shared_state {
65+
static constexpr const char *abi_id() { return AdapterType::abi_id(); }
66+
67+
using payload_type = typename AdapterType::payload_type;
68+
69+
static payload_type **&payload_pp() {
70+
// The reason for the double-indirection is documented here:
71+
// https://github.com/pybind/pybind11/pull/1092
72+
static payload_type **pp;
73+
return pp;
74+
}
75+
76+
static payload_type *get_existing() {
77+
if (payload_pp() && *payload_pp()) {
78+
return *payload_pp();
79+
}
80+
81+
gil_scoped_acquire_simple gil;
82+
error_scope err_scope;
83+
84+
str abi_id_str(AdapterType::abi_id());
85+
dict state_dict = get_python_state_dict();
86+
if (!state_dict.contains(abi_id_str)) {
87+
return nullptr;
88+
}
89+
90+
void *raw_ptr = PyCapsule_GetPointer(state_dict[abi_id_str].ptr(), AdapterType::abi_id());
91+
if (raw_ptr == nullptr) {
92+
raise_from(PyExc_SystemError,
93+
("pybind11::detail::cross_extension_shared_state::get_existing():"
94+
" Retrieve payload_type** from capsule FAILED for ABI ID \""
95+
+ std::string(AdapterType::abi_id()) + "\"")
96+
.c_str());
97+
}
98+
payload_pp() = static_cast<payload_type **>(raw_ptr);
99+
return *payload_pp();
100+
}
101+
102+
static payload_type &get() {
103+
payload_type *existing = get_existing();
104+
if (existing != nullptr) {
105+
return *existing;
106+
}
107+
if (payload_pp() == nullptr) {
108+
payload_pp() = new payload_type *();
109+
}
110+
*payload_pp() = new payload_type();
111+
get_python_state_dict()[AdapterType::abi_id()]
112+
= capsule(payload_pp(), AdapterType::abi_id());
113+
return **payload_pp();
114+
}
115+
116+
struct scoped_clear {
117+
// To be called BEFORE Py_Finalize().
118+
scoped_clear() {
119+
payload_type *existing = get_existing();
120+
if (existing != nullptr) {
121+
AdapterType::payload_clear(*existing);
122+
arm_dtor = true;
123+
}
124+
}
125+
126+
// To be called AFTER Py_Finalize().
127+
~scoped_clear() {
128+
if (arm_dtor) {
129+
delete *payload_pp();
130+
*payload_pp() = nullptr;
131+
}
132+
}
133+
134+
scoped_clear(const scoped_clear &) = delete;
135+
scoped_clear &operator=(const scoped_clear &) = delete;
136+
137+
bool arm_dtor = false;
138+
};
139+
};
140+
141+
PYBIND11_NAMESPACE_END(detail)
142+
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

0 commit comments

Comments
 (0)