cython

Форк
0
/
trashcan.pyx 
158 строк · 3.9 Кб
1
# mode: run
2

3
cimport cython
4

5
import sys
6

7
# The tests here are to do with a deterministic order of destructors which
8
# isn't reliable for PyPy. Therefore, on PyPy we treat the test as
9
# "compiles and doesn't crash"
10
IS_PYPY = hasattr(sys, 'pypy_version_info')
11

12
# Count number of times an object was deallocated twice. This should remain 0.
13
cdef int double_deallocations = 0
14
def assert_no_double_deallocations():
15
    if IS_PYPY:
16
        return
17
    global double_deallocations
18
    err = double_deallocations
19
    double_deallocations = 0
20
    assert not err
21

22

23
# Compute x = f(f(f(...(None)...))) nested n times and throw away the result.
24
# The real test happens when exiting this function: then a big recursive
25
# deallocation of x happens. We are testing two things in the tests below:
26
# that Python does not crash and that no double deallocation happens.
27
# See also https://github.com/python/cpython/pull/11841
28
def recursion_test(f, int n=2**20):
29
    x = None
30
    cdef int i
31
    for i in range(n):
32
        x = f(x)
33

34

35
@cython.trashcan(True)
36
cdef class Recurse:
37
    """
38
    >>> recursion_test(Recurse)
39
    >>> assert_no_double_deallocations()
40
    """
41
    cdef public attr
42
    cdef int deallocated
43

44
    def __cinit__(self, x):
45
        self.attr = x
46

47
    def __dealloc__(self):
48
        # Check that we're not being deallocated twice
49
        global double_deallocations
50
        double_deallocations += self.deallocated
51
        self.deallocated = 1
52

53

54
cdef class RecurseSub(Recurse):
55
    """
56
    >>> recursion_test(RecurseSub)
57
    >>> assert_no_double_deallocations()
58
    """
59
    cdef int subdeallocated
60

61
    def __dealloc__(self):
62
        # Check that we're not being deallocated twice
63
        global double_deallocations
64
        double_deallocations += self.subdeallocated
65
        self.subdeallocated = 1
66

67

68
@cython.freelist(4)
69
@cython.trashcan(True)
70
cdef class RecurseFreelist:
71
    """
72
    >>> recursion_test(RecurseFreelist)
73
    >>> recursion_test(RecurseFreelist, 1000)
74
    >>> assert_no_double_deallocations()
75
    """
76
    cdef public attr
77
    cdef int deallocated
78

79
    def __cinit__(self, x):
80
        self.attr = x
81

82
    def __dealloc__(self):
83
        # Check that we're not being deallocated twice
84
        global double_deallocations
85
        double_deallocations += self.deallocated
86
        self.deallocated = 1
87

88

89
# Subclass of list => uses trashcan by default
90
# As long as https://github.com/python/cpython/pull/11841 is not fixed,
91
# this does lead to double deallocations, so we skip that check.
92
cdef class RecurseList(list):
93
    """
94
    >>> RecurseList(42)
95
    [42]
96
    >>> recursion_test(RecurseList)
97
    """
98
    def __init__(self, x):
99
        super().__init__((x,))
100

101

102
# Some tests where the trashcan is NOT used. When the trashcan is not used
103
# in a big recursive deallocation, the __dealloc__s of the base classes are
104
# only run after the __dealloc__s of the subclasses.
105
# We use this to detect trashcan usage.
106
cdef int base_deallocated = 0
107
cdef int trashcan_used = 0
108
def assert_no_trashcan_used():
109
    if IS_PYPY:
110
        return
111
    global base_deallocated, trashcan_used
112
    err = trashcan_used
113
    trashcan_used = base_deallocated = 0
114
    assert not err
115

116

117
cdef class Base:
118
    def __dealloc__(self):
119
        global base_deallocated
120
        base_deallocated = 1
121

122

123
# Trashcan disabled by default
124
cdef class Sub1(Base):
125
    """
126
    >>> recursion_test(Sub1, 100)
127
    >>> assert_no_trashcan_used()
128
    """
129
    cdef public attr
130

131
    def __cinit__(self, x):
132
        self.attr = x
133

134
    def __dealloc__(self):
135
        global base_deallocated, trashcan_used
136
        trashcan_used += base_deallocated
137

138

139
@cython.trashcan(True)
140
cdef class Middle(Base):
141
    cdef public foo
142

143

144
# Trashcan disabled explicitly
145
@cython.trashcan(False)
146
cdef class Sub2(Middle):
147
    """
148
    >>> recursion_test(Sub2, 1000)
149
    >>> assert_no_trashcan_used()
150
    """
151
    cdef public attr
152

153
    def __cinit__(self, x):
154
        self.attr = x
155

156
    def __dealloc__(self):
157
        global base_deallocated, trashcan_used
158
        trashcan_used += base_deallocated
159

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.