15
15
16
16
namespace {
17
17
18
+ template <typename T>
19
+ class immortal {
20
+ alignas (T) std::byte storage[sizeof (T)];
21
+
22
+ public:
23
+ template <typename ... Args>
24
+ immortal (Args&&... args) {
25
+ // Construct new T in storage
26
+ new (&storage) T (std::forward<Args>(args)...);
27
+ }
28
+ ~immortal () {
29
+ // Intentionally don't call destructor
30
+ }
31
+
32
+ T* get () { return reinterpret_cast <T*>(&storage); }
33
+ const T* get () const { return reinterpret_cast <const T*>(&storage); }
34
+ const T* get_const () const { return reinterpret_cast <const T*>(&storage); }
35
+
36
+ const T* operator ->() const { return get (); }
37
+ T* operator ->() { return get (); }
38
+
39
+ T& operator *() { return *get (); }
40
+ const T& operator *() const { return *get (); }
41
+
42
+ };
43
+
18
44
/* * Handle to a python object that automatically DECREFs */
19
45
class py_ref {
20
46
explicit py_ref (PyObject * object): obj_(object) {}
@@ -129,8 +155,8 @@ using global_state_t = std::unordered_map<std::string, global_backends>;
129
155
using local_state_t = std::unordered_map<std::string, local_backends>;
130
156
131
157
static py_ref BackendNotImplementedError;
132
- static global_state_t global_domain_map;
133
- thread_local global_state_t * current_global_state = & global_domain_map;
158
+ static immortal< global_state_t > global_domain_map;
159
+ thread_local global_state_t * current_global_state = global_domain_map.get() ;
134
160
thread_local global_state_t thread_local_domain_map;
135
161
thread_local local_state_t local_domain_map;
136
162
@@ -140,30 +166,30 @@ Using these with PyObject_GetAttr is faster than PyObject_GetAttrString which
140
166
has to create a new python string internally.
141
167
*/
142
168
struct {
143
- py_ref ua_convert;
144
- py_ref ua_domain;
145
- py_ref ua_function;
169
+ immortal< py_ref> ua_convert;
170
+ immortal< py_ref> ua_domain;
171
+ immortal< py_ref> ua_function;
146
172
147
173
bool init () {
148
- ua_convert = py_ref::steal (PyUnicode_InternFromString (" __ua_convert__" ));
149
- if (!ua_convert)
174
+ * ua_convert = py_ref::steal (PyUnicode_InternFromString (" __ua_convert__" ));
175
+ if (!* ua_convert)
150
176
return false ;
151
177
152
- ua_domain = py_ref::steal (PyUnicode_InternFromString (" __ua_domain__" ));
153
- if (!ua_domain)
178
+ * ua_domain = py_ref::steal (PyUnicode_InternFromString (" __ua_domain__" ));
179
+ if (!* ua_domain)
154
180
return false ;
155
181
156
- ua_function = py_ref::steal (PyUnicode_InternFromString (" __ua_function__" ));
157
- if (!ua_function)
182
+ * ua_function = py_ref::steal (PyUnicode_InternFromString (" __ua_function__" ));
183
+ if (!* ua_function)
158
184
return false ;
159
185
160
186
return true ;
161
187
}
162
188
163
189
void clear () {
164
- ua_convert. reset ();
165
- ua_domain. reset ();
166
- ua_function. reset ();
190
+ ua_convert-> reset ();
191
+ ua_domain-> reset ();
192
+ ua_function-> reset ();
167
193
}
168
194
} identifiers;
169
195
@@ -202,7 +228,7 @@ std::string domain_to_string(PyObject * domain) {
202
228
203
229
Py_ssize_t backend_get_num_domains (PyObject * backend) {
204
230
auto domain =
205
- py_ref::steal (PyObject_GetAttr (backend, identifiers.ua_domain . get ()));
231
+ py_ref::steal (PyObject_GetAttr (backend, identifiers.ua_domain -> get ()));
206
232
if (!domain)
207
233
return -1 ;
208
234
@@ -225,7 +251,7 @@ enum class LoopReturn { Continue, Break, Error };
225
251
template <typename Func>
226
252
LoopReturn backend_for_each_domain (PyObject * backend, Func f) {
227
253
auto domain =
228
- py_ref::steal (PyObject_GetAttr (backend, identifiers.ua_domain . get ()));
254
+ py_ref::steal (PyObject_GetAttr (backend, identifiers.ua_domain -> get ()));
229
255
if (!domain)
230
256
return LoopReturn::Error;
231
257
@@ -537,7 +563,7 @@ struct BackendState {
537
563
538
564
/* * Clean up global python references when the module is finalized. */
539
565
void globals_free (void * /* self */ ) {
540
- global_domain_map. clear ();
566
+ global_domain_map-> clear ();
541
567
BackendNotImplementedError.reset ();
542
568
identifiers.clear ();
543
569
}
@@ -550,7 +576,7 @@ void globals_free(void * /* self */) {
550
576
* cleanup.
551
577
*/
552
578
int globals_traverse (PyObject * self, visitproc visit, void * arg) {
553
- for (const auto & kv : global_domain_map) {
579
+ for (const auto & kv : * global_domain_map) {
554
580
const auto & globals = kv.second ;
555
581
PyObject * backend = globals.global .backend .get ();
556
582
Py_VISIT (backend);
@@ -563,7 +589,7 @@ int globals_traverse(PyObject * self, visitproc visit, void * arg) {
563
589
}
564
590
565
591
int globals_clear (PyObject * /* self */ ) {
566
- global_domain_map. clear ();
592
+ global_domain_map-> clear ();
567
593
return 0 ;
568
594
}
569
595
@@ -1170,7 +1196,7 @@ py_ref Function::canonicalize_kwargs(PyObject * kwargs) {
1170
1196
1171
1197
py_func_args Function::replace_dispatchables (
1172
1198
PyObject * backend, PyObject * args, PyObject * kwargs, PyObject * coerce) {
1173
- auto has_ua_convert = PyObject_HasAttr (backend, identifiers.ua_convert . get ());
1199
+ auto has_ua_convert = PyObject_HasAttr (backend, identifiers.ua_convert -> get ());
1174
1200
if (!has_ua_convert) {
1175
1201
return {py_ref::ref (args), py_ref::ref (kwargs)};
1176
1202
}
@@ -1182,7 +1208,7 @@ py_func_args Function::replace_dispatchables(
1182
1208
1183
1209
PyObject * convert_args[] = {backend, dispatchables.get (), coerce};
1184
1210
auto res = py_ref::steal (Q_PyObject_VectorcallMethod (
1185
- identifiers.ua_convert . get (), convert_args,
1211
+ identifiers.ua_convert -> get (), convert_args,
1186
1212
array_size (convert_args) | Q_PY_VECTORCALL_ARGUMENTS_OFFSET, nullptr ));
1187
1213
if (!res) {
1188
1214
return {};
@@ -1287,7 +1313,7 @@ PyObject * Function::call(PyObject * args_, PyObject * kwargs_) {
1287
1313
backend, reinterpret_cast <PyObject *>(this ), new_args.args .get (),
1288
1314
new_args.kwargs .get ()};
1289
1315
result = py_ref::steal (Q_PyObject_VectorcallMethod (
1290
- identifiers.ua_function . get (), args,
1316
+ identifiers.ua_function -> get (), args,
1291
1317
array_size (args) | Q_PY_VECTORCALL_ARGUMENTS_OFFSET, nullptr ));
1292
1318
1293
1319
// raise BackendNotImplemeted is equivalent to return NotImplemented
@@ -1499,7 +1525,7 @@ PyObject * get_state(PyObject * /* self */, PyObject * /* args */) {
1499
1525
1500
1526
output->locals = local_domain_map;
1501
1527
output->use_thread_local_globals =
1502
- (current_global_state != & global_domain_map);
1528
+ (current_global_state != global_domain_map. get () );
1503
1529
output->globals = *current_global_state;
1504
1530
1505
1531
return ref.release ();
@@ -1523,7 +1549,7 @@ PyObject * set_state(PyObject * /* self */, PyObject * args) {
1523
1549
bool use_thread_local_globals =
1524
1550
(!reset_allowed) || state->use_thread_local_globals ;
1525
1551
current_global_state =
1526
- use_thread_local_globals ? &thread_local_domain_map : & global_domain_map;
1552
+ use_thread_local_globals ? &thread_local_domain_map : global_domain_map. get () ;
1527
1553
1528
1554
if (use_thread_local_globals)
1529
1555
thread_local_domain_map = state->globals ;
@@ -1554,7 +1580,7 @@ PyObject * determine_backend(PyObject * /*self*/, PyObject * args) {
1554
1580
auto result = for_each_backend_in_domain (
1555
1581
domain, [&](PyObject * backend, bool coerce_backend) {
1556
1582
auto has_ua_convert =
1557
- PyObject_HasAttr (backend, identifiers.ua_convert . get ());
1583
+ PyObject_HasAttr (backend, identifiers.ua_convert -> get ());
1558
1584
1559
1585
if (!has_ua_convert) {
1560
1586
// If no __ua_convert__, assume it won't accept the type
@@ -1566,7 +1592,7 @@ PyObject * determine_backend(PyObject * /*self*/, PyObject * args) {
1566
1592
(coerce && coerce_backend) ? Py_True : Py_False};
1567
1593
1568
1594
auto res = py_ref::steal (Q_PyObject_VectorcallMethod (
1569
- identifiers.ua_convert . get (), convert_args,
1595
+ identifiers.ua_convert -> get (), convert_args,
1570
1596
array_size (convert_args) | Q_PY_VECTORCALL_ARGUMENTS_OFFSET,
1571
1597
nullptr ));
1572
1598
if (!res) {
0 commit comments