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