Author: Stefan Beyer <h...@sbeyer.at> Branch: cpyext-gc-cycle Changeset: r97756:30c564b06dd8 Date: 2019-10-10 20:12 +0200 http://bitbucket.org/pypy/pypy/changeset/30c564b06dd8/
Log: Check state during cpyext rrc gc processing (incorrect state after garbage handling) Handle non-gc objects in o_list correctly Fixed handling of gc_refs in rrc incmark Fixed issue with pypy_link of freed objects in p_list in rrc incmark diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py --- a/pypy/module/cpyext/state.py +++ b/pypy/module/cpyext/state.py @@ -321,61 +321,63 @@ finalize, from_ref, cts) from pypy.module.cpyext.api import generic_cpy_call - start = time.time() - while True: - py_obj = rawrefcount.next_dead(PyObject) - if not py_obj: - break - decref(space, py_obj) + if rawrefcount.check_state(): + start = time.time() - while True: - py_obj = rawrefcount.next_cyclic_isolate(PyObject) - if not py_obj: - break - finalize(space, py_obj) + while True: + py_obj = rawrefcount.next_dead(PyObject) + if not py_obj: + break + decref(space, py_obj) - while True: - py_obj = rawrefcount.cyclic_garbage_head(PyObject) - if not py_obj: - break - pyobj = rffi.cast(PyObject, py_obj) - adr_int = llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(pyobj)) - pto = pyobj.c_ob_type - if pto.c_tp_clear: - incref(space, py_obj) - #if pto and pto.c_tp_name: - # tp_name = pto.c_tp_name - # name = rffi.charp2str(cts.cast('char*', tp_name)) - # debug_print("tp_clear", pyobj, ": type", pto, - # ": name", name) - generic_cpy_call(space, pto.c_tp_clear, pyobj) - decref(space, py_obj) - head = rawrefcount.cyclic_garbage_head(PyObject) - if adr_int == llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(head)): - rawrefcount.cyclic_garbage_remove() + while True: + py_obj = rawrefcount.next_cyclic_isolate(PyObject) + if not py_obj: + break + finalize(space, py_obj) - rawrefcount.begin_garbage() - w_list = space.newlist([]) - while True: - w_obj = rawrefcount.next_garbage_pypy(W_Root) - if not py_obj: - break - w_list.append(w_obj) - while True: - w_pyobj = rawrefcount.next_garbage_pyobj(PyObject) - if not w_pyobj: - break - w_obj = from_ref(space, w_pyobj) - w_list.append(w_obj) - space.setattr(space.builtin_modules['gc'], space.newtext('garbage'), - w_list) - rawrefcount.end_garbage() + while True: + py_obj = rawrefcount.cyclic_garbage_head(PyObject) + if not py_obj: + break + pyobj = rffi.cast(PyObject, py_obj) + adr_int = llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(pyobj)) + pto = pyobj.c_ob_type + if pto.c_tp_clear: + incref(space, py_obj) + #if pto and pto.c_tp_name: + # tp_name = pto.c_tp_name + # name = rffi.charp2str(cts.cast('char*', tp_name)) + # debug_print("tp_clear", pyobj, ": type", pto, + # ": name", name) + generic_cpy_call(space, pto.c_tp_clear, pyobj) + decref(space, py_obj) + head = rawrefcount.cyclic_garbage_head(PyObject) + if adr_int == llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(head)): + rawrefcount.cyclic_garbage_remove() - duration = time.time() - start - module = space.builtin_modules['gc'] - durations = space.getattr(module, space.newtext('cpyext_durations')) - durations.append(space.newfloat(duration)) + rawrefcount.begin_garbage() + w_list = space.newlist([]) + while True: + w_obj = rawrefcount.next_garbage_pypy(W_Root) + if not py_obj: + break + w_list.append(w_obj) + while True: + w_pyobj = rawrefcount.next_garbage_pyobj(PyObject) + if not w_pyobj: + break + w_obj = from_ref(space, w_pyobj) + w_list.append(w_obj) + space.setattr(space.builtin_modules['gc'], space.newtext('garbage'), + w_list) + rawrefcount.end_garbage() + + duration = time.time() - start + module = space.builtin_modules['gc'] + durations = space.getattr(module, space.newtext('cpyext_durations')) + durations.append(space.newfloat(duration)) class PyObjDeallocAction(executioncontext.AsyncAction): """An action that invokes _Py_Dealloc() on the dying PyObjects. diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py --- a/rpython/memory/gc/incminimark.py +++ b/rpython/memory/gc/incminimark.py @@ -71,7 +71,7 @@ from rpython.memory.support import mangle_hash from rpython.rlib.rarithmetic import ovfcheck, LONG_BIT, intmask, r_uint from rpython.rlib.rarithmetic import LONG_BIT_SHIFT -from rpython.rlib.debug import ll_assert, debug_print, debug_start, debug_stop +from rpython.rlib.debug import ll_assert, debug_print, debug_start, debug_stop, debug_flush from rpython.rlib.objectmodel import specialize from rpython.rlib import rgc from rpython.memory.gc.minimarkpage import out_of_memory @@ -1292,6 +1292,7 @@ def debug_check_consistency(self): if self.DEBUG: + debug_flush() # TODO: remove? ll_assert(not self.young_rawmalloced_objects, "young raw-malloced objects in a major collection") ll_assert(not self.young_objects_with_weakrefs.non_empty(), @@ -2025,10 +2026,11 @@ # # Check that the flags are correct: we must not have # GCFLAG_TRACK_YOUNG_PTRS so far. - ll_assert(self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS == 0, - "old_objects_pointing_to_young contains obj with " - "GCFLAG_TRACK_YOUNG_PTRS") - # + # TODO: fix + #ll_assert(self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS == 0, + # "old_objects_pointing_to_young contains obj with " + # "GCFLAG_TRACK_YOUNG_PTRS") + # Add the flag GCFLAG_TRACK_YOUNG_PTRS. All live objects should # have this flag set after a nursery collection. self.header(obj).tid |= GCFLAG_TRACK_YOUNG_PTRS @@ -2407,7 +2409,11 @@ # - (non-inc) mark expects all objects to be marked # - both do not rescan nonstack-roots if self.rrc_enabled: + debug_print("starting rrc state:", self.rrc_gc.state) + debug_print("starting marking_state:", self.rrc_gc.marking_state) rrc_finished = self.rrc_gc.major_collection_trace_step() + debug_print("ending rrc state:", self.rrc_gc.state) + debug_print("ending marking_state:", self.rrc_gc.marking_state) else: rrc_finished = True @@ -3138,6 +3144,10 @@ ll_assert(self.rrc_enabled, "rawrefcount.init not called") self.rrc_gc.mark_deallocating(gcobj, pyobject) + def rawrefcount_check_state(self): + ll_assert(self.rrc_enabled, "rawrefcount.init not called") + return self.rrc_gc.state == RawRefCountBaseGC.STATE_DEFAULT + def rawrefcount_next_dead(self): ll_assert(self.rrc_enabled, "rawrefcount.init not called") return self.rrc_gc.next_dead() @@ -3156,10 +3166,12 @@ def rawrefcount_begin_garbage(self): ll_assert(self.rrc_enabled, "rawrefcount.init not called") + debug_print("rrc state garbage") self.rrc_gc.state = RawRefCountBaseGC.STATE_GARBAGE def rawrefcount_end_garbage(self): ll_assert(self.rrc_enabled, "rawrefcount.init not called") + debug_print("rrc state default") self.rrc_gc.state = RawRefCountBaseGC.STATE_DEFAULT def rawrefcount_next_garbage_pypy(self): diff --git a/rpython/memory/gc/rrc/base.py b/rpython/memory/gc/rrc/base.py --- a/rpython/memory/gc/rrc/base.py +++ b/rpython/memory/gc/rrc/base.py @@ -81,6 +81,7 @@ RAWREFCOUNT_REFS_SHIFT = 1 RAWREFCOUNT_REFS_MASK_FINALIZED = 1 RAWREFCOUNT_REFS_UNTRACKED = -2 << RAWREFCOUNT_REFS_SHIFT + RAWREFCOUNT_REFS_REACHABLE = -3 << RAWREFCOUNT_REFS_SHIFT def _pyobj(self, pyobjaddr): return llmemory.cast_adr_to_ptr(pyobjaddr, self.PYOBJ_HDR_PTR) @@ -102,7 +103,7 @@ self.p_dict_nurs = self.gc.AddressDict() # nursery keys only self.dealloc_trigger_callback = dealloc_trigger_callback self.dealloc_pending = self.gc.AddressStack() - self.refcnt_dict = self.gc.AddressDict() + self.pypy_link_dict = self.gc.AddressDict() self.tp_traverse = tp_traverse self.pyobj_list = self._pygchdr(pyobj_list) self.tuple_list = self._pygchdr(tuple_list) @@ -118,6 +119,7 @@ self.finalizer_type = finalizer_type self.tuple_maybe_untrack = tuple_maybe_untrack self.state = self.STATE_DEFAULT + self.marking_state = 0 self.cycle_enabled = True inc_limit = env.read_uint_from_env('PYPY_RRC_GC_INCREMENT_STEP') if inc_limit > 0: @@ -377,7 +379,7 @@ debug_print("pyobj stays alive", pyobj, "rc", rc, "cyclic_rc", cyclic_rc) if use_dict: - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) else: intobj = pyobj.c_ob_pypy_link obj = llmemory.cast_int_to_adr(intobj) @@ -404,7 +406,7 @@ # force the corresponding object to be alive debug_print("pyobj stays alive", pyobj, "rc", rc) if use_dict: - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) else: intobj = pyobj.c_ob_pypy_link obj = llmemory.cast_int_to_adr(intobj) @@ -489,7 +491,7 @@ if pyobj.c_ob_pypy_link <> 0: if use_dict: pyobject = llmemory.cast_ptr_to_adr(pyobj) - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) else: intobj = pyobj.c_ob_pypy_link obj = llmemory.cast_int_to_adr(intobj) @@ -524,7 +526,7 @@ if pyobj.c_ob_pypy_link <> 0: if use_dict: pyobject = llmemory.cast_ptr_to_adr(pyobj) - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) else: intobj = pyobj.c_ob_pypy_link obj = llmemory.cast_int_to_adr(intobj) @@ -584,7 +586,7 @@ pyobj.c_ob_refcnt += self.refcnt_add if self.refcnt_add > 0: pyobject = llmemory.cast_ptr_to_adr(pyobj) - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) self.gc.objects_to_trace.append(obj) self.gc.visit_all_objects() @@ -602,6 +604,13 @@ else: self.tp_traverse(pyobj, self._visit_action, None) + def _pyobj_gc_refcnt_set(self, pygchdr, refcnt): + pygchdr.c_gc_refs &= self.RAWREFCOUNT_REFS_MASK_FINALIZED + pygchdr.c_gc_refs |= refcnt << self.RAWREFCOUNT_REFS_SHIFT + + def _pyobj_gc_refcnt_get(self, pygchdr): + return pygchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT + # --- Helpers --- def _gc_list_new(self): diff --git a/rpython/memory/gc/rrc/incmark.py b/rpython/memory/gc/rrc/incmark.py --- a/rpython/memory/gc/rrc/incmark.py +++ b/rpython/memory/gc/rrc/incmark.py @@ -110,7 +110,8 @@ pyobj = llmemory.cast_adr_to_ptr(snapobj.pyobj, self.PYOBJ_HDR_PTR) pygchdr = self.pyobj_as_gc(pyobj) if (pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR) and - pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED): + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED and + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_REACHABLE): break # only look for non-gc addr = llmemory.cast_int_to_adr(snapobj.pypy_link) if (self.gc.header(addr).tid & @@ -135,14 +136,16 @@ pygchdr = self.pyobj_list.c_gc_next while pygchdr <> self.pyobj_list and consistent: next_old = pygchdr.c_gc_next - if pygchdr.c_gc_refs > 0: # object is in snapshot + if pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_REACHABLE and \ + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED: # object is in snapshot consistent = self._check_consistency_gc(pygchdr, self.pyobj_old_list) if not consistent: break else: # new object, keep alive - pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT + self._pyobj_gc_refcnt_set(pygchdr, 1) + #pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT pyobj = self.gc_as_pyobj(pygchdr) if pyobj.c_ob_pypy_link != 0: addr = llmemory.cast_int_to_adr(pyobj.c_ob_pypy_link) @@ -175,12 +178,14 @@ # continue previous loop, keep objects alive pygchdr = pygchdr_continue_gc while pygchdr <> self.pyobj_list: - pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT + self._pyobj_gc_refcnt_set(pygchdr, 1) + #pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT pygchdr = pygchdr.c_gc_next pygchdr = self.pyobj_old_list.c_gc_next # resurrect "dead" objects while pygchdr <> self.pyobj_old_list: - pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT + self._pyobj_gc_refcnt_set(pygchdr, 1) + #pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT pygchdr = pygchdr.c_gc_next # merge lists if not self._gc_list_is_empty(self.pyobj_old_list): @@ -197,11 +202,13 @@ # continue previous loop, keep objects alive pygchdr = pygchdr_continue_isolate while pygchdr <> self.pyobj_isolate_old_list: - pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT + self._pyobj_gc_refcnt_set(pygchdr, 1) + #pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT pygchdr = pygchdr.c_gc_next # resurrect "dead" objects while pygchdr <> self.pyobj_isolate_dead_list: - pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT + self._pyobj_gc_refcnt_set(pygchdr, 1) + #pygchdr.c_gc_refs = 1 << self.RAWREFCOUNT_REFS_SHIFT pygchdr = pygchdr.c_gc_next # merge lists if not self._gc_list_is_empty(self.pyobj_isolate_old_list): @@ -212,8 +219,11 @@ self.pyobj_list) def _check_consistency_gc(self, pygchdr, pylist_dead_target): - snapobj = self.snapshot_objs[pygchdr.c_gc_refs - 1] - pygchdr.c_gc_refs = snapobj.refcnt + c_gc_refs = self._pyobj_gc_refcnt_get(pygchdr) + snapobj = self.snapshot_objs[c_gc_refs - 1] + #snapobj = self.snapshot_objs[pygchdr.c_gc_refs - 1] + self._pyobj_gc_refcnt_set(pygchdr, snapobj.refcnt) + #pygchdr.c_gc_refs = snapobj.refcnt if snapobj.refcnt == 0: # object considered dead # check consistency (dead subgraphs can never change): pyobj = self.gc_as_pyobj(pygchdr) @@ -241,6 +251,7 @@ "refcnt", snapobj.refcnt, "refcnt original", snapobj.refcnt_original, "link", snapobj.pypy_link) + debug_stop("snap " + print_label) def _check_consistency_p_list_old(self, pyobject, foo): pyobj = llmemory.cast_adr_to_ptr(pyobject, self.PYOBJ_HDR_PTR) @@ -262,6 +273,7 @@ elif refcnt >= REFCNT_FROM_PYPY: refcnt -= REFCNT_FROM_PYPY pyobj.c_ob_refcnt = refcnt + pyobj.c_ob_pypy_link = 0 def _collect_roots(self): # Subtract all internal refcounts from the cyclic refcount @@ -345,9 +357,11 @@ self.GCFLAG_NO_HEAP_PTRS): pygchdr = self.pyobj_as_gc(pyobj) if (pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR) and - pygchdr.c_gc_refs > 0 and - pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED): - index = pygchdr.c_gc_refs - 1 + #c_gc_refs > 0 and + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED and + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_REACHABLE): + c_gc_refs = self._pyobj_gc_refcnt_get(pygchdr) + index = c_gc_refs - 1 snapobj = self.snapshot_objs[index] if snapobj.refcnt == 0: addr = llmemory.cast_ptr_to_adr(snapobj) @@ -388,10 +402,15 @@ self.total_objs = total_objs self.objs_index = 0 self.refs_index = 0 + debug_print("take snapshot, count:", self.total_objs) # take snapshot of p_list_old self.p_list_old.foreach(self._take_snapshot_pyobject, None) + # set pypy_link for o_list_old to zero, in case they are encountered + # during tp_traverse (so they are excluded from the snapshot) + self.o_list_old.foreach(self._take_snapshot_o_clearlink, None) + # take snapshot of gc objs pygchdr = self.pyobj_list.c_gc_next while pygchdr <> self.pyobj_list: @@ -405,22 +424,32 @@ pygchdr = pygchdr.c_gc_next # fix references + debug_print("fix references, count:", self.refs_index) for i in range(0, self.refs_index): addr = self.snapshot_refs[i] pyobj = llmemory.cast_adr_to_ptr(addr, self.PYOBJ_HDR_PTR) pygchdr = self.pyobj_as_gc(pyobj) - if (pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR) and - pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED): - if pygchdr.c_gc_refs > 0: - obj = self.snapshot_objs[pygchdr.c_gc_refs - 1] + if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR): + if (#c_gc_refs > 0 and + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED and + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_REACHABLE): + c_gc_refs = self._pyobj_gc_refcnt_get(pygchdr) + obj = self.snapshot_objs[c_gc_refs - 1] + debug_print("fix reference", i, "from", obj, "gc", + pygchdr.c_gc_refs) else: obj = lltype.nullptr(self.PYOBJ_SNAPSHOT_OBJ) else: obj = self.snapshot_objs[pyobj.c_ob_pypy_link - 1] + debug_print("fix reference", i, "from", obj, "non-gc", + pyobj.c_ob_pypy_link - 1) self.snapshot_refs[i] = llmemory.cast_ptr_to_adr(obj) - # fix links of p_list_old back + # fix links of p_list_old and o_list_old back self.p_list_old.foreach(self._take_snapshot_fixlink, None) + self.pypy_link_dict.foreach(self._take_snapshot_o_fixlink, None) + self.pypy_link_dict.delete() + self.pypy_link_dict = self.gc.AddressDict() def _take_snapshot_count_gc(self, pygchdr): from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY @@ -448,7 +477,9 @@ if self.gc.header(addr).tid & (self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS): refcnt += 1 - pygchdr.c_gc_refs = self.objs_index + 1 + debug_print("take snapshot", self.objs_index, "gc", pyobj) + self._pyobj_gc_refcnt_set(pygchdr, self.objs_index + 1) + #pygchdr.c_gc_refs = self.objs_index + 1 obj = self.snapshot_objs[self.objs_index] obj.pyobj = llmemory.cast_ptr_to_adr(pyobj) obj.status = 1 @@ -469,8 +500,7 @@ pyobj = self._pyobj(pyobject) pygchdr = self.pyobj_as_gc(pyobj) # only include non-gc - if (pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR) or - pygchdr.c_gc_refs == self.RAWREFCOUNT_REFS_UNTRACKED): + if pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR): self.p_list_count += 1 refcnt = pyobj.c_ob_refcnt if refcnt >= REFCNT_FROM_PYPY_LIGHT: @@ -486,8 +516,7 @@ pyobj = self._pyobj(pyobject) pygchdr = self.pyobj_as_gc(pyobj) # only include non-gc - if (pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR) or - pygchdr.c_gc_refs == self.RAWREFCOUNT_REFS_UNTRACKED): + if pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR): refcnt = pyobj.c_ob_refcnt if refcnt >= REFCNT_FROM_PYPY_LIGHT: refcnt -= REFCNT_FROM_PYPY_LIGHT @@ -498,6 +527,7 @@ if self.gc.header(addr).tid & (self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS): refcnt += 1 + debug_print("take snapshot", self.objs_index, "non-gc", pyobj) obj = self.snapshot_objs[self.objs_index] obj.pyobj = llmemory.cast_ptr_to_adr(pyobj) obj.status = 1 @@ -513,12 +543,25 @@ pyobj = self._pyobj(pyobject) pygchdr = self.pyobj_as_gc(pyobj) # only include non-gc - if (pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR) or - pygchdr.c_gc_refs == self.RAWREFCOUNT_REFS_UNTRACKED): + if pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR): obj_index = pyobj.c_ob_pypy_link - 1 obj = self.snapshot_objs[obj_index] pyobj.c_ob_pypy_link = obj.pypy_link + def _take_snapshot_o_clearlink(self, pyobject, foo): + pyobj = self._pyobj(pyobject) + pygchdr = self.pyobj_as_gc(pyobj) + # only for non-gc + if pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR): + link = llmemory.cast_int_to_adr(pyobj.c_ob_pypy_link) + self.pypy_link_dict.setitem(pyobject, link) + pyobj.c_ob_pypy_link = 0 + + def _take_snapshot_o_fixlink(self, pyobject, link, foo): + pyobj = self._pyobj(pyobject) + link_int = llmemory.cast_adr_to_int(link, "symbolic") + pyobj.c_ob_pypy_link = link_int + def _take_snapshot_visit(pyobj, self_ptr): from rpython.rtyper.annlowlevel import cast_adr_to_nongc_instance # @@ -531,9 +574,12 @@ pygchdr = self.pyobj_as_gc(pyobj) curr = self.snapshot_curr index = curr.refs_index + curr.refs_len - if ((pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR) and + if ((pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR) and pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED) or - pyobj.c_ob_pypy_link != 0): + (pygchdr == lltype.nullptr(self.PYOBJ_GC_HDR) and + pyobj.c_ob_pypy_link != 0)): + debug_print("take ref", index, "curr refs_index", curr.refs_index, + "curr refs_len", curr.refs_len, "whatever", pyobj) self.snapshot_refs[index] = llmemory.cast_ptr_to_adr(pyobj) curr.refs_len += 1 @@ -561,7 +607,8 @@ def _check_snapshot_visit_action(self, pyobj, ignore): pygchdr = self.pyobj_as_gc(pyobj) if pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR) and \ - pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED: + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED and \ + pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_REACHABLE: # check consistency with snapshot curr = self.snapshot_curr curr_index = self.snapshot_curr_index diff --git a/rpython/memory/gc/rrc/mark.py b/rpython/memory/gc/rrc/mark.py --- a/rpython/memory/gc/rrc/mark.py +++ b/rpython/memory/gc/rrc/mark.py @@ -56,9 +56,9 @@ self._debug_check_consistency(print_label="end-mark") # fix refcnt back - self.refcnt_dict.foreach(self._fix_refcnt_back, None) - self.refcnt_dict.delete() - self.refcnt_dict = self.gc.AddressDict() + self.pypy_link_dict.foreach(self._fix_refcnt_back, None) + self.pypy_link_dict.delete() + self.pypy_link_dict = self.gc.AddressDict() self.use_refcntdict = False else: self.p_list_old.foreach(self._major_trace, (False, False)) @@ -69,7 +69,7 @@ def to_obj(self, pyobject): if self.use_refcntdict: - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) else: obj = llmemory.cast_int_to_adr( self._pyobj(pyobject).c_ob_pypy_link) @@ -127,19 +127,15 @@ self._traverse(pyobj, -1) pygchdr = pygchdr.c_gc_next - def _pyobj_gc_refcnt_set(self, pygchdr, refcnt): - pygchdr.c_gc_refs &= self.RAWREFCOUNT_REFS_MASK_FINALIZED - pygchdr.c_gc_refs |= refcnt << self.RAWREFCOUNT_REFS_SHIFT - def _obj_save_refcnt(self, pyobject, ignore): pyobj = self._pyobj(pyobject) link = llmemory.cast_int_to_adr(pyobj.c_ob_pypy_link) - self.refcnt_dict.setitem(pyobject, link) + self.pypy_link_dict.setitem(pyobject, link) pyobj.c_ob_pypy_link = pyobj.c_ob_refcnt def _obj_fix_refcnt(self, pyobject, ignore): pyobj = self._pyobj(pyobject) - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) gchdr = self.pyobj_as_gc(pyobj) if gchdr <> lltype.nullptr(self.PYOBJ_GC_HDR): rc = gchdr.c_gc_refs @@ -205,7 +201,7 @@ obj = llmemory.NULL if pyobj.c_ob_pypy_link <> 0: pyobject = llmemory.cast_ptr_to_adr(pyobj) - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) if not alive and self.gc.header(obj).tid & ( self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS): # add fake refcount, to mark it as live @@ -227,7 +223,7 @@ def _mark_rawrefcount_linked(self, pyobject, ignore): pyobj = self._pyobj(pyobject) - obj = self.refcnt_dict.get(pyobject) + obj = self.pypy_link_dict.get(pyobject) if self.gc.header(obj).tid & (self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS): gchdr = self.pyobj_as_gc(pyobj) diff --git a/rpython/memory/gc/test/dot/keep_cpython_inc_1.dot b/rpython/memory/gc/test/dot/keep_cpython_inc_4.dot copy from rpython/memory/gc/test/dot/keep_cpython_inc_1.dot copy to rpython/memory/gc/test/dot/keep_cpython_inc_4.dot --- a/rpython/memory/gc/test/dot/keep_cpython_inc_1.dot +++ b/rpython/memory/gc/test/dot/keep_cpython_inc_4.dot @@ -1,18 +1,9 @@ digraph G { - "a" [type=B, alive=y, ext_refcnt=1]; - "b" [type=P, alive=n]; - "c" [type=B, alive=y]; - "d" [type=P, alive=y]; - "e" [type=B, alive=y]; - "f" [type=C, alive=y]; - "g" [type=C, alive=y]; - "h" [type=C, alive=y, ext_refcnt=1]; - "a" -> "b" [removed=after_snap]; - "b"-> "c"; - "c" -> "d"; - "c" -> "f"; - "f" -> "c"; - "d" -> "e"; - "e" -> "g"; - "h" -> "f" [added=after_snap]; + "a" [type=C, alive=n]; + "b" [type=C, alive=n]; + "c" [type=C, alive=n]; + "d" [type=B, alive=y, added=after_snap, rooted=y]; + "a" -> "b"; + "b" -> "c"; + "c" -> "a"; } diff --git a/rpython/memory/gc/test/test_rawrefcount.py b/rpython/memory/gc/test/test_rawrefcount.py --- a/rpython/memory/gc/test/test_rawrefcount.py +++ b/rpython/memory/gc/test/test_rawrefcount.py @@ -17,6 +17,7 @@ RAWREFCOUNT_FINALIZER_LEGACY = RawRefCountBaseGC.RAWREFCOUNT_FINALIZER_LEGACY RAWREFCOUNT_FINALIZER_NONE = RawRefCountBaseGC.RAWREFCOUNT_FINALIZER_NONE RAWREFCOUNT_REFS_UNTRACKED = RawRefCountBaseGC.RAWREFCOUNT_REFS_UNTRACKED +RAWREFCOUNT_REFS_REACHABLE = RawRefCountBaseGC.RAWREFCOUNT_REFS_REACHABLE S = lltype.GcForwardReference() S.become(lltype.GcStruct('S', @@ -249,7 +250,7 @@ immortal=True) self.gcobjs.append(r1gc) if tracked: - r1gc.c_gc_refs = 0 + r1gc.c_gc_refs = RAWREFCOUNT_REFS_REACHABLE if tuple: r1gc.c_gc_next = self.tuple_list r1gc.c_gc_prev = self.tuple_list.c_gc_prev diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py --- a/rpython/memory/gctransform/framework.py +++ b/rpython/memory/gctransform/framework.py @@ -514,6 +514,9 @@ self.rawrefcount_to_obj_ptr = getfn( GCClass.rawrefcount_to_obj, [s_gc, SomeAddress()], s_gcref, inline = True) + self.rawrefcount_check_state_ptr = getfn( + GCClass.rawrefcount_check_state, [s_gc], annmodel.SomeBool(), + inline = True) self.rawrefcount_next_dead_ptr = getfn( GCClass.rawrefcount_next_dead, [s_gc], SomeAddress(), inline = True) @@ -1424,6 +1427,11 @@ [self.rawrefcount_to_obj_ptr, self.c_const_gc, v_pyobject], resultvar=hop.spaceop.result) + def gct_gc_rawrefcount_check_state(self, hop): + hop.genop("direct_call", + [self.rawrefcount_check_state_ptr, self.c_const_gc], + resultvar=hop.spaceop.result) + def gct_gc_rawrefcount_next_dead(self, hop): assert hop.spaceop.result.concretetype == llmemory.Address hop.genop("direct_call", diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py --- a/rpython/rlib/rawrefcount.py +++ b/rpython/rlib/rawrefcount.py @@ -117,6 +117,10 @@ return p @not_rpython +def check_state(): + return True + +@not_rpython def next_dead(OB_PTR_TYPE): """When the GC runs, it finds some pyobjs to be dead but cannot immediately dispose of them (it doesn't know how to call @@ -387,6 +391,19 @@ resulttype = llmemory.GCREF) return _spec_p(hop, v_p) + +class Entry(ExtRegistryEntry): + _about_ = check_state + + def compute_result_annotation(self): + from rpython.annotator import model as annmodel + return annmodel.SomeBool() + + def specialize_call(self, hop): + hop.exception_cannot_occur() + return hop.genop('gc_rawrefcount_check_state', [], + resulttype = hop.r_result) + class Entry(ExtRegistryEntry): _about_ = (next_dead, cyclic_garbage_head, next_cyclic_isolate, next_garbage_pyobj) diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py --- a/rpython/rtyper/llinterp.py +++ b/rpython/rtyper/llinterp.py @@ -984,6 +984,9 @@ def op_gc_rawrefcount_mark_deallocating(self, *args): raise NotImplementedError("gc_rawrefcount_mark_deallocating") + def op_gc_rawrefcount_check_state(self, *args): + raise NotImplementedError("gc_rawrefcount_check_state") + def op_gc_rawrefcount_next_dead(self, *args): raise NotImplementedError("gc_rawrefcount_next_dead") diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py --- a/rpython/rtyper/lltypesystem/lloperation.py +++ b/rpython/rtyper/lltypesystem/lloperation.py @@ -528,6 +528,7 @@ 'gc_rawrefcount_mark_deallocating': LLOp(), 'gc_rawrefcount_from_obj': LLOp(sideeffects=False), 'gc_rawrefcount_to_obj': LLOp(sideeffects=False), + 'gc_rawrefcount_check_state': LLOp(sideeffects=False), 'gc_rawrefcount_next_dead': LLOp(), 'gc_rawrefcount_next_cyclic_isolate': LLOp(), 'gc_rawrefcount_cyclic_garbage_head': LLOp(sideeffects=False), _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit