Skip to content

Commit 3009359

Browse files
committed
Use an object for Gen.next but skip it in tp_clear
1 parent c3893a7 commit 3009359

File tree

3 files changed

+36
-30
lines changed

3 files changed

+36
-30
lines changed

cypari2/gen.pxd

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,12 @@ cdef class Gen(Gen_base):
2929

3030
# The Gen objects on the PARI stack form a linked list, from the
3131
# bottom to the top of the stack. This makes sense since we can only
32-
# deallocate a Gen which is on the bottom of the PARI stack.
32+
# deallocate a Gen which is on the bottom of the PARI stack. If this
33+
# is the last object on the stack, then next = top_of_stack
34+
# (a singleton object).
3335
#
34-
# Link to the next (up the PARI stack) Gen object. This is manually
35-
# refcounted (not by Cython) because we need complete control over
36-
# it. If this is the last object on the stack, then
37-
# next = top_of_stack (a singleton object). In the clone and constant
38-
# cases, this is NULL.
39-
cdef PyObject* next
36+
# In the clone and constant cases, this is None.
37+
cdef Gen next
4038

4139
# A cache for __getitem__. Initially, this is None but it will be
4240
# turned into a dict when needed.

cypari2/gen.pyx

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,10 @@ AUTHORS:
5757

5858
from __future__ import absolute_import, division, print_function
5959

60-
import types
6160
cimport cython
6261

63-
from cpython.int cimport PyInt_Check
64-
from cpython.long cimport PyLong_Check
65-
from cpython.bytes cimport PyBytes_Check
66-
from cpython.unicode cimport PyUnicode_Check
67-
from cpython.float cimport PyFloat_AS_DOUBLE
68-
from cpython.complex cimport PyComplex_RealAsDouble, PyComplex_ImagAsDouble
69-
from cpython.object cimport Py_EQ, Py_NE, Py_LE, Py_GE, Py_LT, Py_GT
62+
from cpython.object cimport (Py_EQ, Py_NE, Py_LE, Py_GE, Py_LT, Py_GT,
63+
PyTypeObject)
7064

7165
from cysignals.memory cimport sig_free, check_malloc
7266
from cysignals.signals cimport sig_check, sig_on, sig_off, sig_block, sig_unblock
@@ -158,7 +152,7 @@ cdef class Gen(Gen_base):
158152
raise RuntimeError("PARI objects cannot be instantiated directly; use pari(x) to convert x to PARI")
159153

160154
def __dealloc__(self):
161-
if self.next is not NULL:
155+
if self.next is not None:
162156
# stack
163157
remove_from_pari_stack(self)
164158
elif self.address is not NULL:
@@ -194,7 +188,7 @@ cdef class Gen(Gen_base):
194188
>>> pari("[[1, 2], 3]")[0][1] # indirect doctest
195189
2
196190
"""
197-
if self.next is not NULL:
191+
if self.next is not None:
198192
raise TypeError("cannot create reference to PARI stack (call fixGEN() first)")
199193
if is_on_stack(g):
200194
raise ValueError("new_ref() called with GEN which does not belong to parent")
@@ -208,7 +202,7 @@ cdef class Gen(Gen_base):
208202
Return the PARI ``GEN`` corresponding to ``self`` which is
209203
guaranteed not to change.
210204
"""
211-
if self.next is not NULL:
205+
if self.next is not None:
212206
move_gens_to_heap(self.sp())
213207
return self.g
214208

@@ -4554,6 +4548,20 @@ cdef class Gen(Gen_base):
45544548
raise NotImplementedError("the method allocatemem() should not be used; use pari.allocatemem() instead")
45554549

45564550

4551+
cdef int Gen_clear(self) except -1:
4552+
"""
4553+
Implementation of tp_clear() for Gen. We need to override Cython's
4554+
default since we do not want self.next to be cleared: it is crucial
4555+
that the next Gen stays alive until remove_from_pari_stack(self) is
4556+
called by __dealloc__.
4557+
"""
4558+
# Only itemcache needs to be cleared
4559+
(<Gen>self).itemcache = None
4560+
4561+
4562+
(<PyTypeObject*>Gen).tp_clear = Gen_clear
4563+
4564+
45574565
@cython.boundscheck(False)
45584566
@cython.wraparound(False)
45594567
cdef Gen list_of_Gens_to_Gen(list s):

cypari2/stack.pyx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,30 +59,30 @@ cdef void remove_from_pari_stack(Gen self):
5959
else:
6060
warn(f"cypari2 leaked {self.sp() - avma} bytes on the PARI stack",
6161
RuntimeWarning, stacklevel=2)
62-
stackbottom = n = self.next
63-
self.next = NULL
62+
n = self.next
63+
stackbottom = <PyObject*>n
64+
self.next = None
6465
reset_avma()
65-
Py_XDECREF(n)
6666

6767

6868
cdef inline Gen Gen_stack_new(GEN x):
6969
"""
7070
Allocate and initialize a new instance of ``Gen`` wrapping
7171
a GEN on the PARI stack.
7272
"""
73-
# INCREF(stackbottom) must be done BEFORE calling Gen_new
73+
global stackbottom
74+
# n = <Gen>stackbottom must be done BEFORE calling Gen_new()
7475
# since Gen_new may invoke gc.collect() which would mess up
7576
# the PARI stack.
76-
global stackbottom
77-
Py_XINCREF(stackbottom)
77+
n = <Gen>stackbottom
7878
z = Gen_new(x, <GEN>avma)
79-
z.next = stackbottom
79+
z.next = n
8080
stackbottom = <PyObject*>z
81-
if z.next is not <PyObject*>top_of_stack:
82-
s0 = z.sp()
83-
s1 = (<Gen>z.next).sp()
84-
if s0 > s1:
85-
raise SystemError(f"objects on PARI stack in invalid order (first: 0x{s0:x}; next: 0x{s1:x})")
81+
if n is not top_of_stack:
82+
sz = z.sp()
83+
sn = n.sp()
84+
if sz > sn:
85+
raise SystemError(f"objects on PARI stack in invalid order (first: 0x{sz:x}; next: 0x{sn:x})")
8686
return z
8787

8888

0 commit comments

Comments
 (0)