[pypy-svn] r45152 - pypy/dist/pypy/rpython/lltypesystem

arigo at codespeak.net arigo at codespeak.net
Tue Jul 17 10:27:49 CEST 2007


Author: arigo
Date: Tue Jul 17 10:27:47 2007
New Revision: 45152

Modified:
   pypy/dist/pypy/rpython/lltypesystem/ll2ctypes.py
   pypy/dist/pypy/rpython/lltypesystem/lltype.py
Log:
Move the _ctypes_storage runtime checks away from lltype.py.
It seems that they are not the cause of the major slowdowns, though.


Modified: pypy/dist/pypy/rpython/lltypesystem/ll2ctypes.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/ll2ctypes.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/ll2ctypes.py	Tue Jul 17 10:27:47 2007
@@ -63,18 +63,6 @@
                 return bigstruct
         _malloc = classmethod(_malloc)
 
-        def _getattr(self, field_name):
-            T = getattr(S, field_name)
-            cobj = getattr(self, field_name)
-            return ctypes2lltype(T, cobj)
-
-        def _setattr(self, field_name, value):
-            cobj = lltype2ctypes(value)
-            setattr(self, field_name, cobj)
-
-        def _eq(self, other):
-            return ctypes.addressof(self) == ctypes.addressof(other)
-
     CStruct.__name__ = 'ctypes_%s' % (S,)
     if max_n is not None:
         CStruct._normalized_ctype = get_ctypes_type(S)
@@ -123,9 +111,6 @@
             cobj = lltype2ctypes(value)
             items[index] = cobj
 
-        def _eq(self, other):
-            return ctypes.addressof(self) == ctypes.addressof(other)
-
     CArray.__name__ = 'ctypes_%s*%d' % (A, max_n)
     if max_n > 0:
         CArray._normalized_ctype = get_ctypes_type(A)
@@ -162,7 +147,7 @@
     STRUCT = container._TYPE
     cls = get_ctypes_type(STRUCT)
     cstruct = cls._malloc()
-    container._ctypes_storage = cstruct
+    add_storage(container, _struct_mixin, cstruct)
     for field_name in STRUCT._names:
         field_value = getattr(container, field_name)
         setattr(cstruct, field_name, lltype2ctypes(field_value))
@@ -177,7 +162,7 @@
     ARRAY = container._TYPE
     cls = get_ctypes_type(ARRAY)
     carray = cls._malloc(container.getlength())
-    container._ctypes_storage = carray
+    add_storage(container, _array_mixin, carray)
     for i in range(container.getlength()):
         item_value = container.items[i]    # fish fish
         carray.items[i] = lltype2ctypes(item_value)
@@ -187,23 +172,99 @@
     for i in range(container.getlength()):
         container.items[i] = None
 
-class _array_of_unknown_length(lltype._parentable):
-    _kind = "array"
+# ____________________________________________________________
+# Ctypes-aware subclasses of the _parentable classes
 
+def get_common_subclass(cls1, cls2, cache={}):
+    """Return a unique subclass with (cls1, cls2) as bases."""
+    try:
+        return cache[cls1, cls2]
+    except KeyError:
+        subcls = type('_ctypes_%s' % (cls1.__name__,),
+                      (cls1, cls2),
+                      {'__slots__': ()})
+        assert '__dict__' not in dir(subcls)   # use __slots__ everywhere
+        cache[cls1, cls2] = subcls
+        return subcls
+
+def add_storage(instance, mixin_cls, ctypes_storage):
+    """Put ctypes_storage on the instance, changing its __class__ so that it
+    sees the methods of the given mixin class."""
+    assert not isinstance(instance, _parentable_mixin)  # not yet
+    subcls = get_common_subclass(mixin_cls, instance.__class__)
+    instance.__class__ = subcls
+    instance._storage = ctypes_storage
+
+class _parentable_mixin(object):
+    """Mixin added to _parentable containers when they become ctypes-based.
+    (This is done by changing the __class__ of the instance to reference
+    a subclass of both its original class and of this mixin class.)
+    """
     __slots__ = ()
 
+    def __eq__(self, other):
+        if not isinstance(other, lltype._parentable):
+            return False
+        if self._storage is None or other._storage is None:
+            raise RuntimeError("pointer comparison with a freed structure")
+        if other._storage is True:
+            return False    # the other container is not ctypes-based
+        # both containers are ctypes-based, compare by address
+        return (ctypes.addressof(self._storage) ==
+                ctypes.addressof(other._storage))
+
+    def __ne__(self, other):
+        return not (self == other)
+
     def __repr__(self):
-        return '<C array at 0x%x>' % (uaddressof(self._ctypes_storage),)
+        if self._storage is None:
+            return '<freed C object %s>' % (self._TYPE,)
+        else:
+            return '<C object %s at 0x%x>' % (self._TYPE,
+                                              uaddressof(self._storage),)
+
+    def __str__(self):
+        return repr(self)
+
+class _struct_mixin(_parentable_mixin):
+    """Mixin added to _struct containers when they become ctypes-based."""
+    __slots__ = ()
+
+    def __getattr__(self, field_name):
+        T = getattr(self._TYPE, field_name)
+        cobj = getattr(self._storage, field_name)
+        return ctypes2lltype(T, cobj)
+
+    def __setattr__(self, field_name, value):
+        if field_name.startswith('_'):
+            object.__setattr__(self, field_name, value)  # '_xxx' attributes
+        else:
+            cobj = lltype2ctypes(value)
+            setattr(self._storage, field_name, cobj)
+
+class _array_mixin(_parentable_mixin):
+    """Mixin added to _array containers when they become ctypes-based."""
+    __slots__ = ()
+
+    def getitem(self, index, uninitialized_ok=False):
+        return self._storage._getitem(index)
+
+    def setitem(self, index, value):
+        self._storage._setitem(index, value)
+
+class _array_of_unknown_length(_parentable_mixin, lltype._parentable):
+    _kind = "array"
+    __slots__ = ()
 
     def getbounds(self):
         # we have no clue, so we allow whatever index
         return 0, sys.maxint
 
     def getitem(self, index, uninitialized_ok=False):
