mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-12-20 22:59:34 -05:00
ITS#7515 Fix mdb_txn_commit(nested txn).
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.
This commit is contained in:
parent
f43ae20be7
commit
208e5c614d
1 changed files with 42 additions and 24 deletions
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue