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