Author: Stefan Beyer <h...@sbeyer.at> Branch: cpyext-gc-cycle Changeset: r95600:94b062729ca4 Date: 2018-03-23 11:46 +0100 http://bitbucket.org/pypy/pypy/changeset/94b062729ca4/
Log: Refactored call to tp_traverse from incminimark so there are no dependencies to pypy diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -1719,11 +1719,6 @@ return make_generic_cpy_call(FT, False)(space, func, *args) @specialize.ll() -def generic_cpy_call_gc(func, *args): - FT = lltype.typeOf(func).TO - return make_generic_cpy_call_gc(FT, False)(func, *args) - -@specialize.ll() def generic_cpy_call_expect_null(space, func, *args): FT = lltype.typeOf(func).TO return make_generic_cpy_call(FT, True)(space, func, *args) diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -20,8 +20,7 @@ from rpython.rlib.debug import ll_assert, fatalerror, debug_print from rpython.rlib.rawrefcount import ( REFCNT_MASK, REFCNT_FROM_PYPY, REFCNT_OVERFLOW, REFCNT_CYCLE_BUFFERED, - REFCNT_CLR_MASK, REFCNT_CLR_GREEN, REFCNT_CLR_PURPLE, - W_MARKER_DEALLOCATING) + REFCNT_CLR_MASK, REFCNT_CLR_GREEN, REFCNT_CLR_PURPLE) from pypy.module.cpyext.api import slot_function from pypy.module.cpyext.typeobjectdefs import visitproc @@ -254,6 +253,8 @@ w_obj._cpyext_attach_pyobj(space, py_obj) +w_marker_deallocating = W_Root() + @jit.dont_look_inside def from_ref(space, ref): """ @@ -265,7 +266,7 @@ return None w_obj = rawrefcount.to_obj(W_Root, ref) if w_obj is not None: - if w_obj is not W_MARKER_DEALLOCATING: + if w_obj is not w_marker_deallocating: return w_obj fatalerror( "*** Invalid usage of a dying CPython object ***\n" @@ -318,7 +319,7 @@ def pyobj_has_w_obj(pyobj): w_obj = rawrefcount.to_obj(W_Root, pyobj) - return w_obj is not None and w_obj is not W_MARKER_DEALLOCATING + return w_obj is not None and w_obj is not w_marker_deallocating def w_obj_has_pyobj(w_obj): return bool(rawrefcount.from_obj(PyObject, w_obj)) @@ -454,7 +455,7 @@ @init_function def write_w_marker_deallocating(space): if we_are_translated(): - llptr = cast_instance_to_base_ptr(W_MARKER_DEALLOCATING) + llptr = cast_instance_to_base_ptr(w_marker_deallocating) state = space.fromcache(State) state.C.set_marker(rffi.cast(Py_ssize_t, llptr)) 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 @@ -1,8 +1,8 @@ from rpython.rlib.objectmodel import we_are_translated, specialize -from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.rtyper.lltypesystem import rffi, lltype, llmemory from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter import executioncontext -from rpython.rtyper.annlowlevel import llhelper +from rpython.rtyper.annlowlevel import llhelper, llhelper_args from rpython.rlib.rdynload import DLLHANDLE from rpython.rlib import rawrefcount import sys @@ -70,7 +70,10 @@ decref(space, ob) print 'dealloc_trigger DONE' return "RETRY" - rawrefcount.init(dealloc_trigger) + def tp_traverse(obj_addr, callback, args): + # TODO: implement + pass + rawrefcount.init(dealloc_trigger, tp_traverse) else: if space.config.translation.gc == "boehm": action = BoehmPyObjDeallocAction(space) @@ -80,6 +83,25 @@ pyobj_dealloc_action = PyObjDeallocAction(space) self.dealloc_trigger = lambda: pyobj_dealloc_action.fire() + def _rawrefcount_tp_traverse(space, pyobj_ptr, callback, args): + from pypy.module.cpyext.api import (generic_cpy_call, + PyObject) + from pypy.module.cpyext.typeobjectdefs import visitproc + # convert to pointers with correct types (PyObject) + callback_addr = llmemory.cast_ptr_to_adr(callback) + callback_ptr = llmemory.cast_adr_to_ptr(callback_addr, + visitproc) + pyobj_addr = llmemory.cast_ptr_to_adr(pyobj_ptr) + pyobj = llmemory.cast_adr_to_ptr(pyobj_addr, PyObject) + # now call tp_traverse (if possible) + if pyobj.c_ob_type and pyobj.c_ob_type.c_tp_traverse: + generic_cpy_call(space, pyobj.c_ob_type.c_tp_traverse, + pyobj, + callback_ptr, args) + self.tp_traverse = (lambda o, v, a: + _rawrefcount_tp_traverse(self.space, + o, v, a)) + def build_api(self): """NOT_RPYTHON This function is called when at object space creation, @@ -111,7 +133,9 @@ # does something different. Sigh. rawrefcount.init( llhelper(rawrefcount.RAWREFCOUNT_DEALLOC_TRIGGER, - self.dealloc_trigger)) + self.dealloc_trigger), + llhelper(rawrefcount.RAWREFCOUNT_TRAVERSE, + self.tp_traverse)) self.builder.attach_all(space) setup_new_method_def(space) 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 @@ -74,7 +74,6 @@ from rpython.rlib.objectmodel import specialize from rpython.rlib import rgc from rpython.memory.gc.minimarkpage import out_of_memory -from pypy.module.cpyext.api import slot_function, PyObject from rpython.rtyper.lltypesystem import rffi # @@ -190,30 +189,6 @@ ('forw', llmemory.Address)) FORWARDSTUBPTR = lltype.Ptr(FORWARDSTUB) NURSARRAY = lltype.Array(llmemory.Address) -VISIT_FUNCTYPE = rffi.CCallback([PyObject, rffi.VOIDP], - rffi.INT_real) - - -def visit_trace_non_rc_roots(pyobj, self_ptr): - from rpython.rlib.rawrefcount import (REFCNT_CLR_BLACK, - REFCNT_CLR_MASK) - from rpython.rtyper.annlowlevel import cast_adr_to_nongc_instance - - self_adr = rffi.cast(llmemory.Address, self_ptr) - self = cast_adr_to_nongc_instance(IncrementalMiniMarkGC, self_adr) - - # if the pyobj is not marked, remember it and if there is a linked pypy - # object also remember it - if pyobj.c_ob_refcnt & REFCNT_CLR_MASK != REFCNT_CLR_BLACK: - pyobject = llmemory.cast_ptr_to_adr(pyobj) - self.rrc_more_pyobjects_to_scan.append(pyobject) - intobj = pyobj.c_ob_pypy_link - if intobj != 0: - obj = llmemory.cast_int_to_adr(intobj) - hdr = self.header(obj) - if not (hdr.tid & GCFLAG_VISITED): - self.objects_to_trace.append(obj) - return rffi.cast(rffi.INT_real, 0) # ____________________________________________________________ @@ -3020,11 +2995,17 @@ ('c_ob_pypy_link', lltype.Signed)) PYOBJ_HDR_PTR = lltype.Ptr(PYOBJ_HDR) RAWREFCOUNT_DEALLOC_TRIGGER = lltype.Ptr(lltype.FuncType([], lltype.Void)) + VISIT_FUNCTYPE = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR, rffi.VOIDP], + rffi.INT_real)) + RAWREFCOUNT_TRAVERSE = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR, + VISIT_FUNCTYPE, + rffi.VOIDP], + lltype.Void)) def _pyobj(self, pyobjaddr): - return llmemory.cast_adr_to_ptr(pyobjaddr, lltype.Ptr(PyObject.TO)) + return llmemory.cast_adr_to_ptr(pyobjaddr, self.PYOBJ_HDR_PTR) - def rawrefcount_init(self, dealloc_trigger_callback): + def rawrefcount_init(self, dealloc_trigger_callback, tp_traverse): # see pypy/doc/discussion/rawrefcount.rst if not self.rrc_enabled: self.rrc_p_list_young = self.AddressStack() @@ -3035,6 +3016,7 @@ self.rrc_p_dict = self.AddressDict() # non-nursery keys only self.rrc_p_dict_nurs = self.AddressDict() # nursery keys only self.rrc_dealloc_trigger_callback = dealloc_trigger_callback + self.rrc_tp_traverse = tp_traverse self.rrc_dealloc_pending = self.AddressStack() self.rrc_pyobjects_to_scan = self.AddressStack() self.rrc_more_pyobjects_to_scan = self.AddressStack() @@ -3214,10 +3196,13 @@ NO_CYCLE_DETECTION = False def rrc_major_collection_trace(self): + debug_start("gc-rrc-trace") if self.NO_CYCLE_DETECTION: self.rrc_p_list_old.foreach(self._rrc_major_trace, None) else: self.rrc_major_collection_trace_cycle() + self.rrc_p_list_old.foreach(self._rrc_major_trace, None) # for now, remove later + debug_stop("gc-rrc-trace") def _rrc_major_trace(self, pyobject, ignore): from rpython.rlib.rawrefcount import REFCNT_MASK @@ -3238,20 +3223,22 @@ assert not self.rrc_more_pyobjects_to_scan.non_empty() assert not self.rrc_pyobjects_to_trace.non_empty() - # initially, scan all old pyobjects which are linked to objects - self.rrc_p_list_old.foreach(self._rrc_major_scan_non_rc_roots, None) + # initially, scan all real pyobjects (not proxies) which are linked to objects + #self.rrc_p_list_old.foreach(self._rrc_major_scan_non_rc_roots, None) + self.rrc_o_list_old.foreach(self._rrc_major_scan_non_rc_roots, None) # as long as we find new pyobjects which should be marked, recursively # mark them while self.rrc_pyobjects_to_trace.non_empty(): while self.rrc_pyobjects_to_trace.non_empty(): - pyobj = self.rrc_pyobjects_to_trace.pop() - self._rrc_major_trace_non_rc_roots(pyobj) + pyobject = self.rrc_pyobjects_to_trace.pop() + self._rrc_traverse(pyobject) # see if we found new pypy objects to trace if self.objects_to_trace.non_empty(): self.visit_all_objects() self.objects_to_trace.delete() + self.objects_to_trace = self.AddressStack() # look if there are some pyobjects with linked objects which were # not marked previously, but are marked now @@ -3261,26 +3248,29 @@ self.rrc_pyobjects_to_scan.foreach( self._rrc_major_scan_non_rc_roots, None) self.rrc_pyobjects_to_scan.delete() + self.rrc_pyobjects_to_scan = self.AddressStack() - def traverse(self, pyobject, func_ptr): - from pypy.module.cpyext.api import generic_cpy_call_gc - from pypy.module.cpyext.typeobjectdefs import visitproc - from rpython.rtyper.annlowlevel import cast_nongc_instance_to_adr - self_addr = cast_nongc_instance_to_adr(self) - pyobj = self._pyobj(pyobject) - if pyobj.c_ob_type and pyobj.c_ob_type.c_tp_traverse: - visitproc_ptr = rffi.cast(visitproc, func_ptr) - generic_cpy_call_gc(pyobj.c_ob_type.c_tp_traverse, pyobj, - visitproc_ptr, rffi.cast(rffi.VOIDP, self_addr)) - #cast_nongc_instance_to_adr(self) + self.rrc_more_pyobjects_to_scan.delete() + self.rrc_more_pyobjects_to_scan = self.AddressStack() - def _rrc_major_trace_non_rc_roots(self, pyobject): - from rpython.rtyper.annlowlevel import llhelper - func_ptr = llhelper(VISIT_FUNCTYPE, visit_trace_non_rc_roots) - self.traverse(pyobject, func_ptr) + def _rrc_mark_cpyobj(self, pyobj): + from rpython.rlib.rawrefcount import (REFCNT_CLR_GRAY, + REFCNT_CLR_MASK) + # if the pyobj is not marked, remember it and if there is a linked pypy + # object also remember it + if pyobj.c_ob_refcnt & REFCNT_CLR_MASK != REFCNT_CLR_GRAY: + pyobj.c_ob_refcnt = REFCNT_CLR_GRAY + pyobject = llmemory.cast_ptr_to_adr(pyobj) + self.rrc_more_pyobjects_to_scan.append(pyobject) + intobj = pyobj.c_ob_pypy_link + if intobj != 0: + obj = llmemory.cast_int_to_adr(intobj) + hdr = self.header(obj) + if not (hdr.tid & GCFLAG_VISITED): + self.objects_to_trace.append(obj) def _rrc_major_scan_non_rc_roots(self, pyobject, ignore): - from rpython.rlib.rawrefcount import (REFCNT_CLR_BLACK, + from rpython.rlib.rawrefcount import (REFCNT_CLR_GRAY, REFCNT_CLR_MASK) # check in the object header of the linked pypy object, if it is marked # or not @@ -3289,8 +3279,9 @@ obj = llmemory.cast_int_to_adr(intobj) hdr = self.header(obj) if hdr.tid & GCFLAG_VISITED: - if pyobj.c_ob_refcnt & REFCNT_CLR_MASK != REFCNT_CLR_BLACK: + if pyobj.c_ob_refcnt & REFCNT_CLR_MASK != REFCNT_CLR_GRAY: # TODO change to black, but make white default # process the pyobject now + pyobj.c_ob_refcnt = REFCNT_CLR_GRAY self.rrc_pyobjects_to_trace.append(pyobject) else: # save the pyobject for later, in case its linked object becomes @@ -3330,3 +3321,22 @@ surviving_dict.insertclean(obj, pyobject) else: self._rrc_free(pyobject) + + def _rrc_visit(pyobj, self_ptr): + from rpython.rtyper.annlowlevel import cast_adr_to_nongc_instance + # + debug_print("visit called!") + self_adr = rffi.cast(llmemory.Address, self_ptr) + self = cast_adr_to_nongc_instance(IncrementalMiniMarkGC, self_adr) + self._rrc_mark_cpyobj(pyobj) + return rffi.cast(rffi.INT_real, 0) + + def _rrc_traverse(self, pyobject): + from rpython.rtyper.annlowlevel import (cast_nongc_instance_to_adr, + llhelper) + # + pyobj = self._pyobj(pyobject) + callback_ptr = llhelper(self.VISIT_FUNCTYPE, + IncrementalMiniMarkGC._rrc_visit) + self_ptr = rffi.cast(rffi.VOIDP, cast_nongc_instance_to_adr(self)) + self.rrc_tp_traverse(pyobj, callback_ptr, self_ptr) \ No newline at end of file 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 @@ -53,7 +53,9 @@ else: rc = REFCNT_FROM_PYPY self.trigger = [] - self.gc.rawrefcount_init(lambda: self.trigger.append(1)) + self.trigger2 = [] + self.gc.rawrefcount_init(lambda: self.trigger.append(1), + lambda: self.trigger2.append(1)) # if create_immortal: p1 = lltype.malloc(S, immortal=True) 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 @@ -475,7 +475,8 @@ if hasattr(GCClass, 'rawrefcount_init'): self.rawrefcount_init_ptr = getfn( GCClass.rawrefcount_init, - [s_gc, SomePtr(GCClass.RAWREFCOUNT_DEALLOC_TRIGGER)], + [s_gc, SomePtr(GCClass.RAWREFCOUNT_DEALLOC_TRIGGER), + SomePtr(GCClass.RAWREFCOUNT_TRAVERSE)], annmodel.s_None) self.rawrefcount_create_link_pypy_ptr = getfn( GCClass.rawrefcount_create_link_pypy, @@ -1314,10 +1315,12 @@ self.pop_roots(hop, livevars) def gct_gc_rawrefcount_init(self, hop): - [v_fnptr] = hop.spaceop.args + [v_fnptr, v_fnptr2] = hop.spaceop.args assert v_fnptr.concretetype == self.GCClass.RAWREFCOUNT_DEALLOC_TRIGGER + assert v_fnptr2.concretetype == self.GCClass.RAWREFCOUNT_TRAVERSE hop.genop("direct_call", - [self.rawrefcount_init_ptr, self.c_const_gc, v_fnptr]) + [self.rawrefcount_init_ptr, self.c_const_gc, v_fnptr, + v_fnptr2]) def gct_gc_rawrefcount_create_link_pypy(self, hop): [v_gcobj, v_pyobject] = hop.spaceop.args diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py --- a/rpython/rlib/rawrefcount.py +++ b/rpython/rlib/rawrefcount.py @@ -10,7 +10,6 @@ from rpython.rtyper.extregistry import ExtRegistryEntry from rpython.translator.tool.cbuild import ExternalCompilationInfo from rpython.rlib import rgc, objectmodel -from pypy.interpreter.baseobjspace import W_Root MAX_BIT = int(math.log(sys.maxint, 2)) @@ -44,9 +43,17 @@ REFCNT_OVERFLOW = 1 << REFCNT_BITS REFCNT_MASK = (1 << REFCNT_BITS + 1) - 1 - +PYOBJ_HDR = lltype.Struct('GCHdr_PyObject', + ('c_ob_refcnt', lltype.Signed), + ('c_ob_pypy_link', lltype.Signed)) +PYOBJ_HDR_PTR = lltype.Ptr(PYOBJ_HDR) RAWREFCOUNT_DEALLOC_TRIGGER = lltype.Ptr(lltype.FuncType([], lltype.Void)) -W_MARKER_DEALLOCATING = W_Root() +VISIT_FUNCTYPE = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR, rffi.VOIDP], + rffi.INT_real)) +RAWREFCOUNT_TRAVERSE = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR, + VISIT_FUNCTYPE, + rffi.VOIDP], + lltype.Void)) def _build_pypy_link(p): @@ -97,12 +104,12 @@ # TODO: _cyclic_refcount_overflow = dict() @not_rpython -def init(dealloc_trigger_callback=None): +def init(dealloc_trigger_callback=None, tp_traverse=None): """set up rawrefcount with the GC. This is only used for tests; it should not be called at all during translation. """ global _p_list, _o_list, _adr2pypy, _pypy2ob, _pypy2ob_rev - global _d_list, _dealloc_trigger_callback + global _d_list, _dealloc_trigger_callback, _tp_traverse _p_list = [] _o_list = [] _adr2pypy = [None] @@ -111,6 +118,7 @@ _d_list = [] _d_marker = None _dealloc_trigger_callback = dealloc_trigger_callback + _tp_traverse = tp_traverse # def init_traverse(traverse_cpy_call): # global _traverse_cpy_call @@ -308,14 +316,15 @@ class Entry(ExtRegistryEntry): _about_ = init - def compute_result_annotation(self, s_dealloc_callback): + def compute_result_annotation(self, s_dealloc_callback, tp_traverse): from rpython.rtyper.llannotation import SomePtr assert isinstance(s_dealloc_callback, SomePtr) # ll-ptr-to-function + # add assert? def specialize_call(self, hop): hop.exception_cannot_occur() - [v_dealloc_callback] = hop.inputargs(hop.args_r[0]) - hop.genop('gc_rawrefcount_init', [v_dealloc_callback]) + v_dealloc_callback, v_tp_traverse = hop.inputargs(*hop.args_r) + hop.genop('gc_rawrefcount_init', [v_dealloc_callback, v_tp_traverse]) class Entry(ExtRegistryEntry): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit