Logo ROOT  
Reference Guide
MemoryRegulator.cxx
Go to the documentation of this file.
1// Bindings
2#include "CPyCppyy.h"
3#include "MemoryRegulator.h"
4#include "CPPInstance.h"
5#include "ProxyWrappers.h"
6
7// Standard
8#include <assert.h>
9#include <string.h>
10#include <iostream>
11
12
13//= pseudo-None type for masking out objects on the python side ===============
14static PyTypeObject CPyCppyy_NoneType;
15
16//-----------------------------------------------------------------------------
18{
19 return 0;
20}
21
22//-----------------------------------------------------------------------------
23static PyMappingMethods CPyCppyy_NoneType_mapping = {
25 (binaryfunc) 0,
26 (objobjargproc) 0
27};
28
29// silence warning about some cast operations
30#if defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ >= 4 && ((__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ >= 1) || (__GNUC_MINOR__ >= 3)))) && !__INTEL_COMPILER
31#pragma GCC diagnostic ignored "-Wstrict-aliasing"
32#endif
33
34
35//-----------------------------------------------------------------------------
36namespace {
37
38struct InitCPyCppyy_NoneType_t {
39 InitCPyCppyy_NoneType_t() {
40 // create a CPyCppyy NoneType (for references that went dodo) from NoneType
41 memset(&CPyCppyy_NoneType, 0, sizeof(CPyCppyy_NoneType));
42
43 ((PyObject&)CPyCppyy_NoneType).ob_type = &PyType_Type;
44 ((PyObject&)CPyCppyy_NoneType).ob_refcnt = 1;
45 ((PyVarObject&)CPyCppyy_NoneType).ob_size = 0;
46
47 CPyCppyy_NoneType.tp_name = const_cast<char*>("CPyCppyy_NoneType");
48 CPyCppyy_NoneType.tp_flags = Py_TPFLAGS_HAVE_RICHCOMPARE | Py_TPFLAGS_HAVE_GC;
49
50 CPyCppyy_NoneType.tp_traverse = (traverseproc)0;
51 CPyCppyy_NoneType.tp_clear = (inquiry)0;
52 CPyCppyy_NoneType.tp_dealloc = (destructor)&InitCPyCppyy_NoneType_t::DeAlloc;
53 CPyCppyy_NoneType.tp_repr = Py_TYPE(Py_None)->tp_repr;
54 CPyCppyy_NoneType.tp_richcompare = (richcmpfunc)&InitCPyCppyy_NoneType_t::RichCompare;
55#if PY_VERSION_HEX < 0x03000000
56 // tp_compare has become tp_reserved (place holder only) in p3
58#endif
59 CPyCppyy_NoneType.tp_hash = (hashfunc)&InitCPyCppyy_NoneType_t::PtrHash;
60
62
63 PyType_Ready(&CPyCppyy_NoneType);
64 }
65
66 static void DeAlloc(PyObject* pyobj) { Py_TYPE(pyobj)->tp_free(pyobj); }
67 static int PtrHash(PyObject* pyobj) { return (int)ptrdiff_t(pyobj); }
68
69 static PyObject* RichCompare(PyObject*, PyObject* other, int opid) {
70 return PyObject_RichCompare(other, Py_None, opid);
71 }
72
73 static int Compare(PyObject*, PyObject* other) {
74#if PY_VERSION_HEX < 0x03000000
75 return PyObject_Compare(other, Py_None);
76#else
77 // TODO the following isn't correct as it doesn't order, but will do for now ...
78 return !PyObject_RichCompareBool(other, Py_None, Py_EQ);
79#endif
80 }
81};
82
83} // unnamed namespace
84
85// Memory regulation hooks
88
89
90//- ctor/dtor ----------------------------------------------------------------
92{
93// setup NoneType for referencing and create weakref cache
94 static InitCPyCppyy_NoneType_t initCPyCppyy_NoneType;
95}
96
97
98//- public members -----------------------------------------------------------
101{
102// if registerd by the framework, called whenever a cppobj gets destroyed
103 if (!cppobj)
104 return false;
105
106 PyObject* pyscope = GetScopeProxy(klass);
107 if (!CPPScope_Check(pyscope)) {
108 Py_XDECREF(pyscope);
109 return false;
110 }
111
112 CppToPyMap_t* cppobjs = ((CPPClass*)pyscope)->fImp.fCppObjects;
113 if (!cppobjs) { // table may have been deleted on shutdown
114 Py_DECREF(pyscope);
115 return false;
116 }
117
118// see whether we're tracking this object
119 CppToPyMap_t::iterator ppo = cppobjs->find(cppobj);
120
121 if (ppo != cppobjs->end()) {
122 // get the tracked object
123 CPPInstance* pyobj = (CPPInstance*)ppo->second;
124
125 // erase the object from tracking
127 cppobjs->erase(ppo);
128
129 // nullify the object
130 if (!CPyCppyy_NoneType.tp_traverse) {
131 // take a reference as we're copying its function pointers
132 Py_INCREF(Py_TYPE(pyobj));
133
134 // all object that arrive here are expected to be of the same type ("instance")
135 CPyCppyy_NoneType.tp_traverse = Py_TYPE(pyobj)->tp_traverse;
136 CPyCppyy_NoneType.tp_clear = Py_TYPE(pyobj)->tp_clear;
137 CPyCppyy_NoneType.tp_free = Py_TYPE(pyobj)->tp_free;
138 } else if (CPyCppyy_NoneType.tp_traverse != Py_TYPE(pyobj)->tp_traverse) {
139 // TODO: SystemError?
140 std::cerr << "in CPyCppyy::MemoryRegulater, unexpected object of type: "
141 << Py_TYPE(pyobj)->tp_name << std::endl;
142
143 // drop object and leave before too much damage is done
144 Py_DECREF(pyscope);
145 return false;
146 }
147
148 // notify any other weak referents by playing dead
149 Py_ssize_t refcnt = ((PyObject*)pyobj)->ob_refcnt;
150 ((PyObject*)pyobj)->ob_refcnt = 0;
151 PyObject_ClearWeakRefs((PyObject*)pyobj);
152 ((PyObject*)pyobj)->ob_refcnt = refcnt;
153
154 // cleanup object internals
155 pyobj->CppOwns(); // held object is out of scope now anyway
156 op_dealloc_nofree(pyobj); // normal object cleanup, while keeping memory
157
158 // reset type object
159 Py_INCREF((PyObject*)(void*)&CPyCppyy_NoneType);
160 Py_DECREF(Py_TYPE(pyobj));
161 ((PyObject*)pyobj)->ob_type = &CPyCppyy_NoneType;
162
163 Py_DECREF(pyscope);
164 return true;
165 }
166
167// unregulated cppobj
168 Py_DECREF(pyscope);
169 return false;
170}
171
172//-----------------------------------------------------------------------------
174 CPPInstance* pyobj, Cppyy::TCppObject_t cppobj)
175{
176// start tracking <cppobj> proxied by <pyobj>
177 if (!(pyobj && cppobj))
178 return false;
179
180 if (registerHook) {
181 auto res = registerHook(cppobj, pyobj->ObjectIsA(false));
182 if (!res.second) return res.first;
183 }
184
185 CppToPyMap_t* cppobjs = ((CPPClass*)Py_TYPE(pyobj))->fImp.fCppObjects;
186 if (!cppobjs)
187 return false;
188
189 CppToPyMap_t::iterator ppo = cppobjs->find(cppobj);
190 if (ppo == cppobjs->end()) {
191 cppobjs->insert(std::make_pair(cppobj, (PyObject*)pyobj));
193 return true;
194 }
195
196 return false;
197}
198
199//-----------------------------------------------------------------------------
201{
202// called when the proxy is garbage collected or is about delete the C++ instance
203 if (!(pyobj && pyclass))
204 return false;
205
206 Cppyy::TCppObject_t cppobj = pyobj->GetObject();
207 if (!cppobj)
208 return false;
209
210 if (unregisterHook) {
211 auto res = unregisterHook(cppobj, ((CPPClass*)pyclass)->fCppType);
212 if (!res.second) return res.first;
213 }
214
215 CppToPyMap_t* cppobjs = ((CPPClass*)pyclass)->fImp.fCppObjects;
216 if (!cppobjs)
217 return false;
218
219// erase if tracked
220 if (cppobjs->erase(cppobj)) {
221 pyobj->fFlags &= ~CPPInstance::kIsRegulated;
222 return true;
223 }
224
225 return false;
226}
227
228//-----------------------------------------------------------------------------
230{
231// lookup to see if a C++ address is already known, return old proxy if tracked
232 if (!(cppobj && pyclass))
233 return nullptr;
234
235 CppToPyMap_t* cppobjs = ((CPPClass*)pyclass)->fImp.fCppObjects;
236 if (!cppobjs)
237 return nullptr;
238
239 CppToPyMap_t::iterator ppo = cppobjs->find(cppobj);
240 if (ppo != cppobjs->end()) {
241 Py_INCREF(ppo->second);
242 return ppo->second;
243 }
244
245 return nullptr;
246}
247
248
249//-----------------------------------------------------------------------------
251// Set custom register hook; called when a regulated object is to be tracked
252 registerHook = h;
253}
254
255//-----------------------------------------------------------------------------
257// Set custom unregister hook; called when a regulated object is to be untracked
258 unregisterHook = h;
259}
#define Py_TYPE(ob)
Definition: CPyCppyy.h:217
int Py_ssize_t
Definition: CPyCppyy.h:236
static Py_ssize_t AlwaysNullLength(PyObject *)
static PyMappingMethods CPyCppyy_NoneType_mapping
static PyTypeObject CPyCppyy_NoneType
_object PyObject
Definition: PyMethodBase.h:43
#define h(i)
Definition: RSha256.hxx:106
Int_t Compare(const void *item1, const void *item2)
Cppyy::TCppType_t ObjectIsA(bool check_smart=true) const
Definition: CPPInstance.h:106
static MemHook_t unregisterHook
static bool RecursiveRemove(Cppyy::TCppObject_t cppobj, Cppyy::TCppType_t klass)
static PyObject * RetrievePyObject(Cppyy::TCppObject_t cppobj, PyObject *pyclass)
static bool RegisterPyObject(CPPInstance *pyobj, void *cppobj)
static MemHook_t registerHook
static void SetUnregisterHook(MemHook_t h)
static void SetRegisterHook(MemHook_t h)
static bool UnregisterPyObject(CPPInstance *pyobj, PyObject *pyclass)
std::map< Cppyy::TCppObject_t, PyObject * > CppToPyMap_t
Type object to hold class reference (this is only semantically a presentation of CPPScope instances,...
Definition: CPPScope.h:34
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
bool CPPScope_Check(T *object)
Definition: CPPScope.h:76
void op_dealloc_nofree(CPPInstance *)
std::function< std::pair< bool, bool >(Cppyy::TCppObject_t, Cppyy::TCppType_t)> MemHook_t
void * TCppObject_t
Definition: cpp_cppyy.h:21
TCppScope_t TCppType_t
Definition: cpp_cppyy.h:19