-        return self._ctypes_storage._getitem(index, boundscheck=False)
+        return self._storage._getitem(index, boundscheck=False)
 
     def setitem(self, index, value):
-        self._ctypes_storage._setitem(index, value, boundscheck=False)
+        self._storage._setitem(index, value, boundscheck=False)
 
 # ____________________________________________________________
 
@@ -236,14 +297,17 @@
                         return lltype2ctypes(llres)
                 return ctypes_func_type(callback)
 
-        if container._ctypes_storage is None:
+        if container._storage is None:
+            raise RuntimeError("attempting to pass a freed structure to C")
+        if container._storage is True:
+            # container has regular lltype storage, convert it to ctypes
             if isinstance(T.TO, lltype.Struct):
                 convert_struct(container)
             elif isinstance(T.TO, lltype.Array):
                 convert_array(container)
             else:
                 raise NotImplementedError(T)
-        storage = container._ctypes_storage
+        storage = container._storage
         p = ctypes.pointer(storage)
         if normalize and hasattr(storage, '_normalized_ctype'):
             p = ctypes.cast(p, ctypes.POINTER(storage._normalized_ctype))
@@ -268,12 +332,12 @@
         if isinstance(T.TO, lltype.Struct):
             # XXX var-sized structs
             container = lltype._struct(T.TO)
-            container._ctypes_storage = cobj.contents
+            add_storage(container, _struct_mixin, cobj.contents)
             remove_regular_struct_content(container)
         elif isinstance(T.TO, lltype.Array):
             if T.TO._hints.get('nolength', False):
                 container = _array_of_unknown_length(T.TO)
-                container._ctypes_storage = cobj.contents
+                container._storage = cobj.contents
             else:
                 raise NotImplementedError("array with an explicit length")
         elif isinstance(T.TO, lltype.FuncType):

Modified: pypy/dist/pypy/rpython/lltypesystem/lltype.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/lltype.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/lltype.py	Tue Jul 17 10:27:47 2007
@@ -1191,19 +1191,19 @@
                  '_parent_type', '_parent_index', '_keepparent',
                  '_wrparent',
                  '__weakref__',
-                 '_dead',
-                 '_ctypes_storage')
+                 '_storage')
 
     def __init__(self, TYPE):
         self._wrparent = None
         self._TYPE = TYPE
-        self._dead = False
-        self._ctypes_storage = None
+        self._storage = True    # means "use default storage", as opposed to:
+                                #    None            - container was freed
+                                #    <ctypes object> - using ctypes
+                                #                      (see ll2ctypes.py)
 
     def _free(self):
         self._check()   # no double-frees
-        self._dead = True
-        self._ctypes_storage = None
+        self._storage = None
 
     def _setparentstructure(self, parent, parentindex):
         self._wrparent = weakref.ref(parent)
@@ -1227,7 +1227,7 @@
         return None
 
     def _check(self):
-        if self._dead:
+        if self._storage is None:
             raise RuntimeError("accessing freed %r" % self._TYPE)
         self._parentstructure()
 
@@ -1248,18 +1248,6 @@
     def _setup_extra_args(self):
         pass
 
-    def __eq__(self, other):
-        if not isinstance(other, _parentable):
-            return False
-        if (self._ctypes_storage is not None and
-            other._ctypes_storage is not None):
-            return self._ctypes_storage._eq(other._ctypes_storage)
-        else:
-            return self is other
-
-    def __ne__(self, other):
-        return not (self == other)
-
 def _struct_variety(flds, cache={}):
     flds = list(flds)
     flds.sort()
@@ -1333,17 +1321,6 @@
             raise UninitializedMemoryAccess("%r.%s"%(self, field_name))
         return r
 
-    def __getattr__(self, field_name):
-        if self._ctypes_storage is not None:
-            return self._ctypes_storage._getattr(field_name)
-        raise AttributeError(field_name)
-
-    def __setattr__(self, field_name, value):
-        if field_name.startswith('_') or self._ctypes_storage is None:
-            _parentable.__setattr__(self, field_name, value)
-        else:
-            self._ctypes_storage._setattr(field_name, value)
-
     # for FixedSizeArray kind of structs:
     
     def getlength(self):
@@ -1423,8 +1400,6 @@
         return 0, stop
 
     def getitem(self, index, uninitialized_ok=False):
-        if self._ctypes_storage is not None:
-            return self._ctypes_storage._getitem(index)
         try:
             v = self.items[index]
             if isinstance(v, _uninitialized) and not uninitialized_ok:
@@ -1439,9 +1414,6 @@
             raise
 
     def setitem(self, index, value):
-        if self._ctypes_storage is not None:
-            self._ctypes_storage._setitem(index, value)
-            return
         try:
             self.items[index] = value
         except IndexError:



More information about the Pypy-commit mailing list