[Zodb-checkins] CVS: Packages/StorageGC - CyclicGC.py:1.15 GCableGraph.py:1.9

tim@digicool.com tim@digicool.com
Tue, 24 Apr 2001 18:24:19 -0400 (EDT)


Update of /cvs-repository/Packages/StorageGC
In directory korak:/tmp/cvs-serv8270

Modified Files:
	CyclicGC.py GCableGraph.py 
Log Message:
After talking w/ Jim, added two new CycleGC methods "for Barry":

.safeToDecref() -> bool, true when and only when it's OK for the storage
to delete objects and/or references.  This answers Barry's good questions
in a way that's easier for both of us <wink>.  See the interface comment-
docs for more info.  Note that this isn't merely a synonym for .done()
(although .done() *implies* .safeToDecref(), the latter *can* be true at
times .done() isn't).

.abort() -> None.  When the storage, e.g., wants to pack, it can't actually
delete any references or objects until CycleGC.safeToDecref() is true.  But
the storage has no way to guess how long it may take CycleGC to finish, and
indeed neither does CycleGC (it depends on all the details of the graph
structure, and CycleGC is busy figuring those out).  So it's at least
prudent to give the storage a way to force CycleGC to abandon analysis
immediately.  Of course .safeToDecref() is true after .abort().



--- Updated File CyclicGC.py in package Packages/StorageGC --
--- CyclicGC.py	2001/04/24 21:21:08	1.14
+++ CyclicGC.py	2001/04/24 22:24:18	1.15
@@ -38,13 +38,19 @@
 # is true.  That is, if the storage doesn't destroy anything before
 # CycleGC is finished, then of course CycleGC won't ask the storage about
 # oids that don't exist anymore.
+#
+# CAUTION:  CycleGC is robust against new references popping into existence
+# between stages.  However, the storage must never delete a reference or
+# object except when CycleGC.safeToDecref() returns true; violating this
+# rule may cause CycleGC to call GCable.gcTrash() with objects that aren't
+# actually trash.
 
 ###########################################################################
 # The CycleGC interface.
 # The CycleGC class here supplies the following methods for use by
 # refcounted storages desiring detecting of unreachable cycles.
 #
-# Typical use:
+# Possible use:
 #
 #     collector = CycleGC(self)
 #     collector.start(list_of_suspect_objects)
@@ -86,8 +92,20 @@
 #     XXX      elapsed?  Not yet clear what would be most useful.
 #
 # CycleGC.done() -> bool
-#     Return true when analysis is complete, false if .resume() has more
-#     work to do.
+#     Return true when analysis is complete, of all objects ever passed to
+#     .start(); false if .resume() has more work to do.  Note that .done()
+#     implies .safeToDecref().
+#
+# CycleGC.abort() -> None
+#     Abandon all analysis.  After, .done() and .safeToDecref() are true.
+#
+# CycleGC.safeToDecref() -> bool
+#     Return true if CycleGC is in a stage of its analysis where it's OK
+#     for the storage to delete references and objects.  The storage must
+#     never delete references or objects when .safeToDecref() returns
+#     false.  .safeToDecref() is initially true, is true whenever .done()
+#     returns true, and is true between the time GCable.gcTrash() is called
+#     and the storage invokes .resume() again.
 
 ###########################################################################
 # The approach takes a bit from many techniques in the literature, but is
@@ -186,6 +204,9 @@
         # self._workflow.
         self._stage = 0
 
+        # Is it safe for the storage to delete objects and references?
+        self._is_decref_safe = 1
+
     def start(self, objs):
         """Objs is a list of oids that *may* be in trash cycles.
 
@@ -256,14 +277,36 @@
 
         return len(self._allobjds) == 0
 
+    def abort(self):
+        """Abandon all analysis.
+
+        After, .done() and .safeToDecref() return true.
+        """
+
+        self._clear_current_work()
+        self._pending = []
+        assert self.done()
+        assert self.safeToDecref()
+
+    def safeToDecref(self):
+        """Return true if it's safe for the storage to delete refs.
+
+        The storage must never delete references or objects when this
+        returns false.
+        """
+
+        return self._is_decref_safe
+
     def _build_descriptors(self, objs):
         "Build descriptors for the passed-in objects."
 
         assert len(self._allobjds) == 0
         assert len(self._obj2d) == 0
-        for x in objs:
-            # Note that _get_initial_info() weeds out duplicates.
-            self._get_initial_info(x)
+        if objs:
+            self._is_decref_safe = 0
+            for x in objs:
+                # Note that _get_initial_info() weeds out duplicates.
+                self._get_initial_info(x)
 
     def _get_initial_info(self, obj):
         """Return descriptor for obj, building a new one if obj is new.
@@ -407,14 +450,14 @@
                                      "refcount %d for OID %s" %
                                      (d[RC], storagerc, repr(obj)))
 
+        self._is_decref_safe = 1
         if result:
             self._storage.gcTrash(result)
 
     def _start_next_batch(self):
         "Start next batch of work (if any)."
 
-        self._allobjds = []
-        self._obj2d.clear()
+        self._clear_current_work()
         if self._pending:
             temp = self._pending
             self._pending = []
@@ -426,8 +469,13 @@
 
     def _back_to_stage0(self):
         "Restart the virtual machine."
-
         self._stage = 0
+
+    def _clear_current_work(self):
+        "Forget everything about the current start set."
+        self._obj2d.clear()
+        self._allobjds = []
+        self._is_decref_safe = 1
 
     def _getkids(self, obj):
         "Return list of obj's immediate successors."

--- Updated File GCableGraph.py in package Packages/StorageGC --
--- GCableGraph.py	2001/04/23 05:12:23	1.8
+++ GCableGraph.py	2001/04/24 22:24:18	1.9
@@ -128,8 +128,10 @@
         print "    starting gc ...",
         start = clock()
         gc.start(roots)
+        assert gc.safeToDecref() == (len(roots) == 0)
         while not gc.done():
             gc.resume(1)
+        assert gc.safeToDecref()
         finish = clock()
         print "and done with gc, in %.3f seconds" % (finish - start)