#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.
*/
*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
} else {
mdb_mid2l_insert(txn->mt_u.dirty_list, &mid);
}
+ txn->mt_dirty_room--;
*mp = np;
return MDB_SUCCESS;
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)
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;
}
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;
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;
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);