[Zodb-checkins] CVS: Zope/lib/python/BTrees - BTreeTemplate.c:1.59

Tim Peters tim.one@comcast.net
Mon, 17 Jun 2002 16:31:40 -0400


Update of /cvs-repository/Zope/lib/python/BTrees
In directory cvs.zope.org:/tmp/cvs-serv24085

Modified Files:
	BTreeTemplate.c 
Log Message:
_BTree_set():  This is BTree_grow's only caller, and BTree_grow() can leave
a BTree in an invalid state.  Normally, _BTree_set() repairs this before
return, but in case of error may not.  Now it does.


=== Zope/lib/python/BTrees/BTreeTemplate.c 1.58 => 1.59 ===
 
 /*
+** _BTree_clear
+**
+** Clears out all of the values in the BTree (firstbucket, keys, and children);
+** leaving self an empty BTree.
+**
+** Arguments:	self	The BTree
+**
+** Returns:	 0	on success
+**		-1	on failure
+**
+** Internal:  Deallocation order is important.  The danger is that a long
+** list of buckets may get freed "at once" via decref'ing the first bucket,
+** in which case a chain of consequenct Py_DECREF calls may blow the stack.
+** Luckily, every bucket has a refcount of at least two, one due to being a
+** BTree node's child, and another either because it's not the first bucket in
+** the chain (so the preceding bucket points to it), or because firstbucket
+** points to it.  By clearing in the natural depth-first, left-to-right
+** order, the BTree->bucket child pointers prevent Py_DECREF(bucket->next)
+** calls from freeing bucket->next, and the maximum stack depth is equal
+** to the height of the tree.
+**/
+static int
+_BTree_clear(BTree *self)
+{
+    const int len = self->len;
+
+    if (self->firstbucket) {
+	ASSERT(self->firstbucket->ob_refcnt > 1,
+	       "Invalid firstbucket pointer", -1);
+	Py_DECREF(self->firstbucket);
+	self->firstbucket = NULL;
+    }
+
+    if (self->data) {
+        int i;
+        if (len > 0) { /* 0 is special because key 0 is trash */
+            Py_DECREF(self->data[0].child);
+	}
+
+        for (i = 1; i < len; i++) {
+	    DECREF_KEY(self->data[i].key);
+            Py_DECREF(self->data[i].child);
+        }
+	free(self->data);
+	self->data = NULL;
+    }
+
+    self->len = self->size = 0;
+    return 0;
+}
+
+/*
   Set (value != 0) or delete (value=0) a tree item.
 
   If unique is non-zero, then only change if the key is
@@ -371,7 +423,7 @@
 
      1  Successful, number of entries changed, but firstbucket did not go away.
 
-     2  Successful, number of entires changed, firstbucket did go away.
+     2  Successful, number of entries changed, firstbucket did go away.
         This can only happen on a delete (value == NULL).  The caller may
         need to change its own firstbucket pointer, and in any case *someone*
         needs to adjust the 'next' pointer of the bucket immediately preceding
@@ -388,6 +440,7 @@
     BTreeItem *d;       /* self->data[min] */
     int childlength;    /* len(self->data[min].child) */
     int status;         /* our return value; and return value from callee */
+    int self_was_empty; /* was self empty at entry? */
 
     KEY_TYPE key;
     int copied = 1;
@@ -397,7 +450,8 @@
 
     PER_USE_OR_RETURN(self, -1);
 
-    if (! self->len) {
+    self_was_empty = self->len == 0;
+    if (self_was_empty) {
         /* We're empty.  Make room. */
 	if (value) {
 	    if (BTree_grow(self, 0, noval) < 0)
@@ -552,6 +606,12 @@
     return status;
 
 Error:
+    if (self_was_empty) {
+        /* BTree_grow may have left the BTree in an invalid state.  Make
+         * sure the tree is a legitimate empty tree.
+         */
+        _BTree_clear(self);
+    }
     status = -1;
     goto _return;
 }
@@ -573,58 +633,6 @@
 {
   if (_BTree_set(self, key, v, 0, 0) < 0) return -1;
   return 0;
-}
-
-/*
-** _BTree_clear
-**
-** Clears out all of the values in the BTree (firstbucket, keys, and children);
-** leaving self an empty BTree.
-**
-** Arguments:	self	The BTree
-**
-** Returns:	 0	on success
-**		-1	on failure
-**
-** Internal:  Deallocation order is important.  The danger is that a long
-** list of buckets may get freed "at once" via decref'ing the first bucket,
-** in which case a chain of consequenct Py_DECREF calls may blow the stack.
-** Luckily, every bucket has a refcount of at least two, one due to being a
-** BTree node's child, and another either because it's not the first bucket in
-** the chain (so the preceding bucket points to it), or because firstbucket
-** points to it.  By clearing in the natural depth-first, left-to-right
-** order, the BTree->bucket child pointers prevent Py_DECREF(bucket->next)
-** calls from freeing bucket->next, and the maximum stack depth is equal
-** to the height of the tree.
-**/
-static int
-_BTree_clear(BTree *self)
-{
-    const int len = self->len;
-
-    if (self->firstbucket) {
-	ASSERT(self->firstbucket->ob_refcnt > 1,
-	       "Invalid firstbucket pointer", -1);
-	Py_DECREF(self->firstbucket);
-	self->firstbucket = NULL;
-    }
-
-    if (self->data) {
-        int i;
-        if (len > 0) { /* 0 is special because key 0 is trash */
-            Py_DECREF(self->data[0].child);
-	}
-
-        for (i = 1; i < len; i++) {
-	    DECREF_KEY(self->data[i].key);
-            Py_DECREF(self->data[i].child);
-        }
-	free(self->data);
-	self->data = NULL;
-    }
-
-    self->len = self->size = 0;
-    return 0;
 }
 
 #ifdef PERSISTENT