[pypy-commit] lang-smalltalk storage-gcrefs: Extracted walking of gc references in separate module.

anton_gulenko noreply at buildbot.pypy.org
Mon Jul 21 11:10:02 CEST 2014


Author: Anton Gulenko <anton.gulenko at googlemail.com>
Branch: storage-gcrefs
Changeset: r925:8476f6a16d21
Date: 2014-07-21 11:10 +0200
http://bitbucket.org/pypy/lang-smalltalk/changeset/8476f6a16d21/

Log:	Extracted walking of gc references in separate module. Implemented
	walking the heap correctly without collecting a huge list of all
	objects. someInstance primitive still fails in interpreted mode, due
	to MemoryError in get_rpy_roots(). Don't see a way to fix except to
	go 64 bit. Translated version segfaults.

diff --git a/spyvm/gcrefs.py b/spyvm/gcrefs.py
new file mode 100644
--- /dev/null
+++ b/spyvm/gcrefs.py
@@ -0,0 +1,67 @@
+
+from rpython.rlib import rgc, objectmodel, jit
+
+# ======== Internal functions ========
+
+def flag(gcref):
+    return rgc.get_gcflag_extra(gcref)
+
+def toggle_flag(gcref):
+    rgc.toggle_gcflag_extra(gcref)
+
+def references(gcref):
+    return rgc.get_rpy_referents(gcref)
+
+def gc_roots():
+    return rgc.get_rpy_roots()
+
+def _clear_all_flags(gcrefs):
+    for gcref in gcrefs:
+        if gcref and flag(gcref):
+            toggle_flag(gcref)
+            _clear_all_flags(references(gcref))
+
+def _walk_gc_references(func, extra_parameter, collect_into, gcrefs):
+    for gcref in gcrefs:
+        if gcref and not flag(gcref):
+            toggle_flag(gcref)
+            result = func(gcref, extra_parameter)
+            if result is not None:
+                collect_into.append(result)
+            _walk_gc_references(func, extra_parameter, collect_into, references(gcref))
+    return collect_into
+
+# ======== API of this module ========
+# The extra_parameter is here to avoid creating closures in the function parameters,
+# and still be able to pass some context into the functions. It should always be a short tuple,
+# so that rpython can autmatically specialize these functions. If it fails to do so, annotate
+# all functions with extra_parameter with @objectmodel.specialize.argtype(2).
+
+def try_cast(type, gcref):
+    return rgc.try_cast_gcref_to_instance(type, gcref)
+
+ at jit.dont_look_inside
+def walk_gc_references(func, extra_parameter = None):
+    roots = gc_roots()
+    result = _walk_gc_references(func, extra_parameter, [], roots)
+    _clear_all_flags(roots)
+    _clear_all_flags(gc_roots()) # Just in case
+    return result
+
+def walk_gc_references_of_type(type, func, extra_parameter = None):
+    def check_type(gcref, extra):
+        type, func, extra_parameter = extra
+        w_obj = try_cast(type, gcref)
+        if w_obj:
+            func(w_obj, extra_parameter)
+        return None
+    walk_gc_references(check_type, (type, func, extra_parameter))
+
+def collect_gc_references_of_type(type, filter_func = lambda obj, extra: True, extra_parameter = None):
+    def check_type(gcref, extra):
+        type, filter_func, extra_parameter = extra
+        w_obj = try_cast(type, gcref)
+        if w_obj and filter_func(w_obj, extra_parameter):
+            return w_obj
+        return None
+    return walk_gc_references(check_type, (type, filter_func, extra_parameter))
diff --git a/spyvm/primitives.py b/spyvm/primitives.py
--- a/spyvm/primitives.py
+++ b/spyvm/primitives.py
@@ -4,7 +4,7 @@
 import operator
 from spyvm import model, shadow, error, constants, display
 from spyvm.error import PrimitiveFailedError, PrimitiveNotYetWrittenError
-from spyvm import wrapper
+from spyvm import wrapper, gcrefs
 
 from rpython.rlib import rarithmetic, rfloat, unroll, jit, objectmodel
 
