Logo ROOT   6.16/01
Reference Guide
TMemoryRegulator.cxx
Go to the documentation of this file.
1// @(#)root/pyroot:$Id$
2// Author: Wim Lavrijsen, Apr 2004
3
4// Bindings
5#include "PyROOT.h"
6#include "TMemoryRegulator.h"
7#include "ObjectProxy.h"
8
9// Standard
10#include <assert.h>
11#include <string.h>
12#include <Riostream.h>
13
14
15//- static data --------------------------------------------------------------
18
19
20namespace {
21
22// memory regulater callback for deletion of registered objects
23 PyMethodDef gObjectEraseMethodDef = {
24 const_cast< char* >( "TMemoryRegulator_internal_ObjectEraseCallback" ),
26 METH_O,
27 NULL
28 };
29
30// pseudo-None type for masking out objects on the python side
31 PyTypeObject PyROOT_NoneType;
32
33////////////////////////////////////////////////////////////////////////////////
34
35 Py_ssize_t AlwaysNullLength( PyObject* )
36 {
37 return 0;
38 }
39
40////////////////////////////////////////////////////////////////////////////////
41
42 PyMappingMethods PyROOT_NoneType_mapping = {
43 AlwaysNullLength,
44 (binaryfunc) 0,
45 (objobjargproc) 0
46 };
47
48// silence warning about some cast operations
49#if defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ >= 4 && ((__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ >= 1) || (__GNUC_MINOR__ >= 3)))) && !__INTEL_COMPILER
50#pragma GCC diagnostic ignored "-Wstrict-aliasing"
51#endif
52
53////////////////////////////////////////////////////////////////////////////////
54
55 struct InitPyROOT_NoneType_t {
56 InitPyROOT_NoneType_t()
57 {
58 // createa PyROOT NoneType (for references that went dodo) from NoneType
59 memset( &PyROOT_NoneType, 0, sizeof( PyROOT_NoneType ) );
60
61 ((PyObject&)PyROOT_NoneType).ob_type = &PyType_Type;
62 ((PyObject&)PyROOT_NoneType).ob_refcnt = 1;
63 ((PyVarObject&)PyROOT_NoneType).ob_size = 0;
64
65 PyROOT_NoneType.tp_name = const_cast< char* >( "PyROOT_NoneType" );
66 PyROOT_NoneType.tp_flags = Py_TPFLAGS_HAVE_RICHCOMPARE | Py_TPFLAGS_HAVE_GC;
67
68 PyROOT_NoneType.tp_traverse = (traverseproc) 0;
69 PyROOT_NoneType.tp_clear = (inquiry) 0;
70 PyROOT_NoneType.tp_dealloc = (destructor) &InitPyROOT_NoneType_t::DeAlloc;
71 PyROOT_NoneType.tp_repr = Py_TYPE(Py_None)->tp_repr;
72 PyROOT_NoneType.tp_richcompare = (richcmpfunc) &InitPyROOT_NoneType_t::RichCompare;
73#if PY_VERSION_HEX < 0x03000000
74// tp_compare has become tp_reserved (place holder only) in p3
75 PyROOT_NoneType.tp_compare = (cmpfunc) &InitPyROOT_NoneType_t::Compare;
76#endif
77 PyROOT_NoneType.tp_hash = (hashfunc) &InitPyROOT_NoneType_t::PtrHash;
78
79 PyROOT_NoneType.tp_as_mapping = &PyROOT_NoneType_mapping;
80
81 PyType_Ready( &PyROOT_NoneType );
82 }
83
84 static void DeAlloc( PyObject* obj ) { Py_TYPE(obj)->tp_free( obj ); }
85 static int PtrHash( PyObject* obj ) { return (int)Long_t(obj); }
86
87 static PyObject* RichCompare( PyObject*, PyObject* other, int opid )
88 {
89 return PyObject_RichCompare( other, Py_None, opid );
90 }
91
92 static int Compare( PyObject*, PyObject* other )
93 {
94#if PY_VERSION_HEX < 0x03000000
95 return PyObject_Compare( other, Py_None );
96#else
97// TODO the following isn't correct as it doens't order, but will do for now ...
98 return ! PyObject_RichCompareBool( other, Py_None, Py_EQ );
99#endif
100 }
101 };
102
103} // unnamed namespace
104
105
106//- ctor/dtor ----------------------------------------------------------------
108{
109// setup NoneType for referencing and create weakref cache
110 static InitPyROOT_NoneType_t initPyROOT_NoneType;
111
112 assert( fgObjectTable == 0 );
114
115 assert( fgWeakRefTable == 0 );
117}
118
119////////////////////////////////////////////////////////////////////////////////
120/// cleanup weakref cache
121
123{
124 delete fgWeakRefTable;
125 fgWeakRefTable = 0;
126
127 delete fgObjectTable;
128 fgObjectTable = 0;
129}
130
131
132//- public members -----------------------------------------------------------
134{
135// called whenever a TObject gets destroyed
136 if ( ! object || ! fgObjectTable ) // table can be deleted before libCore is done
137 return;
138
139// see whether we're tracking this object
140 ObjectMap_t::iterator ppo = fgObjectTable->find( object );
141
142 if ( ppo != fgObjectTable->end() ) {
143 fgWeakRefTable->erase( fgWeakRefTable->find( ppo->second ) );
144
145 // get the tracked object
146 ObjectProxy* pyobj = (ObjectProxy*)PyWeakref_GetObject( ppo->second );
147 if ( ! pyobj ) {
148 fgObjectTable->erase( ppo );
149 return;
150 }
151
152 // clean up the weak reference.
153 Py_DECREF( ppo->second );
154
155 // nullify the object
156 if ( ObjectProxy_Check( pyobj ) ) {
157 if ( ! PyROOT_NoneType.tp_traverse ) {
158 // take a reference as we're copying its function pointers
159 Py_INCREF( Py_TYPE(pyobj) );
160
161 // all object that arrive here are expected to be of the same type ("instance")
162 PyROOT_NoneType.tp_traverse = Py_TYPE(pyobj)->tp_traverse;
163 PyROOT_NoneType.tp_clear = Py_TYPE(pyobj)->tp_clear;
164 PyROOT_NoneType.tp_free = Py_TYPE(pyobj)->tp_free;
165 } else if ( PyROOT_NoneType.tp_traverse != Py_TYPE(pyobj)->tp_traverse ) {
166 std::cerr << "in PyROOT::TMemoryRegulater, unexpected object of type: "
167 << Py_TYPE(pyobj)->tp_name << std::endl;
168
169 // leave before too much damage is done
170 return;
171 }
172
173 // notify any other weak referents by playing dead
174 int refcnt = ((PyObject*)pyobj)->ob_refcnt;
175 ((PyObject*)pyobj)->ob_refcnt = 0;
176 PyObject_ClearWeakRefs( (PyObject*)pyobj );
177 ((PyObject*)pyobj)->ob_refcnt = refcnt;
178
179 // cleanup object internals
180 pyobj->Release(); // held object is out of scope now anyway
181 op_dealloc_nofree( pyobj ); // normal object cleanup, while keeping memory
182
183 // reset type object
184 Py_INCREF( (PyObject*)(void*)&PyROOT_NoneType );
185 Py_DECREF( Py_TYPE(pyobj) );
186 ((PyObject*)pyobj)->ob_type = &PyROOT_NoneType;
187 }
188
189 // erase the object from tracking (weakref table already cleared, above)
190 fgObjectTable->erase( ppo );
191 }
192}
193
194////////////////////////////////////////////////////////////////////////////////
195/// start tracking <object> proxied by <pyobj>
196
198{
199 static PyObject* objectEraseCallback = PyCFunction_New(&gObjectEraseMethodDef, nullptr);
200
201 if ( ! ( pyobj && object ) )
202 return kFALSE;
203
204 ObjectMap_t::iterator ppo = fgObjectTable->find( object );
205 if ( ppo == fgObjectTable->end() ) {
206 object->SetBit( TObject::kMustCleanup );
207 PyObject* pyref = PyWeakref_NewRef( (PyObject*)pyobj, objectEraseCallback );
208 ObjectMap_t::iterator newppo = fgObjectTable->insert( std::make_pair( object, pyref ) ).first;
209 (*fgWeakRefTable)[ pyref ] = newppo; // no Py_INCREF on pyref, as object table has one
210 return kTRUE;
211 }
212
213 return kFALSE;
214}
215
216////////////////////////////////////////////////////////////////////////////////
217/// stop tracking <object>, without notification
218
220{
221 ObjectMap_t::iterator ppo = fgObjectTable->find( object );
222
223 if ( ppo != fgObjectTable->end() ) {
224 fgWeakRefTable->erase( fgWeakRefTable->find( ppo->second ) );
225 fgObjectTable->erase( ppo );
226 return kTRUE;
227 }
228
229 return kFALSE;
230}
231
232////////////////////////////////////////////////////////////////////////////////
233/// lookup <object>, return old proxy if tracked
234
236{
237 if ( ! object )
238 return 0;
239
240 ObjectMap_t::iterator ppo = fgObjectTable->find( object );
241 if ( ppo != fgObjectTable->end() ) {
242 PyObject* pyobj = PyWeakref_GetObject( ppo->second );
243 Py_XINCREF( pyobj );
244 if ( pyobj && ((ObjectProxy*)pyobj)->ObjectIsA() != klass ) {
245 Py_DECREF( pyobj );
246 return 0;
247 }
248 return pyobj;
249 }
250
251 return 0;
252}
253
254
255//- private static members ------------------------------------------------------
257{
258// called when one of the python objects we've registered is going away
259 ObjectProxy* pyobj = (ObjectProxy*)PyWeakref_GetObject( pyref );
260
261 if ( ObjectProxy_Check( pyobj ) && pyobj->GetObject() != 0 ) {
262 // get TObject pointer to the object
263 static Cppyy::TCppScope_t sTObjectScope = Cppyy::GetScope( "TObject" );
264 Cppyy::TCppType_t klass = pyobj->ObjectIsA();
265 if ( Cppyy::IsSubtype( klass, sTObjectScope) ) {
266 void* address = pyobj->GetObject();
267 TObject* object = (TObject*)((Long_t)address + \
268 Cppyy::GetBaseOffset( klass, sTObjectScope, address, 1 /* up-cast */ ) );
269
270 // erase if tracked
271 ObjectMap_t::iterator ppo = fgObjectTable->find( object );
272 if ( ppo != fgObjectTable->end() ) {
273 // cleanup table entries and weak reference
274 fgWeakRefTable->erase( fgWeakRefTable->find( ppo->second ) );
275 Py_DECREF( ppo->second );
276 fgObjectTable->erase( ppo );
277 }
278 }
279 } else {
280 // object already dead; need to clean up the weak ref from the table
281 WeakRefMap_t::iterator wri = fgWeakRefTable->find( pyref );
282 if ( wri != fgWeakRefTable->end() ) {
283 fgObjectTable->erase( wri->second );
284 fgWeakRefTable->erase( wri );
285 Py_DECREF( pyref );
286 }
287 }
288
289 Py_INCREF( Py_None );
290 return Py_None;
291}
#define Py_TYPE(ob)
Definition: PyROOT.h:161
int Py_ssize_t
Definition: PyROOT.h:166
const Bool_t kFALSE
Definition: RtypesCore.h:88
long Long_t
Definition: RtypesCore.h:50
bool Bool_t
Definition: RtypesCore.h:59
const Bool_t kTRUE
Definition: RtypesCore.h:87
Int_t Compare(const void *item1, const void *item2)
_object PyObject
Definition: TPyArg.h:20
Cppyy::TCppType_t ObjectIsA() const
Definition: ObjectProxy.h:66
void * GetObject() const
Definition: ObjectProxy.h:47
static Bool_t UnregisterObject(TObject *object)
stop tracking <object>, without notification
static Bool_t RegisterObject(ObjectProxy *pyobj, TObject *object)
start tracking <object> proxied by <pyobj>
virtual void RecursiveRemove(TObject *object)
Recursively remove this object from a list.
~TMemoryRegulator()
cleanup weakref cache
static ObjectMap_t * fgObjectTable
static PyObject * ObjectEraseCallback(PyObject *, PyObject *pyref)
std::map< PyObject *, ObjectMap_t::iterator > WeakRefMap_t
static PyObject * RetrieveObject(TObject *object, Cppyy::TCppType_t klass)
lookup <object>, return old proxy if tracked
static WeakRefMap_t * fgWeakRefTable
std::map< TObject *, PyObject * > ObjectMap_t
Mother of all ROOT objects.
Definition: TObject.h:37
@ kMustCleanup
if object destructor must call RecursiveRemove()
Definition: TObject.h:60
ptrdiff_t GetBaseOffset(TCppType_t derived, TCppType_t base, TCppObject_t address, int direction, bool rerror=false)
Definition: Cppyy.cxx:637
ptrdiff_t TCppScope_t
Definition: Cppyy.h:15
TCppScope_t TCppType_t
Definition: Cppyy.h:16
TCppScope_t GetScope(const std::string &scope_name)
Definition: Cppyy.cxx:193
Bool_t IsSubtype(TCppType_t derived, TCppType_t base)
Definition: Cppyy.cxx:615
Bool_t ObjectProxy_Check(T *object)
Definition: ObjectProxy.h:91
void op_dealloc_nofree(ObjectProxy *)
Destroy the held C++ object, if owned; does not deallocate the proxy.
Definition: ObjectProxy.cxx:46