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