[pypy-commit] pypy default: Change in cffi 1.3: allow ctype objects to die
arigo
noreply at buildbot.pypy.org
Thu Oct 1 16:32:43 CEST 2015
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r79920:438df7ee1d33
Date: 2015-10-01 16:26 +0200
http://bitbucket.org/pypy/pypy/changeset/438df7ee1d33/
Log: Change in cffi 1.3: allow ctype objects to die
diff --git a/pypy/module/_cffi_backend/ctypeobj.py b/pypy/module/_cffi_backend/ctypeobj.py
--- a/pypy/module/_cffi_backend/ctypeobj.py
+++ b/pypy/module/_cffi_backend/ctypeobj.py
@@ -11,7 +11,8 @@
class W_CType(W_Root):
- _attrs_ = ['space', 'size', 'name', 'name_position', '_lifeline_']
+ _attrs_ = ['space', 'size', 'name', 'name_position', '_lifeline_',
+ '_pointer_type']
_immutable_fields_ = ['size?', 'name', 'name_position']
# note that 'size' is not strictly immutable, because it can change
# from -1 to the real value in the W_CTypeStruct subclass.
diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py
--- a/pypy/module/_cffi_backend/ctypeptr.py
+++ b/pypy/module/_cffi_backend/ctypeptr.py
@@ -168,7 +168,7 @@
class W_CTypePointer(W_CTypePtrBase):
- _attrs_ = ['is_file', 'cache_array_type', 'is_void_ptr']
+ _attrs_ = ['is_file', 'cache_array_type', 'is_void_ptr', '_array_types']
_immutable_fields_ = ['is_file', 'cache_array_type?', 'is_void_ptr']
kind = "pointer"
cache_array_type = None
diff --git a/pypy/module/_cffi_backend/newtype.py b/pypy/module/_cffi_backend/newtype.py
--- a/pypy/module/_cffi_backend/newtype.py
+++ b/pypy/module/_cffi_backend/newtype.py
@@ -4,7 +4,7 @@
from rpython.rlib.objectmodel import specialize, r_dict, compute_identity_hash
from rpython.rlib.rarithmetic import ovfcheck, intmask
-from rpython.rlib import jit
+from rpython.rlib import jit, rweakref
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rtyper.tool import rffi_platform
@@ -23,27 +23,12 @@
class UniqueCache:
def __init__(self, space):
- self.ctvoid = None # There can be only one
- self.ctvoidp = None # Cache for self.pointers[self.ctvoid]
- self.ctchara = None # Cache for self.arrays[charp, -1]
- self.primitives = {} # Keys: name
- self.pointers = {} # Keys: base_ctype
- self.arrays = {} # Keys: (ptr_ctype, length_or_-1)
- self.functions = r_dict(# Keys: (fargs, w_fresult, ellipsis)
- _func_key_eq, _func_key_hash)
-
-def _func_key_eq((fargs1, w_fresult1, ellipsis1),
- (fargs2, w_fresult2, ellipsis2)):
- return (fargs1 == fargs2 and # list equality here
- w_fresult1 is w_fresult2 and
- ellipsis1 == ellipsis2)
-
-def _func_key_hash((fargs, w_fresult, ellipsis)):
- x = compute_identity_hash(w_fresult) ^ ellipsis
- for w_arg in fargs:
- y = compute_identity_hash(w_arg)
- x = intmask((1000003 * x) ^ y)
- return x
+ self.ctvoid = None # Cache for the 'void' type
+ self.ctvoidp = None # Cache for the 'void *' type
+ self.ctchara = None # Cache for the 'char[]' type
+ self.primitives = {} # Cache for {name: primitive_type}
+ self.functions = [] # see _new_function_type()
+ self.for_testing = False
def _clean_cache(space):
"NOT_RPYTHON"
@@ -165,20 +150,24 @@
# ____________________________________________________________
+ at specialize.memo()
+def _setup_wref(has_weakref_support):
+ assert has_weakref_support, "_cffi_backend requires weakrefs"
+ ctypeobj.W_CType._pointer_type = rweakref.dead_ref
+ ctypeptr.W_CTypePointer._array_types = None
+
@unwrap_spec(w_ctype=ctypeobj.W_CType)
def new_pointer_type(space, w_ctype):
return _new_pointer_type(space, w_ctype)
@jit.elidable
def _new_pointer_type(space, w_ctype):
- unique_cache = space.fromcache(UniqueCache)
- try:
- return unique_cache.pointers[w_ctype]
- except KeyError:
- pass
- ctypepointer = ctypeptr.W_CTypePointer(space, w_ctype)
- unique_cache.pointers[w_ctype] = ctypepointer
- return ctypepointer
+ _setup_wref(rweakref.has_weakref_support())
+ ctptr = w_ctype._pointer_type()
+ if ctptr is None:
+ ctptr = ctypeptr.W_CTypePointer(space, w_ctype)
+ w_ctype._pointer_type = rweakref.ref(ctptr)
+ return ctptr
# ____________________________________________________________
@@ -195,16 +184,19 @@
@jit.elidable
def _new_array_type(space, w_ctptr, length):
- unique_cache = space.fromcache(UniqueCache)
- unique_key = (w_ctptr, length)
- try:
- return unique_cache.arrays[unique_key]
- except KeyError:
- pass
- #
+ _setup_wref(rweakref.has_weakref_support())
if not isinstance(w_ctptr, ctypeptr.W_CTypePointer):
raise OperationError(space.w_TypeError,
space.wrap("first arg must be a pointer ctype"))
+ arrays = w_ctptr._array_types
+ if arrays is None:
+ arrays = rweakref.RWeakValueDictionary(int, ctypearray.W_CTypeArray)
+ w_ctptr._array_types = arrays
+ else:
+ ctype = arrays.get(length)
+ if ctype is not None:
+ return ctype
+ #
ctitem = w_ctptr.ctitem
if ctitem.size < 0:
raise oefmt(space.w_ValueError, "array item of unknown size: '%s'",
@@ -222,7 +214,7 @@
extra = '[%d]' % length
#
ctype = ctypearray.W_CTypeArray(space, w_ctptr, length, arraysize, extra)
- unique_cache.arrays[unique_key] = ctype
+ arrays.set(length, ctype)
return ctype
# ____________________________________________________________
@@ -612,29 +604,69 @@
fargs.append(w_farg)
return _new_function_type(space, fargs, w_fresult, bool(ellipsis))
+def _func_key_hash(unique_cache, fargs, fresult, ellipsis):
+ x = compute_identity_hash(fresult)
+ for w_arg in fargs:
+ y = compute_identity_hash(w_arg)
+ x = intmask((1000003 * x) ^ y)
+ x ^= ellipsis
+ if unique_cache.for_testing: # constant-folded to False in translation;
+ x &= 3 # but for test, keep only 2 bits of hash
+ return x
+
# can't use @jit.elidable here, because it might call back to random
# space functions via force_lazy_struct()
-def _new_function_type(space, fargs, w_fresult, ellipsis=False):
+def _new_function_type(space, fargs, fresult, ellipsis=False):
+ try:
+ return _get_function_type(space, fargs, fresult, ellipsis)
+ except KeyError:
+ return _build_function_type(space, fargs, fresult, ellipsis)
+
+ at jit.elidable
+def _get_function_type(space, fargs, fresult, ellipsis):
+ # This function is elidable because if called again with exactly the
+ # same arguments (and if it didn't raise KeyError), it would give
+ # the same result, at least as long as this result is still live.
+ #
+ # 'unique_cache.functions' is a list of weak dicts, each mapping
+ # the func_hash number to a W_CTypeFunc. There is normally only
+ # one such dict, but in case of hash collision, there might be
+ # more.
+ unique_cache = space.fromcache(UniqueCache)
+ func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis)
+ for weakdict in unique_cache.functions:
+ ctype = weakdict.get(func_hash)
+ if (ctype is not None and
+ ctype.ctitem is fresult and
+ ctype.fargs == fargs and
+ ctype.ellipsis == ellipsis):
+ return ctype
+ raise KeyError
+
+ at jit.dont_look_inside
+def _build_function_type(space, fargs, fresult, ellipsis):
from pypy.module._cffi_backend import ctypefunc
#
- unique_cache = space.fromcache(UniqueCache)
- unique_key = (fargs, w_fresult, ellipsis)
- try:
- return unique_cache.functions[unique_key]
- except KeyError:
- pass
- #
- if ((w_fresult.size < 0 and
- not isinstance(w_fresult, ctypevoid.W_CTypeVoid))
- or isinstance(w_fresult, ctypearray.W_CTypeArray)):
- if (isinstance(w_fresult, ctypestruct.W_CTypeStructOrUnion) and
- w_fresult.size < 0):
+ if ((fresult.size < 0 and
+ not isinstance(fresult, ctypevoid.W_CTypeVoid))
+ or isinstance(fresult, ctypearray.W_CTypeArray)):
+ if (isinstance(fresult, ctypestruct.W_CTypeStructOrUnion) and
+ fresult.size < 0):
raise oefmt(space.w_TypeError,
- "result type '%s' is opaque", w_fresult.name)
+ "result type '%s' is opaque", fresult.name)
else:
raise oefmt(space.w_TypeError,
- "invalid result type: '%s'", w_fresult.name)
+ "invalid result type: '%s'", fresult.name)
#
- fct = ctypefunc.W_CTypeFunc(space, fargs, w_fresult, ellipsis)
- unique_cache.functions[unique_key] = fct
+ fct = ctypefunc.W_CTypeFunc(space, fargs, fresult, ellipsis)
+ unique_cache = space.fromcache(UniqueCache)
+ func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis)
+ for weakdict in unique_cache.functions:
+ if weakdict.get(func_hash) is None:
+ weakdict.set(func_hash, fct)
+ break
+ else:
+ weakdict = rweakref.RWeakValueDictionary(int, ctypefunc.W_CTypeFunc)
+ unique_cache.functions.append(weakdict)
+ weakdict.set(func_hash, fct)
return fct
diff --git a/pypy/module/_cffi_backend/test/test_c.py b/pypy/module/_cffi_backend/test/test_c.py
--- a/pypy/module/_cffi_backend/test/test_c.py
+++ b/pypy/module/_cffi_backend/test/test_c.py
@@ -22,7 +22,7 @@
from rpython.tool.udir import udir
from pypy.interpreter import gateway
from pypy.module._cffi_backend import Module
-from pypy.module._cffi_backend.newtype import _clean_cache
+from pypy.module._cffi_backend.newtype import _clean_cache, UniqueCache
from rpython.translator import cdir
from rpython.translator.platform import host
from rpython.translator.tool.cbuild import ExternalCompilationInfo
@@ -86,8 +86,10 @@
_all_test_c.find_and_load_library = func
_all_test_c._testfunc = testfunc
""")
+ UniqueCache.for_testing = True
def teardown_method(self, method):
+ UniqueCache.for_testing = False
_clean_cache(self.space)
More information about the pypy-commit
mailing list