1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
from pypy.interpreter.gateway import unwrap_spec
from pypy.interpreter.error import oefmt
from rpython.rlib import rgc
from pypy.module.gc.hook import W_GcCollectStepStats
@unwrap_spec(generation=int)
def collect(space, generation=0):
"Run a full collection. The optional argument is ignored."
# First clear the method and the map cache.
# See test_gc for an example of why.
from pypy.objspace.std.typeobject import MethodCache
from pypy.objspace.std.mapdict import MapAttrCache
cache = space.fromcache(MethodCache)
cache.clear()
cache = space.fromcache(MapAttrCache)
cache.clear()
rgc.collect()
_run_finalizers(space)
if space.config.objspace.usemodules.cpyext:
# perform dealloc callbacks now, instead of waiting for the next
# AsyncAction to fire. Like the _run_finalizers call, this ensures
# tp_dealloc will be called as part of gc.collect()
from pypy.module.cpyext.state import _rawrefcount_perform
_rawrefcount_perform(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
# specifically rely on that. This is similar to how, in CPython, an
# explicit gc.collect() will invoke finalizers from cycles and fully
# ignore the gc.disable() mode.
temp_reenable = not space.user_del_action.enabled_at_app_level
if temp_reenable:
enable_finalizers(space)
try:
# fetch the pending finalizers from the queue, where they are
# likely to have been added by rgc.collect() above, and actually
# run them now. This forces them to run before this function
# returns, and also always in the enable_finalizers() mode.
space.user_del_action._run_finalizers()
finally:
if temp_reenable:
disable_finalizers(space)
return space.newint(0)
def enable(space):
"""Non-recursive version. Enable major collections and finalizers.
If they were already enabled, no-op.
If they were disabled even several times, enable them anyway.
"""
rgc.enable()
if not space.user_del_action.enabled_at_app_level:
space.user_del_action.enabled_at_app_level = True
enable_finalizers(space)
def disable(space):
"""Non-recursive version. Disable major collections and finalizers.
Multiple calls to this function are ignored.
"""
rgc.disable()
if space.user_del_action.enabled_at_app_level:
space.user_del_action.enabled_at_app_level = False
disable_finalizers(space)
def isenabled(space):
return space.newbool(space.user_del_action.enabled_at_app_level)
def enable_finalizers(space):
uda = space.user_del_action
if uda.finalizers_lock_count == 0:
raise oefmt(space.w_ValueError, "finalizers are already enabled")
uda.finalizers_lock_count -= 1
if uda.finalizers_lock_count == 0:
pending = uda.pending_with_disabled_del
uda.pending_with_disabled_del = None
if pending is not None:
for i in range(len(pending)):
uda._call_finalizer(pending[i])
pending[i] = None # clear the list as we progress
def disable_finalizers(space):
uda = space.user_del_action
uda.finalizers_lock_count += 1
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
oldstate = W_GcCollectStepStats.STATE_USERDEL
newstate = W_GcCollectStepStats.STATE_SCANNING
major_is_done = True # now we are finally done
else:
states = self._collect_step()
oldstate = rgc.old_state(states)
newstate = rgc.new_state(states)
major_is_done = False # USERDEL still to do
if rgc.is_done(states):
newstate = W_GcCollectStepStats.STATE_USERDEL
self.finalizing = True
#
duration = -1
return W_GcCollectStepStats(
count = 1,
duration = duration,
duration_min = duration,
duration_max = duration,
oldstate = oldstate,
newstate = newstate,
major_is_done = major_is_done)
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)
w_stats = sc.do()
return w_stats
# ____________________________________________________________
@unwrap_spec(filename='fsencode')
def dump_heap_stats(space, filename):
tb = rgc._heap_stats()
if not tb:
raise oefmt(space.w_RuntimeError, "Wrong GC")
f = open(filename, mode="w")
for i in range(len(tb)):
f.write("%d %d " % (tb[i].count, tb[i].size))
f.write(",".join([str(tb[i].links[j]) for j in range(len(tb))]) + "\n")
f.close()
|