File: regina.patch

package info (click to toggle)
regina-normal 7.4.1-1.1
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 154,244 kB
  • sloc: cpp: 295,026; xml: 9,992; sh: 1,344; python: 1,225; perl: 616; ansic: 138; makefile: 26
file content (206 lines) | stat: -rw-r--r-- 9,510 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
diff --git a/python/pybind11_v2/pybind11/functional.h b/python/pybind11_v2/pybind11/functional.h
index 4b3610117..64e6a7acf 100644
--- a/python/pybind11_v2/pybind11/functional.h
+++ b/python/pybind11_v2/pybind11/functional.h
@@ -31,12 +31,18 @@ struct func_handle {
     }
     func_handle(const func_handle &f_) { operator=(f_); }
     func_handle &operator=(const func_handle &f_) {
-        gil_scoped_acquire acq;
+        // Called on the way into a Regina function that expects
+        // a std::function argument.  In such situations the
+        // interpreter should already be holding the GIL.
+        // gil_scoped_acquire acq;
         f = f_.f;
         return *this;
     }
     ~func_handle() {
-        gil_scoped_acquire acq;
+        // Called on the way into and also out of a Regina function
+        // that expects a std::function argument.  In such situations
+        // the interpreter should already be holding the GIL.
+        // gil_scoped_acquire acq;
         function kill_f(std::move(f));
     }
 };
