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 ===============
14 static PyTypeObject CPyCppyy_NoneType;
15 
16 //-----------------------------------------------------------------------------
18 {
19  return 0;
20 }
21 
22 //-----------------------------------------------------------------------------
23 static 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 //-----------------------------------------------------------------------------
36 namespace {
37 
38 struct 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));
192  pyobj->fFlags |= CPPInstance::kIsRegulated;
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 }
TCppScope_t TCppType_t
Definition: cpp_cppyy.h:19
static bool RegisterPyObject(CPPInstance *pyobj, void *cppobj)
static MemHook_t unregisterHook
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
void op_dealloc_nofree(CPPInstance *)
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
static Py_ssize_t AlwaysNullLength(PyObject *)
#define Py_TYPE(ob)
Definition: CPyCppyy.h:209
std::function< std::pair< bool, bool >Cppyy::TCppObject_t, Cppyy::TCppType_t)> MemHook_t
static bool RecursiveRemove(Cppyy::TCppObject_t cppobj, Cppyy::TCppType_t klass)
static PyMappingMethods CPyCppyy_NoneType_mapping
static bool UnregisterPyObject(CPPInstance *pyobj, PyObject *pyclass)
_object PyObject
Definition: PyMethodBase.h:41
int Py_ssize_t
Definition: CPyCppyy.h:228
static PyTypeObject CPyCppyy_NoneType
bool CPPScope_Check(T *object)
Definition: CPPScope.h:76
static PyObject * RetrievePyObject(Cppyy::TCppObject_t cppobj, PyObject *pyclass)
void * TCppObject_t
Definition: cpp_cppyy.h:21
Int_t Compare(const void *item1, const void *item2)
static MemHook_t registerHook
Cppyy::TCppType_t ObjectIsA(bool check_smart=true) const
Definition: CPPInstance.h:106