@@ -540,43 +540,25 @@
     w_frame.store(interp.space, constants.CTXPART_STACKP_INDEX, interp.space.wrap_int(stackp))
     return w_frame
 
-
 def stm_enabled():
     """NOT RPYTHON"""
     from rpython.rlib import rgc
     return hasattr(rgc, "stm_is_enabled") and rgc.stm_is_enabled()
+
 if stm_enabled():
     def get_instances_array(space, s_frame, w_class):
         return []
 else:
     def get_instances_array(space, s_frame, w_class):
-        # This primitive returns some instance of the class on the stack.
-        # Not sure quite how to do this; maintain a weak list of all
-        # existing instances or something?
-        match_w = s_frame.instances_array(w_class)
-        if match_w is None:
-            match_w = []
-            from rpython.rlib import rgc
-
-            roots = [gcref for gcref in rgc.get_rpy_roots() if gcref]
-            pending = roots[:]
-            while pending:
-                gcref = pending.pop()
-                if not rgc.get_gcflag_extra(gcref):
-                    rgc.toggle_gcflag_extra(gcref)
-                    w_obj = rgc.try_cast_gcref_to_instance(model.W_Object, gcref)
-                    if (w_obj is not None and w_obj.has_class()
-                        and w_obj.getclass(space) is w_class):
-                        match_w.append(w_obj)
-                    pending.extend(rgc.get_rpy_referents(gcref))
-
-            while roots:
-                gcref = roots.pop()
-                if rgc.get_gcflag_extra(gcref):
-                    rgc.toggle_gcflag_extra(gcref)
-                    roots.extend(rgc.get_rpy_referents(gcref))
-            s_frame.store_instances_array(w_class, match_w)
-        return match_w
+        # TODO find a better way then always pre-collecting the entire list of objects.
+        instances = s_frame.instances_array(w_class)
+        if instances is None:
+            def filter_w_obj(w_obj, extra):
+                w_class, space = extra
+                return w_obj.has_class() and w_obj.getclass(space) is w_class
+            instances = gcrefs.collect_gc_references_of_type(model.W_Object, filter_w_obj, (w_class, space))
+            s_frame.store_instances_array(w_class, instances)
+        return instances
 
 @expose_primitive(SOME_INSTANCE, unwrap_spec=[object])
 def func(interp, s_frame, w_class):
@@ -935,32 +917,6 @@
         w_class = assert_pointers(w_class)
         w_class.as_class_get_shadow(interp.space).flush_method_caches()
     return w_rcvr
-
- at objectmodel.specialize.arg(0)
-def walk_gc_references(func, gcrefs):
-    from rpython.rlib import rgc
-    for gcref in gcrefs:
-        if gcref and not rgc.get_gcflag_extra(gcref):
-            try:
-                rgc.toggle_gcflag_extra(gcref)
-                func(gcref)
-                walk_gc_references(func, rgc.get_rpy_referents(gcref))
-            finally:
-                rgc.toggle_gcflag_extra(gcref)
-
- at objectmodel.specialize.arg(0)
-def walk_gc_objects(func):
-    from rpython.rlib import rgc
-    walk_gc_references(func, rgc.get_rpy_roots())
-
- at objectmodel.specialize.arg(0, 1)
-def walk_gc_objects_of_type(type, func):
-    from rpython.rlib import rgc
-    def check_type(gcref):
-        w_obj = rgc.try_cast_gcref_to_instance(type, gcref)
-        if w_obj:
-            func(w_obj)
-    walk_gc_objects(check_type)
     
 if not stm_enabled():
     # XXX: We don't have a global symbol cache. Instead, we walk all
@@ -969,7 +925,7 @@
     def func(interp, s_frame, w_rcvr):
         # This takes a long time (at least in interpreted mode), and is not really necessary.
         # We are monitoring changes to MethodDictionaries, so there is no need for the image to tell us.
-        #walk_gc_objects_of_type(shadow.MethodDictionaryShadow, lambda s_dict: s_dict.flush_method_cache())
+        # gcrefs.walk_gc_references_of_type(shadow.MethodDictionaryShadow, lambda s_dict: s_dict.flush_method_cache())
         return w_rcvr
 
 # ___________________________________________________________________________


More information about the pypy-commit mailing list