-
-
Notifications
You must be signed in to change notification settings - Fork 553
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from dimpase/35084_update
rebase patches for cython 0.29.33
- Loading branch information
Showing
2 changed files
with
215 additions
and
140 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,32 @@ | ||
See https://github.com/cython/cython/pull/2842 | ||
From 452076a52040a789a4b010a6d8d6abda56d060b4 Mon Sep 17 00:00:00 2001 | ||
From: Jeroen Demeyer <[email protected]> | ||
Date: Thu, 14 Feb 2019 10:02:41 +0100 | ||
Subject: [PATCH] @cython.trashcan directive to enable the Python trashcan for | ||
deallocations | ||
|
||
and https://github.com/cython/cython/pull/4475 | ||
|
||
commit c47c4ef735c4b7f1863b21bbe6f112b06c4aad05 | ||
Author: Jeroen Demeyer <[email protected]> | ||
Date: Thu Feb 14 10:02:41 2019 +0100 | ||
|
||
@cython.trashcan directive to enable the Python trashcan for deallocations | ||
--- | ||
Cython/Compiler/ModuleNode.py | 10 +++ | ||
Cython/Compiler/Options.py | 4 +- | ||
Cython/Compiler/PyrexTypes.py | 8 +- | ||
Cython/Compiler/Symtab.py | 18 +++- | ||
Cython/Utility/ExtensionTypes.c | 43 ++++++++++ | ||
tests/run/trashcan.pyx | 148 ++++++++++++++++++++++++++++++++ | ||
6 files changed, 228 insertions(+), 3 deletions(-) | ||
create mode 100644 tests/run/trashcan.pyx | ||
|
||
diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py | ||
index d5742de..27fcad6 100644 | ||
index 56845330d..3a3e8a956 100644 | ||
--- a/Cython/Compiler/ModuleNode.py | ||
+++ b/Cython/Compiler/ModuleNode.py | ||
@@ -1426,6 +1426,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): | ||
@@ -1443,6 +1443,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): | ||
|
||
is_final_type = scope.parent_type.is_final_type | ||
needs_gc = scope.needs_gc() | ||
+ needs_trashcan = scope.needs_trashcan() | ||
|
||
weakref_slot = scope.lookup_here("__weakref__") if not scope.is_closure_class_scope else None | ||
if weakref_slot not in scope.var_entries: | ||
@@ -1464,6 +1465,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): | ||
@@ -1481,6 +1482,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): | ||
# running this destructor. | ||
code.putln("PyObject_GC_UnTrack(o);") | ||
|
||
|
@@ -32,7 +38,7 @@ index d5742de..27fcad6 100644 | |
# call the user's __dealloc__ | ||
self.generate_usr_dealloc_call(scope, code) | ||
|
||
@@ -1537,6 +1543,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): | ||
@@ -1554,6 +1560,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): | ||
code.putln("(*Py_TYPE(o)->tp_free)(o);") | ||
if freelist_size: | ||
code.putln("}") | ||
|
@@ -44,18 +50,20 @@ index d5742de..27fcad6 100644 | |
"}") | ||
|
||
diff --git a/Cython/Compiler/Options.py b/Cython/Compiler/Options.py | ||
index d859c19..19d96f1 100644 | ||
index d03119fca..539629926 100644 | ||
--- a/Cython/Compiler/Options.py | ||
+++ b/Cython/Compiler/Options.py | ||
@@ -313,6 +313,7 @@ directive_types = { | ||
@@ -319,7 +319,8 @@ directive_types = { | ||
'freelist': int, | ||
'c_string_type': one_of('bytes', 'bytearray', 'str', 'unicode'), | ||
'c_string_encoding': normalise_encoding_name, | ||
- 'cpow': bool | ||
+ 'cpow': bool, | ||
+ 'trashcan': bool, | ||
} | ||
|
||
for key, val in _directive_defaults.items(): | ||
@@ -355,6 +356,7 @@ directive_scopes = { # defaults to available everywhere | ||
@@ -362,6 +363,7 @@ directive_scopes = { # defaults to available everywhere | ||
'np_pythran': ('module',), | ||
'fast_gil': ('module',), | ||
'iterable_coroutine': ('module', 'function'), | ||
|
@@ -64,18 +72,18 @@ index d859c19..19d96f1 100644 | |
|
||
|
||
diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py | ||
index 3b572d6..f200c5f 100644 | ||
index c309bd04b..9231130b5 100644 | ||
--- a/Cython/Compiler/PyrexTypes.py | ||
+++ b/Cython/Compiler/PyrexTypes.py | ||
@@ -1136,6 +1136,7 @@ class PyObjectType(PyrexType): | ||
@@ -1129,6 +1129,7 @@ class PyObjectType(PyrexType): | ||
is_extern = False | ||
is_subclassed = False | ||
is_gc_simple = False | ||
+ builtin_trashcan = False # builtin type using trashcan | ||
|
||
def __str__(self): | ||
return "Python object" | ||
@@ -1190,10 +1191,14 @@ class PyObjectType(PyrexType): | ||
@@ -1183,10 +1184,14 @@ class PyObjectType(PyrexType): | ||
|
||
|
||
builtin_types_that_cannot_create_refcycles = set([ | ||
|
@@ -91,7 +99,7 @@ index 3b572d6..f200c5f 100644 | |
|
||
class BuiltinObjectType(PyObjectType): | ||
# objstruct_cname string Name of PyObject struct | ||
@@ -1218,6 +1223,7 @@ class BuiltinObjectType(PyObjectType): | ||
@@ -1211,6 +1216,7 @@ class BuiltinObjectType(PyObjectType): | ||
self.typeptr_cname = "(&%s)" % cname | ||
self.objstruct_cname = objstruct_cname | ||
self.is_gc_simple = name in builtin_types_that_cannot_create_refcycles | ||
|
@@ -100,10 +108,19 @@ index 3b572d6..f200c5f 100644 | |
# Special case the type type, as many C API calls (and other | ||
# libraries) actually expect a PyTypeObject* for type arguments. | ||
diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py | ||
index f7443cb..d44484d 100644 | ||
index 7361a55ae..f0c311ba6 100644 | ||
--- a/Cython/Compiler/Symtab.py | ||
+++ b/Cython/Compiler/Symtab.py | ||
@@ -2085,6 +2085,22 @@ class CClassScope(ClassScope): | ||
@@ -2043,7 +2043,7 @@ class PyClassScope(ClassScope): | ||
class CClassScope(ClassScope): | ||
# Namespace of an extension type. | ||
# | ||
- # parent_type CClassType | ||
+ # parent_type PyExtensionType | ||
# #typeobj_cname string or None | ||
# #objstruct_cname string | ||
# method_table_cname string | ||
@@ -2087,6 +2087,22 @@ class CClassScope(ClassScope): | ||
return not self.parent_type.is_gc_simple | ||
return False | ||
|
||
|
@@ -127,10 +144,10 @@ index f7443cb..d44484d 100644 | |
""" | ||
Do we need to generate an implementation for the tp_clear slot? Can | ||
diff --git a/Cython/Utility/ExtensionTypes.c b/Cython/Utility/ExtensionTypes.c | ||
index 50d0e21..ca2adbe 100644 | ||
index dc187ab49..f359165df 100644 | ||
--- a/Cython/Utility/ExtensionTypes.c | ||
+++ b/Cython/Utility/ExtensionTypes.c | ||
@@ -74,6 +74,54 @@ static int __Pyx_PyType_Ready(PyTypeObject *t) { | ||
@@ -119,6 +119,49 @@ static int __Pyx_PyType_Ready(PyTypeObject *t) { | ||
return r; | ||
} | ||
|
||
|
@@ -142,12 +159,7 @@ index 50d0e21..ca2adbe 100644 | |
+ | ||
+// This requires CPython version >= 2.7.4 | ||
+// (or >= 3.2.4 but we don't support such old Python 3 versions anyway) | ||
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03080000 | ||
+// https://github.com/python/cpython/pull/11841 merged so Cython reimplementation | ||
+// is no longer necessary | ||
+#define __Pyx_TRASHCAN_BEGIN Py_TRASHCAN_BEGIN | ||
+#define __Pyx_TRASHCAN_END Py_TRASHCAN_END | ||
+#elif CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x02070400 | ||
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x02070400 | ||
+#define __Pyx_TRASHCAN_BEGIN_CONDITION(op, cond) \ | ||
+ do { \ | ||
+ PyThreadState *_tstate = NULL; \ | ||
|
@@ -185,3 +197,160 @@ index 50d0e21..ca2adbe 100644 | |
/////////////// CallNextTpDealloc.proto /////////////// | ||
|
||
static void __Pyx_call_next_tp_dealloc(PyObject* obj, destructor current_tp_dealloc); | ||
diff --git a/tests/run/trashcan.pyx b/tests/run/trashcan.pyx | ||
new file mode 100644 | ||
index 000000000..93a501ff8 | ||
--- /dev/null | ||
+++ b/tests/run/trashcan.pyx | ||
@@ -0,0 +1,148 @@ | ||
+# mode: run | ||
+ | ||
+cimport cython | ||
+ | ||
+ | ||
+# Count number of times an object was deallocated twice. This should remain 0. | ||
+cdef int double_deallocations = 0 | ||
+def assert_no_double_deallocations(): | ||
+ global double_deallocations | ||
+ err = double_deallocations | ||
+ double_deallocations = 0 | ||
+ assert not err | ||
+ | ||
+ | ||
+# Compute x = f(f(f(...(None)...))) nested n times and throw away the result. | ||
+# The real test happens when exiting this function: then a big recursive | ||
+# deallocation of x happens. We are testing two things in the tests below: | ||
+# that Python does not crash and that no double deallocation happens. | ||
+# See also https://github.com/python/cpython/pull/11841 | ||
+def recursion_test(f, int n=2**20): | ||
+ x = None | ||
+ cdef int i | ||
+ for i in range(n): | ||
+ x = f(x) | ||
+ | ||
+ | ||
+@cython.trashcan(True) | ||
+cdef class Recurse: | ||
+ """ | ||
+ >>> recursion_test(Recurse) | ||
+ >>> assert_no_double_deallocations() | ||
+ """ | ||
+ cdef public attr | ||
+ cdef int deallocated | ||
+ | ||
+ def __init__(self, x): | ||
+ self.attr = x | ||
+ | ||
+ def __dealloc__(self): | ||
+ # Check that we're not being deallocated twice | ||
+ global double_deallocations | ||
+ double_deallocations += self.deallocated | ||
+ self.deallocated = 1 | ||
+ | ||
+ | ||
+cdef class RecurseSub(Recurse): | ||
+ """ | ||
+ >>> recursion_test(RecurseSub) | ||
+ >>> assert_no_double_deallocations() | ||
+ """ | ||
+ cdef int subdeallocated | ||
+ | ||
+ def __dealloc__(self): | ||
+ # Check that we're not being deallocated twice | ||
+ global double_deallocations | ||
+ double_deallocations += self.subdeallocated | ||
+ self.subdeallocated = 1 | ||
+ | ||
+ | ||
+@cython.freelist(4) | ||
+@cython.trashcan(True) | ||
+cdef class RecurseFreelist: | ||
+ """ | ||
+ >>> recursion_test(RecurseFreelist) | ||
+ >>> recursion_test(RecurseFreelist, 1000) | ||
+ >>> assert_no_double_deallocations() | ||
+ """ | ||
+ cdef public attr | ||
+ cdef int deallocated | ||
+ | ||
+ def __init__(self, x): | ||
+ self.attr = x | ||
+ | ||
+ def __dealloc__(self): | ||
+ # Check that we're not being deallocated twice | ||
+ global double_deallocations | ||
+ double_deallocations += self.deallocated | ||
+ self.deallocated = 1 | ||
+ | ||
+ | ||
+# Subclass of list => uses trashcan by default | ||
+# As long as https://github.com/python/cpython/pull/11841 is not fixed, | ||
+# this does lead to double deallocations, so we skip that check. | ||
+cdef class RecurseList(list): | ||
+ """ | ||
+ >>> RecurseList(42) | ||
+ [42] | ||
+ >>> recursion_test(RecurseList) | ||
+ """ | ||
+ def __init__(self, x): | ||
+ super().__init__((x,)) | ||
+ | ||
+ | ||
+# Some tests where the trashcan is NOT used. When the trashcan is not used | ||
+# in a big recursive deallocation, the __dealloc__s of the base classs are | ||
+# only run after the __dealloc__s of the subclasses. | ||
+# We use this to detect trashcan usage. | ||
+cdef int base_deallocated = 0 | ||
+cdef int trashcan_used = 0 | ||
+def assert_no_trashcan_used(): | ||
+ global base_deallocated, trashcan_used | ||
+ err = trashcan_used | ||
+ trashcan_used = base_deallocated = 0 | ||
+ assert not err | ||
+ | ||
+ | ||
+cdef class Base: | ||
+ def __dealloc__(self): | ||
+ global base_deallocated | ||
+ base_deallocated = 1 | ||
+ | ||
+ | ||
+# Trashcan disabled by default | ||
+cdef class Sub1(Base): | ||
+ """ | ||
+ >>> recursion_test(Sub1, 100) | ||
+ >>> assert_no_trashcan_used() | ||
+ """ | ||
+ cdef public attr | ||
+ | ||
+ def __init__(self, x): | ||
+ self.attr = x | ||
+ | ||
+ def __dealloc__(self): | ||
+ global base_deallocated, trashcan_used | ||
+ trashcan_used += base_deallocated | ||
+ | ||
+ | ||
+@cython.trashcan(True) | ||
+cdef class Middle(Base): | ||
+ cdef public foo | ||
+ | ||
+ | ||
+# Trashcan disabled explicitly | ||
+@cython.trashcan(False) | ||
+cdef class Sub2(Middle): | ||
+ """ | ||
+ >>> recursion_test(Sub2, 1000) | ||
+ >>> assert_no_trashcan_used() | ||
+ """ | ||
+ cdef public attr | ||
+ | ||
+ def __init__(self, x): | ||
+ self.attr = x | ||
+ | ||
+ def __dealloc__(self): | ||
+ global base_deallocated, trashcan_used | ||
+ trashcan_used += base_deallocated | ||
-- | ||
2.37.1 (Apple Git-137.1) | ||
|
Oops, something went wrong.