diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c index c98247c..c21f556 100644 --- a/libraries/liblmdb/mdb.c +++ b/libraries/liblmdb/mdb.c @@ -899,6 +899,10 @@ struct MDB_txn { /** The list of pages that became unused during this transaction. */ MDB_IDL mt_free_pgs; + /** The list of loose pages that became unused and may be reused + * in this transaction. + */ + MDB_page *mt_loose_pgs; /** The sorted list of dirty pages we temporarily wrote to disk * because the dirty list was full. page numbers in here are * shifted left by 1, deleted slots have the LSB set. @@ -1022,7 +1026,6 @@ typedef struct MDB_xcursor { typedef struct MDB_pgstate { pgno_t *mf_pghead; /**< Reclaimed freeDB pages, or NULL before use */ txnid_t mf_pglast; /**< ID of last used record, or 0 if !mf_pghead */ - MDB_page *mf_pgloose; /**< Dirty pages that can be reused */ } MDB_pgstate; /** The database environment. */ @@ -1059,7 +1062,6 @@ struct MDB_env { MDB_pgstate me_pgstate; /**< state of old pages from freeDB */ # define me_pglast me_pgstate.mf_pglast # define me_pghead me_pgstate.mf_pghead -# define me_pgloose me_pgstate.mf_pgloose MDB_page *me_dpages; /**< list of malloc'd blocks for re-use */ /** IDL of pages that became unused in a write txn */ MDB_IDL me_free_pgs; @@ -1527,21 +1529,58 @@ mdb_dlist_free(MDB_txn *txn) dl[0].mid = 0; } -/** Loosen a single page. +/** Loosen or free a single page. * Saves single pages to a list for future reuse * in this same txn. It has been pulled from the freeDB * and already resides on the dirty list, but has been * deleted. Use these pages first before pulling again * from the freeDB. + * + * If the page wasn't dirtied in this txn, just add it + * to this txn's free list. */ -static void -mdb_page_loose(MDB_env *env, MDB_page *mp) +static int +mdb_page_loose(MDB_cursor *mc, MDB_page *mp) { + int loose = 0; + pgno_t pgno = mp->mp_pgno; + + if ((mp->mp_flags & P_DIRTY) && mc->mc_dbi != FREE_DBI) { + if (mc->mc_txn->mt_parent) { + MDB_ID2 *dl = mc->mc_txn->mt_u.dirty_list; + /* If txn has a parent, make sure the page is in our + * dirty list. + */ + if (dl[0].mid) { + unsigned x = mdb_mid2l_search(dl, pgno); + if (x <= dl[0].mid && dl[x].mid == pgno) { + if (mp != dl[x].mptr) { /* bad cursor? */ + mc->mc_flags &= ~(C_INITIALIZED|C_EOF); + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CORRUPTED; + } + /* ok, it's ours */ + loose = 1; + } + } + } else { + /* no parent txn, so it's just ours */ + loose = 1; + } + } + if (loose) { pgno_t *pp = (pgno_t *)mp->mp_ptrs; - *pp = mp->mp_pgno; - mp->mp_next = env->me_pgloose; - env->me_pgloose = mp; + *pp = pgno; + mp->mp_next = mc->mc_txn->mt_loose_pgs; + mc->mc_txn->mt_loose_pgs = mp; mp->mp_flags |= P_LOOSE; + } else { + int rc = mdb_midl_append(&mc->mc_txn->mt_free_pgs, pgno); + if (rc) + return rc; + } + + return MDB_SUCCESS; } /** Set or clear P_KEEP in dirty, non-overflow, non-sub pages watched by txn. @@ -1593,7 +1632,7 @@ mdb_pages_xkeep(MDB_cursor *mc, unsigned pflags, int all) } /* Loose pages shouldn't be spilled */ - for (dp = txn->mt_env->me_pgloose; dp; dp=dp->mp_next) { + for (dp = txn->mt_loose_pgs; dp; dp=dp->mp_next) { if ((dp->mp_flags & Mask) == pflags) dp->mp_flags ^= P_KEEP; } @@ -1826,10 +1865,10 @@ mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp) MDB_cursor m2; /* If there are any loose pages, just use them */ - if (num == 1 && env->me_pgloose) { + if (num == 1 && txn->mt_loose_pgs) { pgno_t *pp; - np = env->me_pgloose; - env->me_pgloose = np->mp_next; + np = txn->mt_loose_pgs; + txn->mt_loose_pgs = np->mp_next; pp = (pgno_t *)np->mp_ptrs; np->mp_pgno = *pp; *mp = np; @@ -2700,8 +2739,8 @@ mdb_freelist_save(MDB_txn *txn) /* Dispose of loose pages. Usually they will have all * been used up by the time we get here. */ - if (env->me_pgloose) { - MDB_page *mp = env->me_pgloose; + if (txn->mt_loose_pgs) { + MDB_page *mp = txn->mt_loose_pgs; pgno_t *pp; /* Just return them to freeDB */ if (env->me_pghead) { @@ -2726,7 +2765,7 @@ mdb_freelist_save(MDB_txn *txn) mp = mp->mp_next; } } - env->me_pgloose = NULL; + txn->mt_loose_pgs = NULL; } /* MDB_RESERVE cancels meminit in ovpage malloc (when no WRITEMAP) */ @@ -7343,15 +7382,11 @@ mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst) psrc = csrc->mc_pg[csrc->mc_top]; /* If not operating on FreeDB, allow this page to be reused - * in this txn. + * in this txn. Otherwise just add to free list. */ - if ((psrc->mp_flags & P_DIRTY) && csrc->mc_dbi != FREE_DBI) { - mdb_page_loose(csrc->mc_txn->mt_env, psrc); - } else { - rc = mdb_midl_append(&csrc->mc_txn->mt_free_pgs, psrc->mp_pgno); - if (rc) - return rc; - } + rc = mdb_page_loose(csrc, psrc); + if (rc) + return rc; if (IS_LEAF(psrc)) csrc->mc_db->md_leaf_pages--; else