Author: Antonio Cuni <anto.c...@gmail.com>
Branch: gc-disable
Changeset: r94713:8315bb2ed1ef
Date: 2018-05-30 18:42 +0200
http://bitbucket.org/pypy/pypy/changeset/8315bb2ed1ef/

Log:    implement the app-level version of gc.collect_step()

diff --git a/pypy/module/gc/__init__.py b/pypy/module/gc/__init__.py
--- a/pypy/module/gc/__init__.py
+++ b/pypy/module/gc/__init__.py
@@ -4,6 +4,7 @@
 class Module(MixedModule):
     interpleveldefs = {
         'collect': 'interp_gc.collect',
+        'collect_step': 'interp_gc.collect_step',
         'enable': 'interp_gc.enable',
         'disable': 'interp_gc.disable',
         'isenabled': 'interp_gc.isenabled',
diff --git a/pypy/module/gc/interp_gc.py b/pypy/module/gc/interp_gc.py
--- a/pypy/module/gc/interp_gc.py
+++ b/pypy/module/gc/interp_gc.py
@@ -16,7 +16,9 @@
     cache.clear()
 
     rgc.collect()
+    _run_finalizers(space)
 
+def _run_finalizers(space):
     # if we are running in gc.disable() mode but gc.collect() is called,
     # we should still call the finalizers now.  We do this as an attempt
     # to get closer to CPython's behavior: in Py3.5 some tests
@@ -79,6 +81,43 @@
     if uda.pending_with_disabled_del is None:
         uda.pending_with_disabled_del = []
 
+
+class StepCollector(object):
+    """
+    Invoke rgc.collect_step() until we are done, then run the app-level
+    finalizers as a separate step
+    """
+
+    def __init__(self, space):
+        self.space = space
+        self.finalizing = False
+
+    def do(self):
+        if self.finalizing:
+            self._run_finalizers()
+            self.finalizing = False
+            return True # everything done
+        else:
+            done = self._collect_step()
+            if done:
+                self.finalizing = True
+            return False # still something to do
+
+    def _collect_step(self):
+        return rgc.collect_step()
+
+    def _run_finalizers(self):
+        _run_finalizers(self.space)
+
+def collect_step(space):
+    """
+    If the GC is incremental, run a single gc-collect-step. Return True when
+    the major collection is completed.
+    If the GC is not incremental, do a full collection and return True.
+    """
+    sc = space.fromcache(StepCollector)
+    return space.newbool(sc.do())
+
 # ____________________________________________________________
 
 @unwrap_spec(filename='fsencode')
diff --git a/pypy/module/gc/test/test_gc.py b/pypy/module/gc/test/test_gc.py
--- a/pypy/module/gc/test/test_gc.py
+++ b/pypy/module/gc/test/test_gc.py
@@ -3,6 +3,7 @@
 from rpython.rlib import rgc
 from pypy.interpreter.baseobjspace import ObjSpace
 from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.module.gc.interp_gc import StepCollector
 
 class AppTestGC(object):
 
@@ -99,6 +100,24 @@
         assert deleted == [1]
         gc.enable()
 
+    def test_gc_collect_step(self):
+        import gc
+
+        class X(object):
+            deleted = 0
+            def __del__(self):
+                X.deleted += 1
+
+        gc.disable()
+        X(); X(); X();
+        n = 0
+        while True:
+            n += 1
+            if gc.collect_step():
+                break
+
+        assert n >= 2 # at least one step + 1 finalizing
+        assert X.deleted == 3
 
 class AppTestGcDumpHeap(object):
     pytestmark = py.test.mark.xfail(run=False)
@@ -172,3 +191,36 @@
         gc.collect()    # the classes C should all go away here
         for r in rlist:
             assert r() is None
+
+
+def test_StepCollector():
+    class MyStepCollector(StepCollector):
+        my_steps = 0
+        my_done = False
+        my_finalized = 0
+
+        def _collect_step(self):
+            self.my_steps += 1
+            return self.my_done
+
+        def _run_finalizers(self):
+            self.my_finalized += 1
+
+    sc = MyStepCollector(space=None)
+    assert not sc.do()
+    assert sc.my_steps == 1
+    assert not sc.do()
+    assert sc.my_steps == 2
+    sc.my_done = True
+    assert not sc.do()
+    assert sc.my_steps == 3
+    assert sc.my_finalized == 0
+    assert sc.finalizing
+    assert sc.do()
+    assert sc.my_steps == 3
+    assert sc.my_finalized == 1
+    assert not sc.finalizing
+    assert not sc.do()
+    assert sc.my_steps == 4
+    assert sc.my_finalized == 1
+
diff --git a/rpython/memory/gc/base.py b/rpython/memory/gc/base.py
--- a/rpython/memory/gc/base.py
+++ b/rpython/memory/gc/base.py
@@ -160,6 +160,7 @@
         return True
 
     def collect_step(self):
+        self.collect()
         return True
 
     def malloc(self, typeid, length=0, zero=False):
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -21,8 +21,9 @@
     """
     If the GC is incremental, run a single gc-collect-step. Return True when
     the major collection is completed.
-    If the GC is not incremental, do nothing.
+    If the GC is not incremental, do a full collection and return True.
     """
+    gc.collect()
     return True
 
 def set_max_heap_size(nbytes):
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to