@@ -51,7 +57,10 @@ template <typename Return, typename... Args>
 struct func_wrapper : func_wrapper_base {
     using func_wrapper_base::func_wrapper_base;
     Return operator()(Args... args) const {
-        gil_scoped_acquire acq;
+        // Called when a std::function is executed as a callback within
+        // one of Regina's own functions.  In such situations Regina's
+        // Python bindings are responsible for ensuring the GIL is held.
+        // gil_scoped_acquire acq;
         // casts the returned object as a rvalue to the return type
         return hfunc.f(std::forward<Args>(args)...).template cast<Return>();
     }
diff --git a/python/pybind11_v2/pybind11/gil.h b/python/pybind11_v2/pybind11/gil.h
index 6b0edaee4..d5bb7d7b0 100644
--- a/python/pybind11_v2/pybind11/gil.h
+++ b/python/pybind11_v2/pybind11/gil.h
@@ -216,4 +216,27 @@ public:
 
 #endif // PYBIND11_SIMPLE_GIL_MANAGEMENT
 
+/**
+ * A less lightweight version of gil_scoped_acquire that actually checks
+ * whether we are already holding the GIL before trying to acquire it.
+ *
+ * This is needed in scenarios with multiple subinterpreters, where pybind11
+ * could otherwise causes deadlocks because its TLS mechanism implicitly (and
+ * incorrectly) assumes that different subinterpreters must be running
+ * from different native OS threads.
+ *
+ * - Ben Burton, 30/09/2022.
+ */
+class safe_gil_scoped_acquire {
+    std::optional<gil_scoped_acquire> gil;
+public:
+    safe_gil_scoped_acquire() {
+        if (! PyGILState_Check())
+            gil.emplace();
+    }
+    safe_gil_scoped_acquire(const safe_gil_scoped_acquire&) = delete;
+    safe_gil_scoped_acquire& operator = (const safe_gil_scoped_acquire&) =
+        delete;
+};
+
 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
diff --git a/python/pybind11_v2/pybind11/iostream.h b/python/pybind11_v2/pybind11/iostream.h
index 1878089e3..60586b9d7 100644
--- a/python/pybind11_v2/pybind11/iostream.h
+++ b/python/pybind11_v2/pybind11/iostream.h
@@ -93,7 +93,7 @@ private:
     // This function must be non-virtual to be called in a destructor.
     int _sync() {
         if (pbase() != pptr()) { // If buffer is not empty
-            gil_scoped_acquire tmp;
+            safe_gil_scoped_acquire tmp;
             // This subtraction cannot be negative, so dropping the sign.
             auto size = static_cast<size_t>(pptr() - pbase());
             size_t remainder = utf8_remainder();
diff --git a/python/pybind11_v2/pybind11/pybind11.h b/python/pybind11_v2/pybind11/pybind11.h
index 5219c0ff8..101de3b20 100644
--- a/python/pybind11_v2/pybind11/pybind11.h
+++ b/python/pybind11_v2/pybind11/pybind11.h
@@ -26,6 +26,10 @@
 #include <utility>
 #include <vector>
 
+// This allows us to verify that we are #including our own patched pybind11,
+// not some other pybind11 that has been installed system-wide.
+#define REGINA_PYBIND11 1
+
 #if defined(__cpp_lib_launder) && !(defined(_MSC_VER) && (_MSC_VER < 1914))
 #    define PYBIND11_STD_LAUNDER std::launder
 #    define PYBIND11_HAS_STD_LAUNDER 1
@@ -37,6 +41,10 @@
 #    include <cxxabi.h>
 #endif
 
+namespace regina {
+    const char* pythonTypename(const std::type_info*);
+}
+
 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
 
 /* https://stackoverflow.com/questions/46798456/handling-gccs-noexcept-type-warning
@@ -363,6 +371,16 @@ protected:
         std::vector<char *> strings;
     };
 
+    /* Regina-specific tweak to generation of function signatures */
+    inline std::string fix_regina(const std::string& modulename, const std::string& qualname) {
+        if (modulename == "regina.engine")
+            return "regina." + qualname;
+        else if (modulename == "regina.engine.internal")
+            return "regina.internal." + qualname;
+        else
+            return modulename + "." + qualname;
+    }
+
     /// Register a function call with Python (generic non-templated code goes here)
     void initialize_generic(unique_function_record &&unique_rec,
                             const char *text,
@@ -464,15 +482,33 @@ protected:
                 if (!t) {
                     pybind11_fail("Internal error while parsing type signature (1)");
                 }
-                if (auto *tinfo = detail::get_type_info(*t)) {
+                if (const char* name = regina::pythonTypename(t)) {
+                    signature += name;
+                } else if (auto *tinfo = detail::get_type_info(*t)) {
                     handle th((PyObject *) tinfo->type);
-                    signature += th.attr("__module__").cast<std::string>() + "."
-                                 + th.attr("__qualname__").cast<std::string>();
+                    std::string qualname;
+                    try {
+                        qualname = th.attr("__qualname__").cast<std::string>();
+                    } catch (...) {
+                        // In Python 3.12 we get an error in our stripped-down
+                        // interpreter since PythonOutputStream is missing both
+                        // __qualname__ and __name__.  Do not make this fatal.
+                        qualname = "<unknown>";
+                    }
+                    const auto m = th.attr("__module__").cast<std::string>();
+                    signature += fix_regina(m, qualname);
                 } else if (rec->is_new_style_constructor && arg_index == 0) {
                     // A new-style `__init__` takes `self` as `value_and_holder`.
                     // Rewrite it to the proper class type.
-                    signature += rec->scope.attr("__module__").cast<std::string>() + "."
-                                 + rec->scope.attr("__qualname__").cast<std::string>();
+                    std::string qualname;
+                    try {
+                        qualname = rec->scope.attr("__qualname__").cast<std::string>();
+                    } catch (...) {
+                        // See above for the explanation here.
+                        qualname = "<unknown>";
+                    }
+                    const auto m = rec->scope.attr("__module__").cast<std::string>();
+                    signature += fix_regina(m, qualname);
                 } else {
                     signature += detail::quote_cpp_type_name(detail::clean_type_id(t->name()));
                 }
@@ -1021,6 +1057,16 @@ protected:
         } catch (abi::__forced_unwind &) {
             throw;
 #endif
+        } catch (const pybind11::stop_iteration& stop) {
+            /* We prioritise catching stop_iteration before any of the other
+               exception logic below, since stop_iteration is arguably the one
+               setting where exceptions are normal as opposed to "exceptional".
+               The default exception logic involves many try/catch/rethrow
+               blocks, and in settings where try/catch is slow (e.g., running
+               within SageMath on macOS), this can add to a noticeable
+               performance penalty when using iterators. */
+            PyErr_SetString(PyExc_StopIteration, stop.what());
+            return nullptr;
         } catch (...) {
             try_translate_exceptions();
             return nullptr;
@@ -2726,13 +2772,13 @@ void print(Args &&...args) {
 
 inline void
 error_already_set::m_fetched_error_deleter(detail::error_fetch_and_normalize *raw_ptr) {
-    gil_scoped_acquire gil;
+    safe_gil_scoped_acquire gil;
     error_scope scope;
     delete raw_ptr;
 }
 
 inline const char *error_already_set::what() const noexcept {
-    gil_scoped_acquire gil;
+    safe_gil_scoped_acquire gil;
     error_scope scope;
     return m_fetched_error->error_string().c_str();
 }
@@ -2862,7 +2908,7 @@ function get_override(const T *this_ptr, const char *name) {
 
 #define PYBIND11_OVERRIDE_IMPL(ret_type, cname, name, ...)                                        \
     do {                                                                                          \
-        pybind11::gil_scoped_acquire gil;                                                         \
+        pybind11::safe_gil_scoped_acquire gil;                                                    \
         pybind11::function override                                                               \
             = pybind11::get_override(static_cast<const cname *>(this), name);                     \
         if (override) {                                                                           \