Author: Antonio Cuni <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit