Author: Armin Rigo <[email protected]>
Branch: share-cpyext-cpython-api
Changeset: r84012:29ffb56dd3c7
Date: 2016-04-28 17:20 +0200
http://bitbucket.org/pypy/pypy/changeset/29ffb56dd3c7/
Log: First try
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -10,6 +10,7 @@
from rpython.rtyper.lltypesystem import ll2ctypes
from rpython.rtyper.annlowlevel import llhelper
from rpython.rlib.objectmodel import we_are_translated, keepalive_until_here
+from rpython.rlib.objectmodel import dont_inline
from rpython.translator import cdir
from rpython.translator.tool.cbuild import ExternalCompilationInfo
from rpython.translator.gensupp import NameManager
@@ -668,37 +669,129 @@
pypy_debug_catch_fatal_exception =
rffi.llexternal('pypy_debug_catch_fatal_exception', [], lltype.Void)
+
+# ____________________________________________________________
+
+
+class WrapperCache(object):
+ def __init__(self, space):
+ self.space = space
+ self.wrapper_gens = {} # {signature: WrapperGen()}
+ self.callable2name = {}
+ self.stats = [0, 0]
+
+class WrapperGen(object):
+ def __init__(self, space, signature):
+ self.space = space
+ self.callable2name = {}
+ self.wrapper_second_level = make_wrapper_second_level(
+ self.space, self.callable2name, *signature)
+
+ def make_wrapper(self, callable):
+ self.callable2name[callable] = callable.__name__
+ wrapper_second_level = self.wrapper_second_level
+
+ def wrapper(*args):
+ # no GC here, not even any GC object
+ args += (callable,)
+ return wrapper_second_level(*args)
+
+ wrapper.__name__ = "wrapper for %r" % (callable, )
+ return wrapper
+
+
# Make the wrapper for the cases (1) and (2)
def make_wrapper(space, callable, gil=None):
"NOT_RPYTHON"
+ # This logic is obscure, because we try to avoid creating one
+ # big wrapper() function for every callable. Instead we create
+ # only one per "signature".
+
+ argnames = callable.api_func.argnames
+ argtypesw = zip(callable.api_func.argtypes,
+ [_name.startswith("w_") for _name in argnames])
+ error_value = callable.api_func.error_value
+ if (isinstance(callable.api_func.restype, lltype.Ptr)
+ and error_value is not CANNOT_FAIL):
+ assert lltype.typeOf(error_value) == callable.api_func.restype
+ assert not error_value # only support error=NULL
+ error_value = 0 # because NULL is not hashable
+
+ signature = (tuple(argtypesw),
+ callable.api_func.restype,
+ callable.api_func.result_borrowed,
+ error_value,
+ gil)
+
+ cache = space.fromcache(WrapperCache)
+ cache.stats[1] += 1
+ try:
+ wrapper_gen = cache.wrapper_gens[signature]
+ except KeyError:
+ print signature
+ wrapper_gen = cache.wrapper_gens[signature] = WrapperGen(space,
+ signature)
+ cache.stats[0] += 1
+ print 'Wrapper cache [wrappers/total]:', cache.stats
+ return wrapper_gen.make_wrapper(callable)
+
+
+@dont_inline
+def deadlock_error(funcname):
+ fatalerror_notb("GIL deadlock detected when a CPython C extension "
+ "module calls %r" % (funcname,))
+
+@dont_inline
+def no_gil_error(funcname):
+ fatalerror_notb("GIL not held when a CPython C extension "
+ "module calls %r" % (funcname,))
+
+@dont_inline
+def unexpected_exception(funcname, e, tb):
+ print 'Fatal error in cpyext, CPython compatibility layer,
calling',funcname
+ print 'Either report a bug or consider not using this particular extension'
+ if not we_are_translated():
+ if tb is None:
+ tb = sys.exc_info()[2]
+ import traceback
+ traceback.print_exc()
+ if sys.stdout == sys.__stdout__:
+ import pdb; pdb.post_mortem(tb)
+ # we can't do much here, since we're in ctypes, swallow
+ else:
+ print str(e)
+ pypy_debug_catch_fatal_exception()
+ assert False
+
+def make_wrapper_second_level(space, callable2name, argtypesw, restype,
+ result_borrowed, error_value, gil):
from rpython.rlib import rgil
- names = callable.api_func.argnames
- argtypes_enum_ui =
unrolling_iterable(enumerate(zip(callable.api_func.argtypes,
- [name.startswith("w_") for name in names])))
- fatal_value = callable.api_func.restype._defl()
+ argtypes_enum_ui = unrolling_iterable(enumerate(argtypesw))
+ fatal_value = restype._defl()
gil_acquire = (gil == "acquire" or gil == "around")
gil_release = (gil == "release" or gil == "around")
pygilstate_ensure = (gil == "pygilstate_ensure")
pygilstate_release = (gil == "pygilstate_release")
assert (gil is None or gil_acquire or gil_release
or pygilstate_ensure or pygilstate_release)
- deadlock_error = ("GIL deadlock detected when a CPython C extension "
- "module calls %r" % (callable.__name__,))
- no_gil_error = ("GIL not held when a CPython C extension "
- "module calls %r" % (callable.__name__,))
+ expected_nb_args = len(argtypesw) + pygilstate_ensure
- @specialize.ll()
- def wrapper(*args):
+ if isinstance(restype, lltype.Ptr) and error_value == 0:
+ error_value = lltype.nullptr(restype.TO)
+
+ def wrapper_second_level(*args):
from pypy.module.cpyext.pyobject import make_ref, from_ref, is_pyobj
from pypy.module.cpyext.pyobject import as_pyobj
# we hope that malloc removal removes the newtuple() that is
# inserted exactly here by the varargs specializer
+ callable = args[-1]
+ args = args[:len(args)-1]
# see "Handling of the GIL" above (careful, we don't have the GIL here)
tid = rthread.get_or_make_ident()
if gil_acquire:
if cpyext_glob_tid_ptr[0] == tid:
- fatalerror_notb(deadlock_error)
+ deadlock_error(callable2name[callable])
rgil.acquire()
assert cpyext_glob_tid_ptr[0] == 0
elif pygilstate_ensure:
@@ -711,7 +804,7 @@
args += (pystate.PyGILState_UNLOCKED,)
else:
if cpyext_glob_tid_ptr[0] != tid:
- fatalerror_notb(no_gil_error)
+ no_gil_error(callable2name[callable])
cpyext_glob_tid_ptr[0] = 0
rffi.stackcounter.stacks_counter += 1
@@ -722,8 +815,7 @@
try:
if not we_are_translated() and DEBUG_WRAPPER:
print >>sys.stderr, callable,
- assert len(args) == (len(callable.api_func.argtypes) +
- pygilstate_ensure)
+ assert len(args) == expected_nb_args
for i, (typ, is_wrapped) in argtypes_enum_ui:
arg = args[i]
if is_PyObject(typ) and is_wrapped:
@@ -757,41 +849,28 @@
failed = False
if failed:
- error_value = callable.api_func.error_value
if error_value is CANNOT_FAIL:
raise SystemError("The function '%s' was not supposed to
fail"
% (callable.__name__,))
retval = error_value
- elif is_PyObject(callable.api_func.restype):
+ elif is_PyObject(restype):
if is_pyobj(result):
retval = result
else:
if result is not None:
- if callable.api_func.result_borrowed:
+ if result_borrowed:
retval = as_pyobj(space, result)
else:
retval = make_ref(space, result)
- retval = rffi.cast(callable.api_func.restype, retval)
+ retval = rffi.cast(restype, retval)
else:
retval = lltype.nullptr(PyObject.TO)
- elif callable.api_func.restype is not lltype.Void:
- retval = rffi.cast(callable.api_func.restype, result)
+ elif restype is not lltype.Void:
+ retval = rffi.cast(restype, result)
except Exception, e:
- print 'Fatal error in cpyext, CPython compatibility layer,
calling', callable.__name__
- print 'Either report a bug or consider not using this particular
extension'
- if not we_are_translated():
- if tb is None:
- tb = sys.exc_info()[2]
- import traceback
- traceback.print_exc()
- if sys.stdout == sys.__stdout__:
- import pdb; pdb.post_mortem(tb)
- # we can't do much here, since we're in ctypes, swallow
- else:
- print str(e)
- pypy_debug_catch_fatal_exception()
- assert False
+ unexpected_exception(callable2name[callable], e, tb)
+
rffi.stackcounter.stacks_counter -= 1
# see "Handling of the GIL" above
@@ -808,9 +887,9 @@
cpyext_glob_tid_ptr[0] = tid
return retval
- callable._always_inline_ = 'try'
- wrapper.__name__ = "wrapper for %r" % (callable, )
- return wrapper
+
+ wrapper_second_level._dont_inline_ = True
+ return wrapper_second_level
def process_va_name(name):
return name.replace('*', '_star')
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit