[pypy-commit] pypy cpyext-gc-trialdeletion: Added tests

stevie_92 pypy.commits at gmail.com
Thu Aug 31 05:40:10 EDT 2017


Author: Stefan Beyer <home at sbeyer.at>
Branch: cpyext-gc-trialdeletion
Changeset: r92284:a478bda34d52
Date: 2017-08-31 11:38 +0200
http://bitbucket.org/pypy/pypy/changeset/a478bda34d52/

Log:	Added tests Fixed bug in generic_cpy_call if called recursively
	Fixed bug in cycle detection if object is buffered twice

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
@@ -200,10 +200,11 @@
 # executes.  In non-cpyext-related code, it will thus always be 0.
 #
 # **make_generic_cpy_call():** RPython to C, with the GIL held.  Before
-# the call, must assert that the global variable is 0 and set the
-# current thread identifier into the global variable.  After the call,
-# assert that the global variable still contains the current thread id,
-# and reset it to 0.
+# the call, must assert that the global variable is 0 or the current
+# thread identifier (recursive call) and set the current thread identifier
+# into the global variable.  After the call, assert that the global variable
+# still contains the current thread id, and reset it to the value it held
+# before the call.
 #
 # **make_wrapper():** C to RPython; by default assume that the GIL is
 # held, but accepts gil="acquire", "release", "around",
@@ -1598,7 +1599,8 @@
 
         # see "Handling of the GIL" above
         tid = rthread.get_ident()
-        assert cpyext_glob_tid_ptr[0] == 0
+        tid_before = cpyext_glob_tid_ptr[0]
+        assert tid_before == 0 or tid_before == tid
         cpyext_glob_tid_ptr[0] = tid
 
         try:
@@ -1606,7 +1608,7 @@
             result = call_external_function(func, *boxed_args)
         finally:
             assert cpyext_glob_tid_ptr[0] == tid
-            cpyext_glob_tid_ptr[0] = 0
+            cpyext_glob_tid_ptr[0] = tid_before
             keepalive_until_here(*keepalives)
 
         if is_PyObject(RESULT_TYPE):
diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py
--- a/rpython/memory/gc/incminimark.py
+++ b/rpython/memory/gc/incminimark.py
@@ -3205,9 +3205,7 @@
         from rpython.rlib.rawrefcount import (REFCNT_CYCLE_BUFFERED,
                                               REFCNT_CLR_MASK,
                                               REFCNT_CLR_PURPLE,
-                                              REFCNT_MASK,
-                                              W_MARKER_DEALLOCATING,
-                                              mark_deallocating)
+                                              REFCNT_MASK)
         obj = self._pyobj(pyobject)
         rc = obj.c_ob_refcnt
         debug_print("_rrc_cycle_mark_roots", obj)
@@ -3218,8 +3216,8 @@
             obj.c_ob_refcnt = rc & ~REFCNT_CYCLE_BUFFERED
             self.rrc_buffered.remove(pyobject)
             if rc & REFCNT_MASK == 0:
-                mark_deallocating(W_MARKER_DEALLOCATING, obj)
-                generic_cpy_call(True, obj.c_ob_type.c_tp_dealloc, obj)
+                if obj.c_ob_type.c_tp_dealloc:
+                    generic_cpy_call(True, obj.c_ob_type.c_tp_dealloc, obj)
 
     def _rrc_cycle_scan_roots(self, pyobject, ignore):
         obj = self._pyobj(pyobject)
diff --git a/rpython/memory/gc/test/test_rawrefcount.py b/rpython/memory/gc/test/test_rawrefcount.py
--- a/rpython/memory/gc/test/test_rawrefcount.py
+++ b/rpython/memory/gc/test/test_rawrefcount.py
@@ -2,11 +2,15 @@
 from rpython.rtyper.lltypesystem import lltype, llmemory
 from rpython.memory.gc.incminimark import IncrementalMiniMarkGC
 from rpython.memory.gc.test.test_direct import BaseDirectGCTest
-from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
-from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+from rpython.rlib.rawrefcount import (REFCNT_FROM_PYPY, REFCNT_FROM_PYPY_LIGHT,
+                                      REFCNT_MASK)
 from pypy.module.cpyext.api import (PyObject, PyTypeObject, PyTypeObjectPtr,
                                     PyObjectFields, cpython_struct)
 from pypy.module.cpyext.complexobject import PyComplexObject
