Adding reclaim_disowned logic & miscellaneous naming and documentation improvements. (#2943)

* Using new smart_holder::reclaim_disowned in smart_holder_type_caster for unique_ptr.

* Systematically renaming was_disowned to is_disowned (because disowning is now reversible: reclaim_disowned).

* Systematically renaming virtual_overrider_self_life_support to trampoline_self_life_support (to reuse existing terminology instead of introducing new one).

* Systematically renaming test_class_sh_with_alias to test_class_sh_trampoline_basic.

* Adding a Trampolines and std::unique_ptr section to README_smart_holder.rst.

* MSVC compatibility.
This commit is contained in:
Ralf W. Grosse-Kunstleve
2021-04-09 23:08:44 -07:00
committed by GitHub
parent 88a09988e7
commit 6c922614ed
16 changed files with 201 additions and 108 deletions

View File

@@ -102,7 +102,7 @@ struct smart_holder {
bool vptr_is_using_builtin_delete : 1;
bool vptr_is_external_shared_ptr : 1;
bool is_populated : 1;
bool was_disowned : 1;
bool is_disowned : 1;
bool pointee_depends_on_holder_owner : 1; // SMART_HOLDER_WIP: See PR #2839.
// Design choice: smart_holder is movable but not copyable.
@@ -113,13 +113,13 @@ struct smart_holder {
smart_holder()
: vptr_is_using_noop_deleter{false}, vptr_is_using_builtin_delete{false},
vptr_is_external_shared_ptr{false}, is_populated{false}, was_disowned{false},
vptr_is_external_shared_ptr{false}, is_populated{false}, is_disowned{false},
pointee_depends_on_holder_owner{false} {}
explicit smart_holder(bool vptr_deleter_armed_flag)
: vptr_deleter_armed_flag_ptr{new bool{vptr_deleter_armed_flag}},
vptr_is_using_noop_deleter{false}, vptr_is_using_builtin_delete{false},
vptr_is_external_shared_ptr{false}, is_populated{false}, was_disowned{false},
vptr_is_external_shared_ptr{false}, is_populated{false}, is_disowned{false},
pointee_depends_on_holder_owner{false} {}
bool has_pointee() const { return vptr.get() != nullptr; }
@@ -136,8 +136,8 @@ struct smart_holder {
throw std::runtime_error(std::string("Unpopulated holder (") + context + ").");
}
}
void ensure_was_not_disowned(const char *context) const {
if (was_disowned) {
void ensure_is_not_disowned(const char *context) const {
if (is_disowned) {
throw std::runtime_error(std::string("Holder was disowned already (") + context
+ ").");
}
@@ -237,7 +237,13 @@ struct smart_holder {
// Caller is responsible for ensuring preconditions (SMART_HOLDER_WIP: details).
void disown() {
*vptr_deleter_armed_flag_ptr = false;
was_disowned = true;
is_disowned = true;
}
// Caller is responsible for ensuring preconditions (SMART_HOLDER_WIP: details).
void reclaim_disowned() {
*vptr_deleter_armed_flag_ptr = true;
is_disowned = false;
}
// Caller is responsible for ensuring preconditions (SMART_HOLDER_WIP: details).
@@ -248,7 +254,7 @@ struct smart_holder {
// SMART_HOLDER_WIP: review this function.
void ensure_can_release_ownership(const char *context = "ensure_can_release_ownership") {
ensure_was_not_disowned(context);
ensure_is_not_disowned(context);
ensure_vptr_is_using_builtin_delete(context);
ensure_use_count_1(context);
}

View File

@@ -6,7 +6,7 @@
#include "../gil.h"
#include "../pytypes.h"
#include "../virtual_overrider_self_life_support.h"
#include "../trampoline_self_life_support.h"
#include "common.h"
#include "descr.h"
#include "dynamic_raw_ptr_cast_if_possible.h"
@@ -353,7 +353,7 @@ struct smart_holder_type_caster_load {
if (!have_holder())
return nullptr;
throw_if_uninitialized_or_disowned_holder();
holder().ensure_was_not_disowned("loaded_as_shared_ptr");
holder().ensure_is_not_disowned("loaded_as_shared_ptr");
auto void_raw_ptr = holder().template as_raw_ptr_unowned<void>();
auto type_raw_ptr = convert_type(void_raw_ptr);
if (holder().pointee_depends_on_holder_owner) {
@@ -375,7 +375,7 @@ struct smart_holder_type_caster_load {
if (!have_holder())
return nullptr;
throw_if_uninitialized_or_disowned_holder();
holder().ensure_was_not_disowned(context);
holder().ensure_is_not_disowned(context);
holder().template ensure_compatible_rtti_uqp_del<T, D>(context);
holder().ensure_use_count_1(context);
auto raw_void_ptr = holder().template as_raw_ptr_unowned<void>();
@@ -391,11 +391,11 @@ struct smart_holder_type_caster_load {
T *raw_type_ptr = convert_type(raw_void_ptr);
auto *self_life_support
= dynamic_raw_ptr_cast_if_possible<virtual_overrider_self_life_support>(raw_type_ptr);
= dynamic_raw_ptr_cast_if_possible<trampoline_self_life_support>(raw_type_ptr);
if (self_life_support == nullptr && holder().pointee_depends_on_holder_owner) {
throw value_error("Alias class (also known as trampoline) does not inherit from "
"py::virtual_overrider_self_life_support, therefore the "
"ownership of this instance cannot safely be transferred to C++.");
"py::trampoline_self_life_support, therefore the ownership of this "
"instance cannot safely be transferred to C++.");
}
// Critical transfer-of-ownership section. This must stay together.
@@ -406,8 +406,7 @@ struct smart_holder_type_caster_load {
}
auto result = std::unique_ptr<T, D>(raw_type_ptr);
if (self_life_support != nullptr) {
Py_INCREF((PyObject *) load_impl.loaded_v_h.inst);
self_life_support->loaded_v_h = load_impl.loaded_v_h;
self_life_support->activate_life_support(load_impl.loaded_v_h);
} else {
load_impl.loaded_v_h.value_ptr() = nullptr;
deregister_instance(
@@ -700,8 +699,27 @@ struct smart_holder_type_caster<std::unique_ptr<T, D>> : smart_holder_type_caste
void *src_raw_void_ptr = static_cast<void *>(src_raw_ptr);
const detail::type_info *tinfo = st.second;
if (find_registered_python_instance(src_raw_void_ptr, tinfo))
if (handle existing_inst = find_registered_python_instance(src_raw_void_ptr, tinfo)) {
auto *self_life_support
= dynamic_raw_ptr_cast_if_possible<trampoline_self_life_support>(src_raw_ptr);
if (self_life_support != nullptr) {
value_and_holder &v_h = self_life_support->v_h;
if (v_h.inst != nullptr && v_h.vh != nullptr) {
auto &holder = v_h.holder<pybindit::memory::smart_holder>();
if (!holder.is_disowned) {
pybind11_fail("smart_holder_type_casters: unexpected "
"smart_holder.is_disowned failure.");
}
// Critical transfer-of-ownership section. This must stay together.
self_life_support->deactivate_life_support();
holder.reclaim_disowned();
src.release();
// Critical section end.
return existing_inst;
}
}
throw cast_error("Invalid unique_ptr: another instance owns this pointer already.");
}
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type));
auto *inst_raw_ptr = reinterpret_cast<instance *>(inst.ptr());

View File

@@ -0,0 +1,57 @@
// Copyright (c) 2021 The Pybind Development Team.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#pragma once
#include "detail/common.h"
#include "detail/smart_holder_poc.h"
#include "detail/type_caster_base.h"
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
// SMART_HOLDER_WIP: Needs refactoring of existing pybind11 code.
inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo);
PYBIND11_NAMESPACE_END(detail)
// The original core idea for this struct goes back to PyCLIF:
// https://github.com/google/clif/blob/07f95d7e69dca2fcf7022978a55ef3acff506c19/clif/python/runtime.cc#L37
// URL provided here mainly to give proper credit. To fully explain the `HoldPyObj` feature, more
// context is needed (SMART_HOLDER_WIP).
struct trampoline_self_life_support {
detail::value_and_holder v_h;
void activate_life_support(const detail::value_and_holder &v_h_) {
Py_INCREF((PyObject *) v_h_.inst);
v_h = v_h_;
}
void deactivate_life_support() {
Py_DECREF((PyObject *) v_h.inst);
v_h = detail::value_and_holder();
}
~trampoline_self_life_support() {
if (v_h.inst != nullptr && v_h.vh != nullptr) {
void *value_void_ptr = v_h.value_ptr();
if (value_void_ptr != nullptr) {
PyGILState_STATE threadstate = PyGILState_Ensure();
v_h.value_ptr() = nullptr;
v_h.holder<pybindit::memory::smart_holder>().release_disowned();
detail::deregister_instance(v_h.inst, value_void_ptr, v_h.type);
Py_DECREF((PyObject *) v_h.inst); // Must be after deregister.
PyGILState_Release(threadstate);
}
}
}
// Some compilers complain about implicitly defined versions of some of the following:
trampoline_self_life_support() = default;
trampoline_self_life_support(const trampoline_self_life_support &) = default;
trampoline_self_life_support(trampoline_self_life_support &&) = default;
trampoline_self_life_support &operator=(const trampoline_self_life_support &) = default;
trampoline_self_life_support &operator=(trampoline_self_life_support &&) = default;
};
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@@ -1,48 +0,0 @@
// Copyright (c) 2021 The Pybind Development Team.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#pragma once
#include "detail/common.h"
#include "detail/smart_holder_poc.h"
#include "detail/type_caster_base.h"
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
// SMART_HOLDER_WIP: Needs refactoring of existing pybind11 code.
inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo);
PYBIND11_NAMESPACE_END(detail)
// The original core idea for this struct goes back to PyCLIF:
// https://github.com/google/clif/blob/07f95d7e69dca2fcf7022978a55ef3acff506c19/clif/python/runtime.cc#L37
// URL provided here mainly to give proper credit. To fully explain the `HoldPyObj` feature, more
// context is needed (SMART_HOLDER_WIP).
struct virtual_overrider_self_life_support {
detail::value_and_holder loaded_v_h;
~virtual_overrider_self_life_support() {
if (loaded_v_h.inst != nullptr && loaded_v_h.vh != nullptr) {
void *value_void_ptr = loaded_v_h.value_ptr();
if (value_void_ptr != nullptr) {
PyGILState_STATE threadstate = PyGILState_Ensure();
loaded_v_h.value_ptr() = nullptr;
loaded_v_h.holder<pybindit::memory::smart_holder>().release_disowned();
detail::deregister_instance(loaded_v_h.inst, value_void_ptr, loaded_v_h.type);
Py_DECREF((PyObject *) loaded_v_h.inst); // Must be after deregister.
PyGILState_Release(threadstate);
}
}
}
// Some compilers complain about implicitly defined versions of some of the following:
virtual_overrider_self_life_support() = default;
virtual_overrider_self_life_support(const virtual_overrider_self_life_support &) = default;
virtual_overrider_self_life_support(virtual_overrider_self_life_support &&) = default;
virtual_overrider_self_life_support &operator=(const virtual_overrider_self_life_support &)
= default;
virtual_overrider_self_life_support &operator=(virtual_overrider_self_life_support &&)
= default;
};
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)