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