Skip to content

Commit f5ec1cc

Browse files
committed
Clean up the function_record better, in case of exceptions
1 parent 453783d commit f5ec1cc

File tree

1 file changed

+20
-10
lines changed

1 file changed

+20
-10
lines changed

include/pybind11/pybind11.h

+20-10
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,13 @@ class cpp_function : public function {
114114
object name() const { return attr("__name__"); }
115115

116116
protected:
117+
using unique_function_record = std::unique_ptr<detail::function_record, void (*)(detail::function_record *)>;
118+
117119
/// Space optimization: don't inline this frequently instantiated fragment
118-
PYBIND11_NOINLINE std::unique_ptr<detail::function_record> make_function_record() {
119-
return std::unique_ptr<detail::function_record>(new detail::function_record());
120+
PYBIND11_NOINLINE unique_function_record make_function_record() {
121+
// `destruct<true>(function_record)`: `initialize_generic` copies strings and
122+
// takes care of cleaning up in case of exceptions.
123+
return unique_function_record(new detail::function_record(), destruct<false>);
120124
}
121125

122126
/// Special internal constructor for functors, lambda functions, etc.
@@ -247,7 +251,7 @@ class cpp_function : public function {
247251
};
248252

249253
/// Register a function call with Python (generic non-templated code goes here)
250-
void initialize_generic(std::unique_ptr<detail::function_record> &&unique_rec, const char *text,
254+
void initialize_generic(unique_function_record &&unique_rec, const char *text,
251255
const std::type_info *const *types, size_t args) {
252256
auto rec = unique_rec.get();
253257

@@ -486,6 +490,7 @@ class cpp_function : public function {
486490
}
487491

488492
/// When a cpp_function is GCed, release any memory allocated by pybind11
493+
template <bool DeleteStrings = true>
489494
static void destruct(detail::function_record *rec) {
490495
// If on Python 3.9, check the interpreter "MICRO" (patch) version.
491496
// If this is running on 3.9.0, we have to work around a bug.
@@ -497,14 +502,19 @@ class cpp_function : public function {
497502
detail::function_record *next = rec->next;
498503
if (rec->free_data)
499504
rec->free_data(rec);
500-
std::free((char *) rec->name);
501-
std::free((char *) rec->doc);
502-
std::free((char *) rec->signature);
503-
for (auto &arg: rec->args) {
504-
std::free(const_cast<char *>(arg.name));
505-
std::free(const_cast<char *>(arg.descr));
506-
arg.value.dec_ref();
505+
// During initialization, these strings might not have been copied yet,
506+
// so they cannot be freed. Once the function has been created, they can.
507+
if (DeleteStrings) {
508+
std::free((char *) rec->name);
509+
std::free((char *) rec->doc);
510+
std::free((char *) rec->signature);
511+
for (auto &arg: rec->args) {
512+
std::free(const_cast<char *>(arg.name));
513+
std::free(const_cast<char *>(arg.descr));
514+
}
507515
}
516+
for (auto &arg: rec->args)
517+
arg.value.dec_ref();
508518
if (rec->def) {
509519
std::free(const_cast<char *>(rec->def->ml_doc));
510520
// Python 3.9.0 decref's these in the wrong order; rec->def

0 commit comments

Comments
 (0)