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;
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 CPyCppyy_NoneType.tp_flags = Py_TYPE(pyobj)->tp_flags;
139 } else if (CPyCppyy_NoneType.tp_traverse != Py_TYPE(pyobj)->tp_traverse) {
140 // TODO: SystemError?
141 std::cerr << "in CPyCppyy::MemoryRegulater, unexpected object of type: "
142 << Py_TYPE(pyobj)->tp_name << std::endl;
143
144 // drop object and leave before too much damage is done
145 Py_DECREF(pyscope);
146 return false;
147 }
148
149 // notify any other weak referents by playing dead
150 Py_ssize_t refcnt = ((PyObject*)pyobj)->ob_refcnt;
151 ((PyObject*)pyobj)->ob_refcnt = 0;
152 PyObject_ClearWeakRefs((PyObject*)pyobj);
153 ((PyObject*)pyobj)->ob_refcnt = refcnt;
154
155 // cleanup object internals
156 pyobj->CppOwns(); // held object is out of scope now anyway
157 op_dealloc_nofree(pyobj); // normal object cleanup, while keeping memory
158
159 // reset type object
160 Py_INCREF((PyObject*)(void*)&CPyCppyy_NoneType);
161 Py_DECREF(Py_TYPE(pyobj));
162 ((PyObject*)pyobj)->ob_type = &CPyCppyy_NoneType;
163
164 Py_DECREF(pyscope);
165 return true;
166 }
167
168// unregulated cppobj
169 Py_DECREF(pyscope);
170 return false;
171}
172
173//-----------------------------------------------------------------------------
175 CPPInstance* pyobj, Cppyy::TCppObject_t cppobj)
176{
177// start tracking <cppobj> proxied by <pyobj>
178 if (!(pyobj && cppobj))
179 return false;
180
181 if (registerHook) {
182 auto res = registerHook(cppobj, pyobj->ObjectIsA(false));
183 if (!res.second) return res.first;
184 }
185
186 CppToPyMap_t* cppobjs = ((CPPClass*)Py_TYPE(pyobj))->fImp.fCppObjects;
187 if (!cppobjs)
188 return false;
189
190 CppToPyMap_t::iterator ppo = cppobjs->find(cppobj);
191 if (ppo == cppobjs->end()) {
192 cppobjs->insert(std::make_pair(cppobj, (PyObject*)pyobj));
194 return true;
195 }
196
197 return false;
198}
199
200//-----------------------------------------------------------------------------
202{
203// called when the proxy is garbage collected or is about delete the C++ instance
204 if (!(pyobj && pyclass))
205 return false;
206
207 Cppyy::TCppObject_t cppobj = pyobj->GetObject();
208 if (!cppobj)
209 return false;
210
211 if (unregisterHook) {
212 auto res = unregisterHook(cppobj, ((CPPClass*)pyclass)->fCppType);
213 if (!res.second) return res.first;
214 }
215
216 CppToPyMap_t* cppobjs = ((CPPClass*)pyclass)->fImp.fCppObjects;
217 if (!cppobjs)
218 return false;
219
220// erase if tracked
221 if (cppobjs->erase(cppobj)) {
222 pyobj->fFlags &= ~CPPInstance::kIsRegulated;
223 return true;
224 }
225
226 return false;
227}
228
229//-----------------------------------------------------------------------------
231{
232// lookup to see if a C++ address is already known, return old proxy if tracked
233 if (!(cppobj && pyclass))
234 return nullptr;
235
236 CppToPyMap_t* cppobjs = ((CPPClass*)pyclass)->fImp.fCppObjects;
237 if (!cppobjs)
238 return nullptr;
239
240 CppToPyMap_t::iterator ppo = cppobjs->find(cppobj);
241 if (ppo != cppobjs->end()) {
242 Py_INCREF(ppo->second);
243 return ppo->second;
244 }
245
246 return nullptr;
247}
248
249
250//-----------------------------------------------------------------------------
252// Set custom register hook; called when a regulated object is to be tracked
253 registerHook = h;
254}
255
256//-----------------------------------------------------------------------------
258// Set custom unregister hook; called when a regulated object is to be untracked
259 unregisterHook = h;
260}
#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