+from rpython.rtyper.lltypesystem import rffi
+from pypy.module.cpyext.typeobjectdefs import visitproc, traverseproc
+from rpython.rtyper.annlowlevel import llhelper
+from rpython.rtyper.tool import rffi_platform
 
 PYOBJ_HDR = IncrementalMiniMarkGC.PYOBJ_HDR
 PYOBJ_HDR_PTR = IncrementalMiniMarkGC.PYOBJ_HDR_PTR
@@ -17,6 +21,17 @@
                          ('prev', lltype.Ptr(S)),
                          ('next', lltype.Ptr(S))))
 
+T = lltype.Ptr(lltype.ForwardReference())
+T.TO.become(lltype.Struct('test',
+                          ('base', PyObject.TO),
+                          ('next', T),
+                          ('prev', T),
+                          ('value', lltype.Signed)))
+
+TRAVERSE_FUNCTYPE = rffi.CCallback([PyObject, visitproc, rffi.VOIDP],
+                                   rffi.INT_real)
+t1 = lltype.malloc(PyTypeObject, flavor='raw', immortal=True)
+
 
 class TestRawRefCount(BaseDirectGCTest):
     GCClass = IncrementalMiniMarkGC
@@ -86,47 +101,38 @@
         return p1, p1ref, r1, r1addr, check_alive
 
     def _rawrefcount_cycle_obj(self):
-        from pypy.module.cpyext.typeobjectdefs import visitproc, traverseproc
-        from rpython.rtyper.lltypesystem import rffi
-        from rpython.rtyper.annlowlevel import llhelper
-        from rpython.rlib.rawrefcount import (REFCNT_CLR_PURPLE)
-        from rpython.rtyper.tool import rffi_platform
-
-        self.gc.rawrefcount_init(lambda: self.trigger.append(1))
-
-        # construct test type
-        TEST_P = lltype.Ptr(lltype.ForwardReference())
-        TEST_P.TO.become(lltype.Struct('test',
-                                       ('base', PyObject.TO),
-                                       ('next', TEST_P),
-                                       ('value', lltype.Signed)))
 
         def test_tp_traverse(obj, visit, args):
-            from pypy.module.cpyext.api import generic_cpy_call
-            test = rffi.cast(TEST_P, obj)
+            test = rffi.cast(T, obj)
             vret = 0
-            if test.next is not None:
+            if llmemory.cast_ptr_to_adr(test.next).ptr is not None:
                 next = rffi.cast(PyObject, test.next)
                 vret = visit(next, args)
                 if vret != 0:
                     return vret
+            if llmemory.cast_ptr_to_adr(test.prev).ptr is not None:
+                next = rffi.cast(PyObject, test.prev)
+                vret = visit(next, args)
+                if vret != 0:
+                    return vret
             return vret
 
-        TRAVERSE_FUNCTYPE = rffi.CCallback([PyObject, visitproc, rffi.VOIDP],
-                                           rffi.INT_real)
         func_ptr = llhelper(TRAVERSE_FUNCTYPE, test_tp_traverse)
         rffi_func_ptr = rffi.cast(traverseproc, func_ptr)
-        t1 = lltype.malloc(PyTypeObject, flavor='raw', immortal=True)
         t1.c_tp_traverse = rffi_func_ptr
 
-        # initialize object
-        r1 = lltype.malloc(TEST_P.TO, flavor='raw', immortal=True)
-        r1.base.c_ob_refcnt = 1 | REFCNT_CLR_PURPLE
+        r1 = lltype.malloc(T.TO, flavor='raw', immortal=True)
         r1.base.c_ob_pypy_link = 0
         r1.base.c_ob_type = t1
-        r1addr = llmemory.cast_ptr_to_adr(r1)
+        r1.base.c_ob_refcnt = 1
+        return r1
 
