File: interp_gc.py

package info (click to toggle)
pypy3 7.3.19%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 212,236 kB
  • sloc: python: 2,098,316; ansic: 540,565; sh: 21,462; asm: 14,419; cpp: 4,451; makefile: 4,209; objc: 761; xml: 530; exp: 499; javascript: 314; pascal: 244; lisp: 45; csh: 12; awk: 4
file content (155 lines) | stat: -rw-r--r-- 5,578 bytes parent folder | download | duplicates (2)
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()