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