Commit 232d3cab authored by Stefan Behnel's avatar Stefan Behnel

implement freelist support for extension types with a @cython.freelist(N) decorator

parent 74657f01
......@@ -1033,34 +1033,52 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
else:
unused_marker = 'CYTHON_UNUSED '
if base_type:
freelist_size = 0 # not currently supported
else:
freelist_size = scope.directives.get('freelist', 0)
freelist_name = scope.mangle_internal(Naming.freelist_name)
freecount_name = scope.mangle_internal(Naming.freecount_name)
decls = code.globalstate['decls']
decls.putln("static PyObject *%s(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/" %
slot_func)
code.putln("")
if freelist_size:
code.putln("static %s[%d];" % (
scope.parent_type.declaration_code(freelist_name),
freelist_size))
code.putln("static int %s = 0;" % freecount_name)
code.putln("")
code.putln(
"static PyObject *%s(PyTypeObject *t, %sPyObject *a, %sPyObject *k) {"
% (slot_func, unused_marker, unused_marker))
need_self_cast = type.vtabslot_cname or have_entries or cpp_class_attrs
if need_self_cast:
code.putln(
"%s;"
% scope.parent_type.declaration_code("p"))
code.putln("%s;" % scope.parent_type.declaration_code("p"))
if base_type:
tp_new = TypeSlots.get_base_slot_function(scope, tp_slot)
if tp_new is None:
tp_new = "%s->tp_new" % base_type.typeptr_cname
code.putln(
"PyObject *o = %s(t, a, k);" % tp_new)
code.putln("PyObject *o = %s(t, a, k);" % tp_new)
else:
code.putln(
"PyObject *o = (*t->tp_alloc)(t, 0);")
code.putln(
"if (!o) return 0;")
code.putln("PyObject *o;")
if freelist_size:
code.putln("if ((%s > 0) & (t == %s)) {" % (
freecount_name, type.typeptr_cname))
code.putln("o = (PyObject*)%s[--%s];" % (
freelist_name, freecount_name))
code.putln("PyObject_Init(o, t);")
if scope.needs_gc():
code.putln("PyObject_GC_Track(o);")
code.putln("} else {")
code.putln("o = (*t->tp_alloc)(t, 0);")
code.putln("if (!o) return 0;")
if freelist_size and not base_type:
code.putln('}')
if need_self_cast:
code.putln(
"p = %s;"
% type.cast_code("o"))
code.putln("p = %s;" % type.cast_code("o"))
#if need_self_cast:
# self.generate_self_cast(scope, code)
if type.vtabslot_cname:
......@@ -1184,8 +1202,20 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.globalstate.use_utility_code(
UtilityCode.load_cached("CallNextTpDealloc", "ExtensionTypes.c"))
else:
code.putln(
"(*Py_TYPE(o)->tp_free)(o);")
freelist_size = scope.directives.get('freelist', 0)
if freelist_size:
freelist_name = scope.mangle_internal(Naming.freelist_name)
freecount_name = scope.mangle_internal(Naming.freecount_name)
type = scope.parent_type
code.putln("if ((%s < %d) & (Py_TYPE(o) == %s)) {" % (
freecount_name, freelist_size, type.typeptr_cname))
code.putln("%s[%s++] = %s;" % (
freelist_name, freecount_name, type.cast_code("o")))
code.putln("} else {")
code.putln("(*Py_TYPE(o)->tp_free)(o);")
if freelist_size:
code.putln("}")
code.putln(
"}")
......@@ -2038,6 +2068,19 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
PyrexTypes.py_object_type,
clear_before_decref=True,
nanny=False)
for entry in env.c_class_entries:
cclass_type = entry.type
if cclass_type.base_type:
continue
if cclass_type.scope.directives.get('freelist', 0):
scope = cclass_type.scope
freelist_name = scope.mangle_internal(Naming.freelist_name)
freecount_name = scope.mangle_internal(Naming.freecount_name)
code.putln("while (%s > 0) {" % freecount_name)
code.putln("PyObject* o = (PyObject*)%s[--%s];" % (
freelist_name, freecount_name))
code.putln("(*Py_TYPE(o)->tp_free)(o);")
code.putln("}")
# for entry in env.pynum_entries:
# code.put_decref_clear(entry.cname,
# PyrexTypes.py_object_type,
......
......@@ -103,6 +103,8 @@ global_code_object_cache_find = pyrex_prefix + 'find_code_object'
global_code_object_cache_insert = pyrex_prefix + 'insert_code_object'
genexpr_id_ref = 'genexpr'
freelist_name = 'freelist'
freecount_name = 'freecount'
line_c_macro = "__LINE__"
......
......@@ -123,7 +123,8 @@ directive_defaults = {
# experimental, subject to change
'binding': None,
'experimental_cpp_class_def': False
'experimental_cpp_class_def': False,
'freelist': 0,
}
# Extra warning directives
......@@ -144,6 +145,7 @@ directive_types = {
'cclass' : None,
'returns' : type,
'set_initial_path': str,
'freelist': int,
}
for key, val in directive_defaults.items():
......@@ -160,6 +162,7 @@ directive_scopes = { # defaults to available everywhere
'set_initial_path' : ('module',),
'test_assert_path_exists' : ('function', 'class', 'cclass'),
'test_fail_if_path_exists' : ('function', 'class', 'cclass'),
'freelist': ('cclass',),
}
def parse_directive_value(name, value, relaxed_bool=False):
......
......@@ -883,6 +883,11 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
raise PostParseError(pos,
'The %s directive takes one compile-time boolean argument' % optname)
return (optname, args[0].value)
elif directivetype is int:
if kwds is not None or len(args) != 1 or not isinstance(args[0], ExprNodes.IntNode):
raise PostParseError(pos,
'The %s directive takes one compile-time integer argument' % optname)
return (optname, int(args[0].value))
elif directivetype is str:
if kwds is not None or len(args) != 1 or not isinstance(args[0], (ExprNodes.StringNode,
ExprNodes.UnicodeNode)):
......
cimport cython
@cython.freelist(8)
cdef class ExtTypeNoGC:
"""
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
"""
@cython.freelist(8)
cdef class ExtTypeWithGC:
"""
>>> obj = ExtTypeWithGC()
>>> obj = ExtTypeWithGC()
>>> obj = ExtTypeWithGC()
>>> obj = ExtTypeWithGC()
>>> obj = ExtTypeWithGC()
>>> obj = ExtTypeWithGC()
"""
cdef attribute
def __init__(self):
self.attribute = object()
def tpnew_ExtTypeWithGC():
"""
>>> obj = tpnew_ExtTypeWithGC()
>>> obj = tpnew_ExtTypeWithGC()
>>> obj = tpnew_ExtTypeWithGC()
>>> obj = tpnew_ExtTypeWithGC()
>>> obj = tpnew_ExtTypeWithGC()
>>> obj = tpnew_ExtTypeWithGC()
"""
return ExtTypeWithGC.__new__(ExtTypeWithGC)
@cython.freelist(8)
cdef class ExtTypeWithRefCycle:
"""
>>> obj = first = ExtTypeWithRefCycle()
>>> obj.attribute is None
True
>>> obj = ExtTypeWithRefCycle(obj)
>>> obj.attribute is first
True
>>> obj = ExtTypeWithRefCycle(obj)
>>> obj = ExtTypeWithRefCycle(obj)
>>> obj = ExtTypeWithRefCycle(obj)
>>> obj = ExtTypeWithRefCycle(obj)
>>> obj.attribute is not None
True
>>> first.attribute = obj
>>> del obj
"""
cdef public attribute
def __init__(self, obj=None):
self.attribute = obj
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment