Rev 141: When removing an item from the collection, store it in the proxy. in http://bazaar.launchpad.net/~jameinel/meliae/mem-object-collection

John Arbash Meinel john at arbash-meinel.com
Mon Dec 28 19:59:07 GMT 2009


At http://bazaar.launchpad.net/~jameinel/meliae/mem-object-collection

------------------------------------------------------------
revno: 141
revision-id: john at arbash-meinel.com-20091228195848-y0ici6c6l4humkdp
parent: john at arbash-meinel.com-20091228052742-5a4pyc9lmwpukvy5
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: mem-object-collection
timestamp: Mon 2009-12-28 13:58:48 -0600
message:
  When removing an item from the collection, store it in the proxy.
  
  This allows the proxy to continue working even after the item
  is removed from the collection. This is used by the compute_referrers
  code.
-------------- next part --------------
=== modified file 'meliae/_loader.pyx'
--- a/meliae/_loader.pyx	2009-12-28 05:27:42 +0000
+++ b/meliae/_loader.pyx	2009-12-28 19:58:48 +0000
@@ -149,6 +149,29 @@
     unsigned long total_size
 
 
+cdef int _free_mem_object(_MemObject *cur) except -1:
+    if cur == NULL: # Already cleared
+        return 0
+    if cur == _dummy:
+        return 0
+    if cur.address == NULL:
+        raise RuntimeError('clering something that doesn\'t have address')
+    Py_XDECREF(cur.address)
+    cur.address = NULL
+    Py_XDECREF(cur.type_str)
+    cur.type_str = NULL
+    _free_ref_list(cur.ref_list)
+    cur.ref_list = NULL
+    Py_XDECREF(cur.value)
+    cur.value = NULL
+    Py_XDECREF(cur.name)
+    cur.name = NULL
+    _free_ref_list(cur.referrer_list)
+    cur.referrer_list = NULL
+    PyMem_Free(cur)
+    return 1
+
+
 cdef _MemObject *_dummy
 _dummy = <_MemObject*>(-1)
 
@@ -166,59 +189,52 @@
     """
 
     cdef MemObjectCollection collection
+    # This must be set immediately after construction, before accessing any
+    # member vars
     cdef _MemObject *_obj
+    # If not NULL, this will be freed when this object is deallocated
+    cdef _MemObject *_managed_obj
     cdef object __weakref__
 
     def __init__(self, collection):
         self.collection = collection
         self._obj = NULL
-
-    cdef _MemObject *_ensure_obj(self) except NULL:
-        if self._obj is NULL:
-            raise RuntimeError('_MemObjectProxy was deleted underneath it.')
-        return self._obj
-
-    def is_valid(self):
-        if self._obj is NULL:
-            return False
-        return True
+        self._managed_obj = NULL
+
+    def __dealloc__(self):
+        if self._managed_obj != NULL:
+            _free_mem_object(self._managed_obj)
+            self._managed_obj = NULL
 
     property address:
         def __get__(self):
-            self._ensure_obj()
             return <object>(self._obj.address)
 
     property type_str:
         """The type of this object."""
         def __get__(self):
-            self._ensure_obj()
             return <object>(self._obj.type_str)
 
     property size:
         """The number of bytes allocated for this object."""
         def __get__(self):
-            self._ensure_obj()
             return self._obj.size
 
         def __set__(self, value):
-            self._ensure_obj()
             self._obj.size = value
 
     property name:
         """Name associated with this object."""
         def __get__(self):
-            self._ensure_obj()
             return <object>self._obj.name
 
     property value:
         """Value for this object (for strings and ints)"""
         def __get__(self):
-            self._ensure_obj()
             return <object>self._obj.value
 
         def __set__(self, value):
             cdef PyObject *new_val
-            self._ensure_obj()
             new_val = <PyObject *>value
             # INCREF first, just in case value is self._obj.value
             Py_INCREF(new_val)
@@ -228,15 +244,12 @@
     property total_size:
         """Mean to hold the size of this plus size of all referenced objects."""
         def __get__(self):
-            self._ensure_obj()
             return self._obj.total_size
 
         def __set__(self, value):
-            self._ensure_obj()
             self._obj.total_size = value
 
     def __len__(self):
-        self._ensure_obj()
         if self._obj.ref_list == NULL:
             return 0
         return self._obj.ref_list.size
@@ -246,7 +259,6 @@
             return self.__len__()
 
     def _intern_from_cache(self, cache):
-        self._ensure_obj()
         address = _set_default(cache, <object>self._obj.address)
         if (<PyObject *>address) != self._obj.address:
             Py_DECREF(self._obj.address)
@@ -261,11 +273,9 @@
     property ref_list:
         """The list of objects referenced by this object."""
         def __get__(self):
-            self._ensure_obj()
             return _ref_list_to_list(self._obj.ref_list)
 
         def __set__(self, value):
-            self._ensure_obj()
             _free_ref_list(self._obj.ref_list)
             self._obj.ref_list = _list_to_ref_list(value)
 
@@ -275,18 +285,15 @@
         Original set to None, can be computed on demand.
         """
         def __get__(self):
-            self._ensure_obj()
             return _ref_list_to_list(self._obj.referrer_list)
 
         def __set__(self, value):
-            self._ensure_obj()
             _free_ref_list(self._obj.referrer_list)
             self._obj.referrer_list = _list_to_ref_list(value)
 
     property num_referrers:
         """The length of the referrers list."""
         def __get__(self):
-            self._ensure_obj()
             if self._obj.referrer_list == NULL:
                 return 0
             return self._obj.referrer_list.size
@@ -294,7 +301,6 @@
 
     def __getitem__(self, offset):
         cdef long off
-        self._ensure_obj()
 
         if self._obj.ref_list == NULL:
             raise IndexError('%s has no references' % (self,))
@@ -367,26 +373,7 @@
                            % (n_lookup))
 
     cdef int _clear_slot(self, _MemObject **slot) except -1:
-        if slot[0] == NULL: # Already cleared
-            return 0
-        if slot[0] == _dummy:
-            slot[0] = NULL
-            return 0
-        if slot[0].address == NULL:
-            raise RuntimeError('clering something that doesn\'t have address')
-        Py_XDECREF(slot[0].address)
-        slot[0].address = NULL
-        Py_XDECREF(slot[0].type_str)
-        slot[0].type_str = NULL
-        _free_ref_list(slot[0].ref_list)
-        slot[0].ref_list = NULL
-        Py_XDECREF(slot[0].value)
-        slot[0].value = NULL
-        Py_XDECREF(slot[0].name)
-        slot[0].name = NULL
-        _free_ref_list(slot[0].referrer_list)
-        slot[0].referrer_list = NULL
-        PyMem_Free(slot[0])
+        _free_mem_object(slot[0])
         slot[0] = NULL
         return 1
 
@@ -453,8 +440,10 @@
             raise KeyError('address %s not present' % (at,))
         proxy = self._proxies.get(address, None)
         if proxy is not None:
-            proxy._obj = NULL
-        self._clear_slot(slot)
+            # Have the proxy take over the memory lifetime
+            proxy._managed_obj = proxy._obj
+        else:
+            self._clear_slot(slot)
         slot[0] = _dummy
         self._active -= 1
         # TODO: Shrink

=== modified file 'meliae/tests/test__loader.py'
--- a/meliae/tests/test__loader.py	2009-12-28 05:27:42 +0000
+++ b/meliae/tests/test__loader.py	2009-12-28 19:58:48 +0000
@@ -300,10 +300,9 @@
     def test_deleted_proxy(self):
         mop = self.moc[0]
         del self.moc[0]
-        self.assertFalse(mop.is_valid())
-        self.assertRaises(RuntimeError, lambda: mop.type_str)
-        self.assertRaises(RuntimeError, lambda: mop.size)
-        self.assertRaises(RuntimeError, len, mop)
+        self.assertEqual('foo', mop.type_str)
+        self.assertEqual(100, mop.size)
+        self.assertEqual(0, len(mop))
 
     def test_value(self):
         mop = self.moc.add(1234, 'type', 256, value='testval')



More information about the bazaar-commits mailing list