]> git.sur5r.net Git - openldap/commitdiff
ITS#7515 Fix mdb_txn_commit(nested txn).
authorHallvard Furuseth <hallvard@openldap.org>
Tue, 19 Feb 2013 21:03:41 +0000 (22:03 +0100)
committerHoward Chu <hyc@symas.com>
Wed, 20 Feb 2013 02:44:10 +0000 (18:44 -0800)
Don't modify the parent txn until the current txn cannot fail.
Don't assume new dirty child pgnos > dirty parent pgnos.

Page alloc/touch: Fail if child+parent dirty pages would exceed
dirty_list's maxsize. Avoids an error situation in commit.

libraries/liblmdb/mdb.c

index ab1e497395af4bcdc49a44040e9bc6caf61a0f46..d2cbba264880205af35b129da9c9eee962e3ddc4 100644 (file)
@@ -844,6 +844,8 @@ struct MDB_txn {
 #define MDB_TXN_DIRTY          0x04            /**< must write, even if dirty list is empty */
 /** @} */
        unsigned int    mt_flags;               /**< @ref mdb_txn */
+       /** dirty_list maxsize - #allocated pages including in parent txns */
+       unsigned int    mt_dirty_room;
        /** Tracks which of the two meta pages was used at the start
         *      of this transaction.
         */
@@ -1285,7 +1287,7 @@ mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp)
        *mp = NULL;
 
        /* If our dirty list is already full, we can't do anything */
-       if (txn->mt_u.dirty_list[0].mid >= MDB_IDL_UM_MAX)
+       if (txn->mt_dirty_room == 0)
                return MDB_TXN_FULL;
 
        /* The free list won't have any content at all until txn 2 has
@@ -1524,6 +1526,7 @@ none:
        } else {
                mdb_mid2l_insert(txn->mt_u.dirty_list, &mid);
        }
+       txn->mt_dirty_room--;
        *mp = np;
 
        return MDB_SUCCESS;
@@ -1628,8 +1631,6 @@ finish:
                                return 0;
                        }
                }
-               if (mc->mc_txn->mt_u.dirty_list[0].mid >= MDB_IDL_UM_MAX)
-                       return MDB_TXN_FULL;
                /* No - copy it */
                np = mdb_page_malloc(mc);
                if (!np)
@@ -1821,6 +1822,7 @@ mdb_txn_renew0(MDB_txn *txn)
                if (txn->mt_txnid == mdb_debug_start)
                        mdb_debug = 1;
 #endif
+               txn->mt_dirty_room = MDB_IDL_UM_MAX;
                txn->mt_u.dirty_list = env->me_dirty_list;
                txn->mt_u.dirty_list[0].mid = 0;
                txn->mt_free_pgs = env->me_free_pgs;
@@ -1921,6 +1923,7 @@ mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **ret)
                }
                txn->mt_txnid = parent->mt_txnid;
                txn->mt_toggle = parent->mt_toggle;
+               txn->mt_dirty_room = parent->mt_dirty_room;
                txn->mt_u.dirty_list[0].mid = 0;
                txn->mt_free_pgs[0] = 0;
                txn->mt_next_pgno = parent->mt_next_pgno;
@@ -2099,9 +2102,16 @@ mdb_txn_commit(MDB_txn *txn)
 
        if (txn->mt_parent) {
                MDB_txn *parent = txn->mt_parent;
-               unsigned x, y;
+               unsigned x, y, len;
                MDB_ID2L dst, src;
 
+               /* Append our free list to parent's */
+               if (mdb_midl_append_list(&parent->mt_free_pgs, txn->mt_free_pgs)) {
+                       mdb_txn_abort(txn);
+                       return ENOMEM;
+               }
+               mdb_midl_free(txn->mt_free_pgs);
+
                parent->mt_next_pgno = txn->mt_next_pgno;
                parent->mt_flags = txn->mt_flags;
 
@@ -2113,32 +2123,40 @@ mdb_txn_commit(MDB_txn *txn)
                memcpy(parent->mt_dbflags, txn->mt_dbflags, txn->mt_numdbs);
                txn->mt_parent->mt_numdbs = txn->mt_numdbs;
 
-               /* Append our free list to parent's */
-               mdb_midl_append_list(&txn->mt_parent->mt_free_pgs,
-                       txn->mt_free_pgs);
-               mdb_midl_free(txn->mt_free_pgs);
-
-               /* Merge our dirty list with parent's */
                dst = txn->mt_parent->mt_u.dirty_list;
                src = txn->mt_u.dirty_list;
-               x = mdb_mid2l_search(dst, src[1].mid);
-               for (y=1; y<=src[0].mid; y++) {
-                       while (x <= dst[0].mid && dst[x].mid != src[y].mid) x++;
-                       if (x > dst[0].mid)
-                               break;
-                       free(dst[x].mptr);
-                       dst[x].mptr = src[y].mptr;
-               }
+               /* Find len = length of merging our dirty list with parent's */
                x = dst[0].mid;
-               for (; y<=src[0].mid; y++) {
-                       if (++x >= MDB_IDL_UM_MAX) {
-                               mdb_txn_abort(txn);
-                               return MDB_TXN_FULL;
+               dst[0].mid = 0;         /* simplify loops */
+               if (parent->mt_parent) {
+                       len = x + src[0].mid;
+                       y = mdb_mid2l_search(src, dst[x].mid + 1) - 1;
+                       for (i = x; y && i; y--) {
+                               pgno_t yp = src[y].mid;
+                               while (yp < dst[i].mid)
+                                       i--;
+                               if (yp == dst[i].mid) {
+                                       i--;
+                                       len--;
+                               }
                        }
-                       dst[x] = src[y];
+               } else { /* Simplify the above for single-ancestor case */
+                       len = MDB_IDL_UM_MAX - txn->mt_dirty_room;
                }
-               dst[0].mid = x;
+               /* Merge our dirty list with parent's */
+               y = src[0].mid;
+               for (i = len; y; dst[i--] = src[y--]) {
+                       pgno_t yp = src[y].mid;
+                       while (yp < dst[x].mid)
+                               dst[i--] = dst[x--];
+                       if (yp == dst[x].mid)
+                               free(dst[x--].mptr);
+               }
+               assert(i == x);
+               dst[0].mid = len;
                free(txn->mt_u.dirty_list);
+               parent->mt_dirty_room = txn->mt_dirty_room;
+
                txn->mt_parent->mt_child = NULL;
                free(((MDB_ntxn *)txn)->mnt_pgstate.mf_pgfree);
                free(txn);