Skip to content

Commit 4f61912

Browse files
authored
py::smart_holder std::shared_ptr deleter simplification & optimization. (#3041)
1 parent 8f6ca71 commit 4f61912

File tree

1 file changed

+57
-52
lines changed

1 file changed

+57
-52
lines changed

include/pybind11/detail/smart_holder_poc.h

+57-52
Original file line numberDiff line numberDiff line change
@@ -56,36 +56,43 @@ High-level aspects:
5656
namespace pybindit {
5757
namespace memory {
5858

59-
template <typename T>
60-
struct guarded_builtin_delete {
61-
std::shared_ptr<bool> flag_ptr;
62-
explicit guarded_builtin_delete(std::shared_ptr<bool> armed_flag_ptr)
63-
: flag_ptr{armed_flag_ptr} {}
64-
template <typename T_ = T,
65-
typename std::enable_if<std::is_destructible<T_>::value, int>::type = 0>
66-
void operator()(T *raw_ptr) {
67-
if (*flag_ptr)
68-
delete raw_ptr;
69-
}
70-
template <typename T_ = T,
71-
typename std::enable_if<!std::is_destructible<T_>::value, int>::type = 0>
72-
void operator()(T *) {
73-
// This noop operator is needed to avoid a compilation error (for `delete raw_ptr;`), but
74-
// throwing an exception from here could std::terminate the process. Therefore the runtime
75-
// check for lifetime-management correctness is implemented elsewhere (in
76-
// ensure_pointee_is_destructible()).
59+
struct guarded_delete {
60+
void (*del_ptr)(void *);
61+
bool armed_flag;
62+
guarded_delete(void (*del_ptr)(void *), bool armed_flag)
63+
: del_ptr{del_ptr}, armed_flag{armed_flag} {}
64+
void operator()(void *raw_ptr) const {
65+
if (armed_flag)
66+
(*del_ptr)(raw_ptr);
7767
}
7868
};
7969

70+
template <typename T, typename std::enable_if<std::is_destructible<T>::value, int>::type = 0>
71+
inline void builtin_delete_if_destructible(void *raw_ptr) {
72+
delete (T *) raw_ptr;
73+
}
74+
75+
template <typename T, typename std::enable_if<!std::is_destructible<T>::value, int>::type = 0>
76+
inline void builtin_delete_if_destructible(void *) {
77+
// This noop operator is needed to avoid a compilation error (for `delete raw_ptr;`), but
78+
// throwing an exception from a destructor will std::terminate the process. Therefore the
79+
// runtime check for lifetime-management correctness is implemented elsewhere (in
80+
// ensure_pointee_is_destructible()).
81+
}
82+
83+
template <typename T>
84+
guarded_delete make_guarded_builtin_delete(bool armed_flag) {
85+
return guarded_delete(builtin_delete_if_destructible<T>, armed_flag);
86+
}
87+
8088
template <typename T, typename D>
81-
struct guarded_custom_deleter {
82-
std::shared_ptr<bool> flag_ptr;
83-
explicit guarded_custom_deleter(std::shared_ptr<bool> armed_flag_ptr)
84-
: flag_ptr{armed_flag_ptr} {}
85-
void operator()(T *raw_ptr) {
86-
if (*flag_ptr)
87-
D()(raw_ptr);
88-
}
89+
inline void custom_delete(void *raw_ptr) {
90+
D()((T *) raw_ptr);
91+
}
92+
93+
template <typename T, typename D>
94+
guarded_delete make_guarded_custom_deleter(bool armed_flag) {
95+
return guarded_delete(custom_delete<T, D>, armed_flag);
8996
};
9097

9198
template <typename T>
@@ -96,7 +103,6 @@ inline bool is_std_default_delete(const std::type_info &rtti_deleter) {
96103

97104
struct smart_holder {
98105
const std::type_info *rtti_uqp_del = nullptr;
99-
std::shared_ptr<bool> vptr_deleter_armed_flag_ptr;
100106
std::shared_ptr<void> vptr;
101107
bool vptr_is_using_noop_deleter : 1;
102108
bool vptr_is_using_builtin_delete : 1;
@@ -108,20 +114,14 @@ struct smart_holder {
108114
// Design choice: smart_holder is movable but not copyable.
109115
smart_holder(smart_holder &&) = default;
110116
smart_holder(const smart_holder &) = delete;
111-
smart_holder &operator=(smart_holder &&) = default;
117+
smart_holder &operator=(smart_holder &&) = delete;
112118
smart_holder &operator=(const smart_holder &) = delete;
113119

114120
smart_holder()
115121
: vptr_is_using_noop_deleter{false}, vptr_is_using_builtin_delete{false},
116122
vptr_is_external_shared_ptr{false}, is_populated{false}, is_disowned{false},
117123
pointee_depends_on_holder_owner{false} {}
118124

119-
explicit smart_holder(bool vptr_deleter_armed_flag)
120-
: vptr_deleter_armed_flag_ptr{new bool{vptr_deleter_armed_flag}},
121-
vptr_is_using_noop_deleter{false}, vptr_is_using_builtin_delete{false},
122-
vptr_is_external_shared_ptr{false}, is_populated{false}, is_disowned{false},
123-
pointee_depends_on_holder_owner{false} {}
124-
125125
bool has_pointee() const { return vptr.get() != nullptr; }
126126

127127
template <typename T>
@@ -194,10 +194,20 @@ struct smart_holder {
194194
}
195195
}
196196

197+
void reset_vptr_deleter_armed_flag(bool armed_flag) {
198+
// The const_cast is only for certain compilers (Ubuntu 20 GCC 6.3.0 being one).
199+
auto vptr_del_ptr = const_cast<guarded_delete *>(std::get_deleter<guarded_delete>(vptr));
200+
if (vptr_del_ptr == nullptr) {
201+
throw std::runtime_error(
202+
"smart_holder::reset_vptr_deleter_armed_flag() called in an invalid context.");
203+
}
204+
vptr_del_ptr->armed_flag = armed_flag;
205+
}
206+
197207
template <typename T>
198208
static smart_holder from_raw_ptr_unowned(T *raw_ptr) {
199-
smart_holder hld(false);
200-
hld.vptr.reset(raw_ptr, guarded_builtin_delete<T>(hld.vptr_deleter_armed_flag_ptr));
209+
smart_holder hld;
210+
hld.vptr.reset(raw_ptr, [](void *) {});
201211
hld.vptr_is_using_noop_deleter = true;
202212
hld.is_populated = true;
203213
return hld;
@@ -227,30 +237,27 @@ struct smart_holder {
227237
template <typename T>
228238
static smart_holder from_raw_ptr_take_ownership(T *raw_ptr) {
229239
ensure_pointee_is_destructible<T>("from_raw_ptr_take_ownership");
230-
smart_holder hld(true);
231-
hld.vptr.reset(raw_ptr, guarded_builtin_delete<T>(hld.vptr_deleter_armed_flag_ptr));
240+
smart_holder hld;
241+
hld.vptr.reset(raw_ptr, make_guarded_builtin_delete<T>(true));
232242
hld.vptr_is_using_builtin_delete = true;
233243
hld.is_populated = true;
234244
return hld;
235245
}
236246

237247
// Caller is responsible for ensuring preconditions (SMART_HOLDER_WIP: details).
238248
void disown() {
239-
*vptr_deleter_armed_flag_ptr = false;
240-
is_disowned = true;
249+
reset_vptr_deleter_armed_flag(false);
250+
is_disowned = true;
241251
}
242252

243253
// Caller is responsible for ensuring preconditions (SMART_HOLDER_WIP: details).
244254
void reclaim_disowned() {
245-
*vptr_deleter_armed_flag_ptr = true;
246-
is_disowned = false;
255+
reset_vptr_deleter_armed_flag(true);
256+
is_disowned = false;
247257
}
248258

249259
// Caller is responsible for ensuring preconditions (SMART_HOLDER_WIP: details).
250-
void release_disowned() {
251-
vptr.reset();
252-
vptr_deleter_armed_flag_ptr.reset();
253-
}
260+
void release_disowned() { vptr.reset(); }
254261

255262
// SMART_HOLDER_WIP: review this function.
256263
void ensure_can_release_ownership(const char *context = "ensure_can_release_ownership") {
@@ -261,7 +268,7 @@ struct smart_holder {
261268

262269
// Caller is responsible for ensuring preconditions (SMART_HOLDER_WIP: details).
263270
void release_ownership() {
264-
*vptr_deleter_armed_flag_ptr = false;
271+
reset_vptr_deleter_armed_flag(false);
265272
release_disowned();
266273
}
267274

@@ -275,15 +282,13 @@ struct smart_holder {
275282

276283
template <typename T, typename D>
277284
static smart_holder from_unique_ptr(std::unique_ptr<T, D> &&unq_ptr) {
278-
smart_holder hld(true);
285+
smart_holder hld;
279286
hld.rtti_uqp_del = &typeid(D);
280287
hld.vptr_is_using_builtin_delete = is_std_default_delete<T>(*hld.rtti_uqp_del);
281288
if (hld.vptr_is_using_builtin_delete) {
282-
hld.vptr.reset(unq_ptr.get(),
283-
guarded_builtin_delete<T>(hld.vptr_deleter_armed_flag_ptr));
289+
hld.vptr.reset(unq_ptr.get(), make_guarded_builtin_delete<T>(true));
284290
} else {
285-
hld.vptr.reset(unq_ptr.get(),
286-
guarded_custom_deleter<T, D>(hld.vptr_deleter_armed_flag_ptr));
291+
hld.vptr.reset(unq_ptr.get(), make_guarded_custom_deleter<T, D>(true));
287292
}
288293
unq_ptr.release();
289294
hld.is_populated = true;

0 commit comments

Comments
 (0)