Logo ROOT  
Reference Guide
 
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 ===============
15
16//-----------------------------------------------------------------------------
18{
19 return 0;
20}
21
22//-----------------------------------------------------------------------------
25 (binaryfunc) 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
38// Py_SET_REFCNT was only introduced in Python 3.9
39#if PY_VERSION_HEX < 0x03090000
41 assert(refcnt >= 0);
42#if SIZEOF_VOID_P > 4
43 ob->ob_refcnt = (PY_UINT32_T)refcnt;
44#else
45 ob->ob_refcnt = refcnt;
46#endif
47}
48#endif
49
50struct InitCPyCppyy_NoneType_t {
51 InitCPyCppyy_NoneType_t() {
52 // create a CPyCppyy NoneType (for references that went dodo) from NoneType
54
57 ((PyVarObject&)CPyCppyy_NoneType).ob_size = 0;
58
59 CPyCppyy_NoneType.tp_name = const_cast<char*>("CPyCppyy_NoneType");
61
62 CPyCppyy_NoneType.tp_traverse = (traverseproc)0;
63 CPyCppyy_NoneType.tp_clear = (inquiry)0;
64 CPyCppyy_NoneType.tp_dealloc = (destructor)&InitCPyCppyy_NoneType_t::DeAlloc;
65 CPyCppyy_NoneType.tp_repr = Py_TYPE(Py_None)->tp_repr;
66 CPyCppyy_NoneType.tp_richcompare = (richcmpfunc)&InitCPyCppyy_NoneType_t::RichCompare;
67#if PY_VERSION_HEX < 0x03000000
68 // tp_compare has become tp_reserved (place holder only) in p3
69 CPyCppyy_NoneType.tp_compare = (cmpfunc)&InitCPyCppyy_NoneType_t::Compare;
70#endif
71 CPyCppyy_NoneType.tp_hash = (hashfunc)&InitCPyCppyy_NoneType_t::PtrHash;
72
74
76 }
77
78 static void DeAlloc(PyObject* pyobj) { Py_TYPE(pyobj)->tp_free(pyobj); }
79 static int PtrHash(PyObject* pyobj) { return (int)ptrdiff_t(pyobj); }
80
83 }
84
85 static int Compare(PyObject*, PyObject* other) {
86#if PY_VERSION_HEX < 0x03000000
88#else
89 // TODO the following isn't correct as it doesn't order, but will do for now ...
91#endif
92 }
93};
94
95} // unnamed namespace
96
97// Memory regulation hooks
100
101
102//- ctor/dtor ----------------------------------------------------------------
104{
105// setup NoneType for referencing and create weakref cache
106 static InitCPyCppyy_NoneType_t initCPyCppyy_NoneType;
107}
108
109
110//- public members -----------------------------------------------------------
113{
114// if registered by the framework, called whenever a cppobj gets destroyed
115 if (!cppobj)
116 return false;
117
119 if (!CPPScope_Check(pyscope)) {
121 return false;
122 }
123
124 CppToPyMap_t* cppobjs = ((CPPClass*)pyscope)->fImp.fCppObjects;
125 if (!cppobjs) { // table may have been deleted on shutdown
127 return false;
128 }
129
130// see whether we're tracking this object
131 CppToPyMap_t::iterator ppo = cppobjs->find(cppobj);
132
133 if (ppo != cppobjs->end()) {
134 // get the tracked object
135 CPPInstance* pyobj = (CPPInstance*)ppo->second;
136
137 // erase the object from tracking
138 pyobj->fFlags &= ~CPPInstance::kIsRegulated;
139 cppobjs->erase(ppo);
140
141 // nullify the object
142 if (!CPyCppyy_NoneType.tp_traverse) {
143 // take a reference as we're copying its function pointers
145
146 // all object that arrive here are expected to be of the same type ("instance")
147 CPyCppyy_NoneType.tp_traverse = Py_TYPE(pyobj)->tp_traverse;
148 CPyCppyy_NoneType.tp_clear = Py_TYPE(pyobj)->tp_clear;
149 CPyCppyy_NoneType.tp_free = Py_TYPE(pyobj)->tp_free;
150 CPyCppyy_NoneType.tp_flags |= Py_TYPE(pyobj)->tp_flags;
151 } else if (CPyCppyy_NoneType.tp_traverse != Py_TYPE(pyobj)->tp_traverse) {
152 // TODO: SystemError?
153 std::cerr << "in CPyCppyy::MemoryRegulater, unexpected object of type: "
154 << Py_TYPE(pyobj)->tp_name << std::endl;
155
156 // drop object and leave before too much damage is done
158 return false;
159 }
160
161 // notify any other weak referents by playing dead
166
167 // cleanup object internals
168 pyobj->CppOwns(); // held object is out of scope now anyway
169 op_dealloc_nofree(pyobj); // normal object cleanup, while keeping memory
170
171 // reset type object
174 ((PyObject*)pyobj)->ob_type = &CPyCppyy_NoneType;
175
177 return true;
178 }
179
180// unregulated cppobj
182 return false;
183}
184
185//-----------------------------------------------------------------------------
188{
189// start tracking <cppobj> proxied by <pyobj>
190 if (!(pyobj && cppobj))
191 return false;
192
193 if (registerHook) {
194 auto res = registerHook(cppobj, pyobj->ObjectIsA(false));
195 if (!res.second) return res.first;
196 }
197
198 CppToPyMap_t* cppobjs = ((CPPClass*)Py_TYPE(pyobj))->fImp.fCppObjects;
199 if (!cppobjs)
200 return false;
201
202// if an address was already associated with a different object, then stop following
203// the old and force insert the new proxy for following
204 const auto& res = cppobjs->insert(std::make_pair(cppobj, (PyObject*)pyobj));
205 if (!res.second) {
206 ((CPPInstance*)res.first->second)->fFlags &= ~CPPInstance::kIsRegulated;
207 (*cppobjs)[cppobj] = (PyObject*)pyobj;
208 }
209
211 return true;
212}
213
214//-----------------------------------------------------------------------------
216{
217// called when the proxy is garbage collected or is about delete the C++ instance
218 if (!(pyobj && pyclass))
219 return false;
220
221 Cppyy::TCppObject_t cppobj = pyobj->IsSmart() ? pyobj->GetObjectRaw() : pyobj->GetObject();
222 if (!cppobj)
223 return false;
224
225 if (unregisterHook) {
226 auto res = unregisterHook(cppobj, ((CPPClass*)pyclass)->fCppType);
227 if (!res.second) return res.first;
228 }
229
230 CppToPyMap_t* cppobjs = ((CPPClass*)pyclass)->fImp.fCppObjects;
231 if (!cppobjs)
232 return false;
233
234// erase if tracked
235 if (cppobjs->erase(cppobj)) {
236 pyobj->fFlags &= ~CPPInstance::kIsRegulated;
237 return true;
238 }
239
240 return false;
241}
242
243//-----------------------------------------------------------------------------
245{
246// lookup to see if a C++ address is already known, return old proxy if tracked
247 if (!(cppobj && pyclass))
248 return nullptr;
249
250 CppToPyMap_t* cppobjs = ((CPPClass*)pyclass)->fImp.fCppObjects;
251 if (!cppobjs)
252 return nullptr;
253
254 CppToPyMap_t::iterator ppo = cppobjs->find(cppobj);
255 if (ppo != cppobjs->end()) {
256 Py_INCREF(ppo->second);
257 return ppo->second;
258 }
259
260 return nullptr;
261}
262
263
264//-----------------------------------------------------------------------------
266// Set custom register hook; called when a regulated object is to be tracked
267 registerHook = h;
268}
269
270//-----------------------------------------------------------------------------
272// Set custom unregister hook; called when a regulated object is to be untracked
273 unregisterHook = h;
274}
#define Py_TYPE(ob)
Definition CPyCppyy.h:196
int Py_ssize_t
Definition CPyCppyy.h:215
static Py_ssize_t AlwaysNullLength(PyObject *)
static PyMappingMethods CPyCppyy_NoneType_mapping
static PyTypeObject CPyCppyy_NoneType
_object PyObject
#define h(i)
Definition RSha256.hxx:106
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Int_t Compare(const void *item1, const void *item2)
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)
const_iterator end() const
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:81
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