-        return r1, r1addr
+    def _rawrefcount_buffer_obj(self, obj):
+        from rpython.rlib.rawrefcount import REFCNT_CLR_MASK, REFCNT_CLR_PURPLE
+        rc = obj.base.c_ob_refcnt
+        obj.base.c_ob_refcnt = rc & ~REFCNT_CLR_MASK | REFCNT_CLR_PURPLE
+        objaddr = llmemory.cast_ptr_to_adr(obj)
+        self.gc.rawrefcount_buffer_pyobj(objaddr)
 
     def test_rawrefcount_objects_basic(self, old=False):
         p1, p1ref, r1, r1addr, check_alive = (
@@ -338,16 +344,100 @@
         check_alive(0)
 
     def test_cycle_self_reference_free(self):
-        r1, r1addr = self._rawrefcount_cycle_obj()
+        self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+        r1 = self._rawrefcount_cycle_obj()
         r1.next = r1
-        self.gc.rawrefcount_buffer_pyobj(r1addr)
+        self._rawrefcount_buffer_obj(r1)
         self.gc.rrc_collect_cycles()
-        assert r1.base.c_ob_refcnt == 0
+        assert r1.base.c_ob_refcnt & REFCNT_MASK == 0
 
     def test_cycle_self_reference_not_free(self):
-        r1, r1addr = self._rawrefcount_cycle_obj()
+        self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+        r1 = self._rawrefcount_cycle_obj()
         r1.base.c_ob_refcnt += 1
         r1.next = r1
-        self.gc.rawrefcount_buffer_pyobj(r1addr)
+        self._rawrefcount_buffer_obj(r1)
         self.gc.rrc_collect_cycles()
-        assert r1.base.c_ob_refcnt == 2
+        assert r1.base.c_ob_refcnt & REFCNT_MASK == 2
+
+    def test_simple_cycle_free(self):
+        self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+        r1 = self._rawrefcount_cycle_obj()
+        r2 = self._rawrefcount_cycle_obj()
+        r1.next = r2
+        r2.next = r1
+        self._rawrefcount_buffer_obj(r1)
+        self.gc.rrc_collect_cycles()
+        assert r1.base.c_ob_refcnt & REFCNT_MASK == 0
+        assert r2.base.c_ob_refcnt & REFCNT_MASK == 0
+
+    def test_simple_cycle_not_free(self):
+        self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+        r1 = self._rawrefcount_cycle_obj()
+        r2 = self._rawrefcount_cycle_obj()
+        r1.next = r2
+        r2.next = r1
+        r2.base.c_ob_refcnt += 1
+        self._rawrefcount_buffer_obj(r1)
+        self.gc.rrc_collect_cycles()
+        assert r1.base.c_ob_refcnt & REFCNT_MASK == 1
+        assert r2.base.c_ob_refcnt & REFCNT_MASK == 2
+
+    def test_complex_cycle_free(self):
+        self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+        r1 = self._rawrefcount_cycle_obj()
+        r2 = self._rawrefcount_cycle_obj()
+        r3 = self._rawrefcount_cycle_obj()
+        r1.next = r2
+        r1.prev = r2
+        r2.base.c_ob_refcnt += 1
+        r2.next = r3
+        r3.prev = r1
+        self._rawrefcount_buffer_obj(r1)
+        self.gc.rrc_collect_cycles()
+        assert r1.base.c_ob_refcnt & REFCNT_MASK == 0
+        assert r2.base.c_ob_refcnt & REFCNT_MASK == 0
+        assert r3.base.c_ob_refcnt & REFCNT_MASK == 0
+
+    def test_complex_cycle_not_free(self):
+        self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+        r1 = self._rawrefcount_cycle_obj()
+        r2 = self._rawrefcount_cycle_obj()
+        r3 = self._rawrefcount_cycle_obj()
+        r1.next = r2
+        r1.prev = r2
+        r2.base.c_ob_refcnt += 1
+        r2.next = r3
+        r3.prev = r1
+        r3.base.c_ob_refcnt += 1
+        self._rawrefcount_buffer_obj(r1)
+        self.gc.rrc_collect_cycles()
+        assert r1.base.c_ob_refcnt & REFCNT_MASK == 1
+        assert r2.base.c_ob_refcnt & REFCNT_MASK == 2
+        assert r3.base.c_ob_refcnt & REFCNT_MASK == 2
+
+    def test_cycle_2_buffered_free(self):
+        self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+        r1 = self._rawrefcount_cycle_obj()
+        r2 = self._rawrefcount_cycle_obj()
+        r1.next = r2
+        r2.prev = r1
+        self._rawrefcount_buffer_obj(r1)
+        self._rawrefcount_buffer_obj(r2)
+        self.gc.rrc_collect_cycles()
+        assert r1.base.c_ob_refcnt & REFCNT_MASK == 0
+        assert r2.base.c_ob_refcnt & REFCNT_MASK == 0
+
+    def test_cycle_2_buffered_not_free(self):
+        self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+        r1 = self._rawrefcount_cycle_obj()
+        r2 = self._rawrefcount_cycle_obj()
+        r1.next = r2
+        r2.prev = r1
+        r1.base.c_ob_refcnt += 1
+        self._rawrefcount_buffer_obj(r1)
+        self._rawrefcount_buffer_obj(r2)
+        self.gc.rrc_collect_cycles()
+        assert r1.base.c_ob_refcnt & REFCNT_MASK == 2
+        assert r2.base.c_ob_refcnt & REFCNT_MASK == 1
+


More information about the pypy-commit mailing list