#include <fcntl.h>
#endif
+#if defined(__mips) && defined(__linux)
+/* MIPS has cache coherency issues, requires explicit cache control */
+#include <asm/cachectl.h>
+extern int cacheflush(char *addr, int nbytes, int cache);
+#define CACHEFLUSH(addr, bytes, cache) cacheflush(addr, bytes, cache)
+#else
+#define CACHEFLUSH(addr, bytes, cache)
+#endif
+
+
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <time.h>
#include <unistd.h>
+#if defined(__sun)
+/* Most platforms have posix_memalign, older may only have memalign */
+#define HAVE_MEMALIGN 1
+#include <malloc.h>
+#endif
+
#if !(defined(BYTE_ORDER) || defined(__BYTE_ORDER))
#include <netinet/in.h>
#include <resolv.h> /* defines BYTE_ORDER on HPUX and Solaris */
# error "Two's complement, reasonably sized integer types, please"
#endif
+#ifdef __GNUC__
+/** Put infrequently used env functions in separate section */
+# ifdef __APPLE__
+# define ESECT __attribute__ ((section("__TEXT,text_env")))
+# else
+# define ESECT __attribute__ ((section("text_env")))
+# endif
+#else
+#define ESECT
+#endif
+
/** @defgroup internal LMDB Internals
* @{
*/
* @{
*/
+ /** Features under development */
+#ifndef MDB_DEVEL
+#define MDB_DEVEL 0
+#endif
+
/** Wrapper around __func__, which is a C99 feature */
#if __STDC_VERSION__ >= 199901L
# define mdb_func_ __func__
#define pthread_key_delete(x) TlsFree(x)
#define pthread_getspecific(x) TlsGetValue(x)
#define pthread_setspecific(x,y) (TlsSetValue(x,y) ? 0 : ErrCode())
-#define pthread_mutex_unlock(x) ReleaseMutex(x)
-#define pthread_mutex_lock(x) WaitForSingleObject(x, INFINITE)
+#define pthread_mutex_unlock(x) ReleaseMutex(*x)
+#define pthread_mutex_lock(x) WaitForSingleObject(*x, INFINITE)
#define pthread_cond_signal(x) SetEvent(*x)
-#define pthread_cond_wait(cond,mutex) SignalObjectAndWait(*mutex, *cond, INFINITE, FALSE); WaitForSingleObject(*mutex, INFINITE)
+#define pthread_cond_wait(cond,mutex) do{SignalObjectAndWait(*mutex, *cond, INFINITE, FALSE); WaitForSingleObject(*mutex, INFINITE);}while(0)
#define THREAD_CREATE(thr,start,arg) thr=CreateThread(NULL,0,start,arg,0,NULL)
#define THREAD_FINISH(thr) WaitForSingleObject(thr, INFINITE)
-#define LOCK_MUTEX_R(env) pthread_mutex_lock((env)->me_rmutex)
-#define UNLOCK_MUTEX_R(env) pthread_mutex_unlock((env)->me_rmutex)
-#define LOCK_MUTEX_W(env) pthread_mutex_lock((env)->me_wmutex)
-#define UNLOCK_MUTEX_W(env) pthread_mutex_unlock((env)->me_wmutex)
+#define LOCK_MUTEX_R(env) pthread_mutex_lock(&(env)->me_rmutex)
+#define UNLOCK_MUTEX_R(env) pthread_mutex_unlock(&(env)->me_rmutex)
+#define LOCK_MUTEX_W(env) pthread_mutex_lock(&(env)->me_wmutex)
+#define UNLOCK_MUTEX_W(env) pthread_mutex_unlock(&(env)->me_wmutex)
#define getpid() GetCurrentProcessId()
#define MDB_FDATASYNC(fd) (!FlushFileBuffers(fd))
#define MDB_MSYNC(addr,len,flags) (!FlushViewOfFile(addr,len))
/** @brief The maximum size of a database page.
*
- * This is 32k, since it must fit in #MDB_page.%mp_upper.
+ * It is 32k or 64k, since value-PAGEBASE must fit in
+ * #MDB_page.%mp_upper.
*
* LMDB will use database pages < OS pages if needed.
* That causes more I/O in write transactions: The OS must
* pressure from other processes is high. So until OSs have
* actual paging support for Huge pages, they're not viable.
*/
-#define MAX_PAGESIZE 0x8000
+#define MAX_PAGESIZE (PAGEBASE ? 0x10000 : 0x8000)
/** The minimum number of keys required in a database page.
* Setting this to a larger value will place a smaller bound on the
#define MDB_MAGIC 0xBEEFC0DE
/** The version number for a database's datafile format. */
-#define MDB_DATA_VERSION 1
+#define MDB_DATA_VERSION ((MDB_DEVEL) ? 999 : 1)
/** The version number for a database's lockfile format. */
#define MDB_LOCK_VERSION 1
* Define this as 0 to compute the max from the page size. 511
* is default for backwards compat: liblmdb <= 0.9.10 can break
* when modifying a DB with keys/dupsort data bigger than its max.
+ * #MDB_DEVEL sets the default to 0.
*
* Data items in an #MDB_DUPSORT database are also limited to
* this size, since they're actually keys of a sub-DB. Keys and
* #MDB_DUPSORT data items must fit on a node in a regular page.
*/
#ifndef MDB_MAXKEYSIZE
-#define MDB_MAXKEYSIZE 511
+#define MDB_MAXKEYSIZE ((MDB_DEVEL) ? 0 : 511)
#endif
/** The maximum size of a key we can write to the environment. */
#define mp_next mp_p.p_next
union {
pgno_t p_pgno; /**< page number */
- void * p_next; /**< for in-memory list of freed structs */
+ struct MDB_page *p_next; /**< for in-memory list of freed pages */
} mp_p;
uint16_t mp_pad;
/** @defgroup mdb_page Page Flags
/** Address of first usable data byte in a page, after the header */
#define METADATA(p) ((void *)((char *)(p) + PAGEHDRSZ))
+ /** ITS#7713, change PAGEBASE to handle 65536 byte pages */
+#define PAGEBASE ((MDB_DEVEL) ? PAGEHDRSZ : 0)
+
/** Number of nodes on a page */
-#define NUMKEYS(p) (((p)->mp_lower - PAGEHDRSZ) >> 1)
+#define NUMKEYS(p) (((p)->mp_lower - (PAGEHDRSZ-PAGEBASE)) >> 1)
/** The amount of space remaining in the page */
#define SIZELEFT(p) (indx_t)((p)->mp_upper - (p)->mp_lower)
/** The number of overflow pages needed to store the given size. */
#define OVPAGES(size, psize) ((PAGEHDRSZ-1 + (size)) / (psize) + 1)
- /** Link in #MDB_txn.%mt_loose_pages list */
-#define NEXT_LOOSE_PAGE(p) (*(MDB_page **)METADATA(p))
+ /** Link in #MDB_txn.%mt_loose_pgs list */
+#define NEXT_LOOSE_PAGE(p) (*(MDB_page **)((p) + 2))
/** Header for a single key/data pair within a page.
* Used in pages of type #P_BRANCH and #P_LEAF without #P_LEAF2.
#define LEAFSIZE(k, d) (NODESIZE + (k)->mv_size + (d)->mv_size)
/** Address of node \b i in page \b p */
-#define NODEPTR(p, i) ((MDB_node *)((char *)(p) + (p)->mp_ptrs[i]))
+#define NODEPTR(p, i) ((MDB_node *)((char *)(p) + (p)->mp_ptrs[i] + PAGEBASE))
/** Address of the key for the node */
#define NODEKEY(node) (void *)((node)->mn_data)
* in this transaction, linked through #NEXT_LOOSE_PAGE(page).
*/
MDB_page *mt_loose_pgs;
+ /* #Number of loose pages (#mt_loose_pgs) */
+ int mt_loose_count;
/** 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.
MDB_dbx *mt_dbxs;
/** Array of MDB_db records for each known DB */
MDB_db *mt_dbs;
+ /** Array of sequence numbers for each DB handle */
+ unsigned int *mt_dbiseqs;
/** @defgroup mt_dbflag Transaction DB Flags
* @ingroup internal
* @{
MDB_meta *me_metas[2]; /**< pointers to the two meta pages */
void *me_pbuf; /**< scratch area for DUPSORT put() */
MDB_txn *me_txn; /**< current write transaction */
+ MDB_txn *me_txn0; /**< prealloc'd write transaction */
size_t me_mapsize; /**< size of the data memory map */
off_t me_size; /**< current file size */
pgno_t me_maxpg; /**< me_mapsize / me_psize */
MDB_dbx *me_dbxs; /**< array of static DB info */
uint16_t *me_dbflags; /**< array of flags from MDB_db.md_flags */
+ unsigned int *me_dbiseqs; /**< array of dbi sequence numbers */
pthread_key_t me_txkey; /**< thread-key for readers */
+ txnid_t me_pgoldest; /**< ID of oldest reader last time we looked */
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 TXN_DBI_EXIST(txn, dbi) \
((txn) && (dbi) < (txn)->mt_numdbs && ((txn)->mt_dbflags[dbi] & DB_VALID))
+ /** Check for misused \b dbi handles */
+#define TXN_DBI_CHANGED(txn, dbi) \
+ ((txn)->mt_dbiseqs[dbi] != (txn)->mt_env->me_dbiseqs[dbi])
+
static int mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp);
static int mdb_page_new(MDB_cursor *mc, uint32_t flags, int num, MDB_page **mp);
static int mdb_page_touch(MDB_cursor *mc);
"MDB_BAD_RSLOT: Invalid reuse of reader locktable slot",
"MDB_BAD_TXN: Transaction cannot recover - it must be aborted",
"MDB_BAD_VALSIZE: Unsupported size of key/DB name/data, or wrong DUPFIXED size",
+ "MDB_BAD_DBI: The specified DBI handle was closed/changed unexpectedly",
};
char *
mdb_strerror(int err)
{
+#ifdef _WIN32
+ /** HACK: pad 4KB on stack over the buf. Return system msgs in buf.
+ * This works as long as no function between the call to mdb_strerror
+ * and the actual use of the message uses more than 4K of stack.
+ */
+ char pad[4096];
+ char buf[1024], *ptr = buf;
+#endif
int i;
if (!err)
return ("Successful return: 0");
return mdb_errstr[i];
}
+#ifdef _WIN32
+ /* These are the C-runtime error codes we use. The comment indicates
+ * their numeric value, and the Win32 error they would correspond to
+ * if the error actually came from a Win32 API. A major mess, we should
+ * have used LMDB-specific error codes for everything.
+ */
+ switch(err) {
+ case ENOENT: /* 2, FILE_NOT_FOUND */
+ case EIO: /* 5, ACCESS_DENIED */
+ case ENOMEM: /* 12, INVALID_ACCESS */
+ case EACCES: /* 13, INVALID_DATA */
+ case EBUSY: /* 16, CURRENT_DIRECTORY */
+ case EINVAL: /* 22, BAD_COMMAND */
+ case ENOSPC: /* 28, OUT_OF_PAPER */
+ return strerror(err);
+ default:
+ ;
+ }
+ buf[0] = 0;
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err, 0, ptr, sizeof(buf), pad);
+ return ptr;
+#else
return strerror(err);
+#endif
}
/** assert(3) variant in cursor context */
total = EVEN(total);
}
fprintf(stderr, "Total: header %d + contents %d + unused %d\n",
- IS_LEAF2(mp) ? PAGEHDRSZ : mp->mp_lower, total, SIZELEFT(mp));
+ IS_LEAF2(mp) ? PAGEHDRSZ : PAGEBASE + mp->mp_lower, total, SIZELEFT(mp));
}
void
{
int loose = 0;
pgno_t pgno = mp->mp_pgno;
+ MDB_txn *txn = mc->mc_txn;
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->mt_parent) {
+ MDB_ID2 *dl = txn->mt_u.dirty_list;
/* If txn has a parent, make sure the page is in our
* dirty list.
*/
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;
+ txn->mt_flags |= MDB_TXN_ERROR;
return MDB_CORRUPTED;
}
/* ok, it's ours */
}
}
if (loose) {
- NEXT_LOOSE_PAGE(mp) = mc->mc_txn->mt_loose_pgs;
- mc->mc_txn->mt_loose_pgs = mp;
+ DPRINTF(("loosen db %d page %"Z"u", DDBI(mc),
+ mp->mp_pgno));
+ NEXT_LOOSE_PAGE(mp) = txn->mt_loose_pgs;
+ txn->mt_loose_pgs = mp;
+ txn->mt_loose_count++;
mp->mp_flags |= P_LOOSE;
} else {
- int rc = mdb_midl_append(&mc->mc_txn->mt_free_pgs, pgno);
+ int rc = mdb_midl_append(&txn->mt_free_pgs, pgno);
if (rc)
return rc;
}
static int
mdb_pages_xkeep(MDB_cursor *mc, unsigned pflags, int all)
{
- enum { Mask = P_SUBP|P_DIRTY|P_KEEP };
+ enum { Mask = P_SUBP|P_DIRTY|P_LOOSE|P_KEEP };
MDB_txn *txn = mc->mc_txn;
MDB_cursor *m3;
MDB_xcursor *mx;
break;
}
- /* Loose pages shouldn't be spilled */
- for (dp = txn->mt_loose_pgs; dp; dp = NEXT_LOOSE_PAGE(dp)) {
- if ((dp->mp_flags & Mask) == pflags)
- dp->mp_flags ^= P_KEEP;
- }
-
if (all) {
/* Mark dirty root pages */
for (i=0; i<txn->mt_numdbs; i++) {
for (i=dl[0].mid; i && need; i--) {
MDB_ID pn = dl[i].mid << 1;
dp = dl[i].mptr;
- if (dp->mp_flags & P_KEEP)
+ if (dp->mp_flags & (P_LOOSE|P_KEEP))
continue;
/* Can't spill twice, make sure it's not already in a parent's
* spill list.
#else
enum { Paranoid = 0, Max_retries = INT_MAX /*infinite*/ };
#endif
- int rc, retry = num * 20;
+ int rc, retry = num * 60;
MDB_txn *txn = mc->mc_txn;
MDB_env *env = txn->mt_env;
pgno_t pgno, *mop = env->me_pghead;
- unsigned i, j, k, mop_len = mop ? mop[0] : 0, n2 = num-1;
+ unsigned i, j, mop_len = mop ? mop[0] : 0, n2 = num-1;
MDB_page *np;
txnid_t oldest = 0, last;
MDB_cursor_op op;
MDB_cursor m2;
+ int found_old = 0;
/* If there are any loose pages, just use them */
if (num == 1 && txn->mt_loose_pgs) {
np = txn->mt_loose_pgs;
txn->mt_loose_pgs = NEXT_LOOSE_PAGE(np);
+ txn->mt_loose_count--;
+ DPRINTF(("db %d use loose page %"Z"u", DDBI(mc),
+ np->mp_pgno));
*mp = np;
return MDB_SUCCESS;
}
for (op = MDB_FIRST;; op = MDB_NEXT) {
MDB_val key, data;
MDB_node *leaf;
- pgno_t *idl, old_id, new_id;
+ pgno_t *idl;
/* Seek a big enough contiguous page range. Prefer
* pages at the tail, just truncating the list.
if (op == MDB_FIRST) { /* 1st iteration */
/* Prepare to fetch more and coalesce */
- oldest = mdb_find_oldest(txn);
last = env->me_pglast;
+ oldest = env->me_pgoldest;
mdb_cursor_init(&m2, txn, FREE_DBI, NULL);
if (last) {
op = MDB_SET_RANGE;
last++;
/* Do not fetch more if the record will be too recent */
- if (oldest <= last)
- break;
+ if (oldest <= last) {
+ if (!found_old) {
+ oldest = mdb_find_oldest(txn);
+ env->me_pgoldest = oldest;
+ found_old = 1;
+ }
+ if (oldest <= last)
+ break;
+ }
rc = mdb_cursor_get(&m2, &key, NULL, op);
if (rc) {
if (rc == MDB_NOTFOUND)
goto fail;
}
last = *(txnid_t*)key.mv_data;
- if (oldest <= last)
- break;
+ if (oldest <= last) {
+ if (!found_old) {
+ oldest = mdb_find_oldest(txn);
+ env->me_pgoldest = oldest;
+ found_old = 1;
+ }
+ if (oldest <= last)
+ break;
+ }
np = m2.mc_pg[m2.mc_top];
leaf = NODEPTR(np, m2.mc_ki[m2.mc_top]);
if ((rc = mdb_node_read(txn, leaf, &data)) != MDB_SUCCESS)
#if (MDB_DEBUG) > 1
DPRINTF(("IDL read txn %"Z"u root %"Z"u num %u",
last, txn->mt_dbs[FREE_DBI].md_root, i));
- for (k = i; k; k--)
- DPRINTF(("IDL %"Z"u", idl[k]));
+ for (j = i; j; j--)
+ DPRINTF(("IDL %"Z"u", idl[j]));
#endif
/* Merge in descending sorted order */
- j = mop_len;
- k = mop_len += i;
- mop[0] = (pgno_t)-1;
- old_id = mop[j];
- while (i) {
- new_id = idl[i--];
- for (; old_id < new_id; old_id = mop[--j])
- mop[k--] = old_id;
- mop[k--] = new_id;
- }
- mop[0] = mop_len;
+ mdb_midl_xmerge(mop, idl);
+ mop_len = mop[0];
}
/* Use new pages from the map when nothing suitable in the freeDB */
* alignment so memcpy may copy words instead of bytes.
*/
if ((unused &= -Align) && !IS_LEAF2(src)) {
- upper &= -Align;
- memcpy(dst, src, (lower + (Align-1)) & -Align);
+ upper = (upper + PAGEBASE) & -Align;
+ memcpy(dst, src, (lower + PAGEBASE + (Align-1)) & -Align);
memcpy((pgno_t *)((char *)dst+upper), (pgno_t *)((char *)src+upper),
psize - upper);
} else {
txn->mt_free_pgs[0] = 0;
txn->mt_spill_pgs = NULL;
env->me_txn = txn;
+ memcpy(txn->mt_dbiseqs, env->me_dbiseqs, env->me_maxdbs * sizeof(unsigned int));
}
/* Copy the DB info and flags */
tsize = sizeof(MDB_ntxn);
}
size = tsize + env->me_maxdbs * (sizeof(MDB_db)+1);
- if (!(flags & MDB_RDONLY))
+ if (!(flags & MDB_RDONLY)) {
+ if (!parent) {
+ txn = env->me_txn0;
+ txn->mt_flags = 0;
+ goto ok;
+ }
size += env->me_maxdbs * sizeof(MDB_cursor *);
+ /* child txns use parent's dbiseqs */
+ if (!parent)
+ size += env->me_maxdbs * sizeof(unsigned int);
+ }
if ((txn = calloc(1, size)) == NULL) {
- DPRINTF(("calloc: %s", strerror(ErrCode())));
+ DPRINTF(("calloc: %s", strerror(errno)));
return ENOMEM;
}
txn->mt_dbs = (MDB_db *) ((char *)txn + tsize);
if (flags & MDB_RDONLY) {
txn->mt_flags |= MDB_TXN_RDONLY;
txn->mt_dbflags = (unsigned char *)(txn->mt_dbs + env->me_maxdbs);
+ txn->mt_dbiseqs = env->me_dbiseqs;
} else {
txn->mt_cursors = (MDB_cursor **)(txn->mt_dbs + env->me_maxdbs);
- txn->mt_dbflags = (unsigned char *)(txn->mt_cursors + env->me_maxdbs);
+ if (parent) {
+ txn->mt_dbiseqs = parent->mt_dbiseqs;
+ txn->mt_dbflags = (unsigned char *)(txn->mt_cursors + env->me_maxdbs);
+ } else {
+ txn->mt_dbiseqs = (unsigned int *)(txn->mt_cursors + env->me_maxdbs);
+ txn->mt_dbflags = (unsigned char *)(txn->mt_dbiseqs + env->me_maxdbs);
+ }
}
txn->mt_env = env;
+ok:
if (parent) {
unsigned int i;
txn->mt_u.dirty_list = malloc(sizeof(MDB_ID2)*MDB_IDL_UM_SIZE);
} else {
rc = mdb_txn_renew0(txn);
}
- if (rc)
- free(txn);
- else {
+ if (rc) {
+ if (txn != env->me_txn0)
+ free(txn);
+ } else {
*ret = txn;
DPRINTF(("begin txn %"Z"u%c %p on mdbenv %p, root page %"Z"u",
txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w',
env->me_dbflags[i] = txn->mt_dbs[i].md_flags | MDB_VALID;
} else {
char *ptr = env->me_dbxs[i].md_name.mv_data;
- env->me_dbxs[i].md_name.mv_data = NULL;
- env->me_dbxs[i].md_name.mv_size = 0;
- env->me_dbflags[i] = 0;
- free(ptr);
+ if (ptr) {
+ env->me_dbxs[i].md_name.mv_data = NULL;
+ env->me_dbxs[i].md_name.mv_size = 0;
+ env->me_dbflags[i] = 0;
+ env->me_dbiseqs[i]++;
+ free(ptr);
+ }
}
}
}
if ((txn->mt_flags & MDB_TXN_RDONLY) && txn->mt_u.reader)
txn->mt_u.reader->mr_pid = 0;
- free(txn);
+ if (txn != txn->mt_env->me_txn0)
+ free(txn);
}
/** Save the freelist as of this transaction to the freeDB.
return rc;
}
- /* Dispose of loose pages. Usually they will have all
- * been used up by the time we get here.
- */
- if (txn->mt_loose_pgs) {
+ if (!env->me_pghead && txn->mt_loose_pgs) {
+ /* Put loose page numbers in mt_free_pgs, since
+ * we may be unable to return them to me_pghead.
+ */
MDB_page *mp = txn->mt_loose_pgs;
- /* Just return them to freeDB */
- if (env->me_pghead) {
- int i, j;
- mop = env->me_pghead;
- for (; mp; mp = NEXT_LOOSE_PAGE(mp)) {
- pgno_t pg = mp->mp_pgno;
- j = mop[0] + 1;
- for (i = mop[0]; i && mop[i] < pg; i--)
- mop[j--] = mop[i];
- mop[j] = pg;
- mop[0] += 1;
- }
- } else {
- /* Oh well, they were wasted. Put on freelist */
- for (; mp; mp = NEXT_LOOSE_PAGE(mp)) {
- mdb_midl_append(&txn->mt_free_pgs, mp->mp_pgno);
- }
- }
+ if ((rc = mdb_midl_need(&txn->mt_free_pgs, txn->mt_loose_count)) != 0)
+ return rc;
+ for (; mp; mp = NEXT_LOOSE_PAGE(mp))
+ mdb_midl_xappend(txn->mt_free_pgs, mp->mp_pgno);
txn->mt_loose_pgs = NULL;
+ txn->mt_loose_count = 0;
}
/* MDB_RESERVE cancels meminit in ovpage malloc (when no WRITEMAP) */
}
mop = env->me_pghead;
- mop_len = mop ? mop[0] : 0;
+ mop_len = (mop ? mop[0] : 0) + txn->mt_loose_count;
/* Reserve records for me_pghead[]. Split it if multi-page,
* to avoid searching freeDB for a page range. Use keys in
total_room += head_room;
}
+ /* Return loose page numbers to me_pghead, though usually none are
+ * left at this point. The pages themselves remain in dirty_list.
+ */
+ if (txn->mt_loose_pgs) {
+ MDB_page *mp = txn->mt_loose_pgs;
+ unsigned count = txn->mt_loose_count;
+ MDB_IDL loose;
+ /* Room for loose pages + temp IDL with same */
+ if ((rc = mdb_midl_need(&env->me_pghead, 2*count+1)) != 0)
+ return rc;
+ mop = env->me_pghead;
+ loose = mop + MDB_IDL_ALLOCLEN(mop) - count;
+ for (count = 0; mp; mp = NEXT_LOOSE_PAGE(mp))
+ loose[ ++count ] = mp->mp_pgno;
+ loose[0] = count;
+ mdb_midl_sort(loose);
+ mdb_midl_xmerge(mop, loose);
+ txn->mt_loose_pgs = NULL;
+ txn->mt_loose_count = 0;
+ mop_len = mop[0];
+ }
+
/* Fill in the reserved me_pghead records */
rc = MDB_SUCCESS;
if (mop_len) {
while (++i <= pagecount) {
dp = dl[i].mptr;
/* Don't flush this page yet */
- if (dp->mp_flags & P_KEEP) {
- dp->mp_flags ^= P_KEEP;
+ if (dp->mp_flags & (P_LOOSE|P_KEEP)) {
+ dp->mp_flags &= ~P_KEEP;
dl[++j] = dl[i];
continue;
}
if (++i <= pagecount) {
dp = dl[i].mptr;
/* Don't flush this page yet */
- if (dp->mp_flags & P_KEEP) {
- dp->mp_flags ^= P_KEEP;
+ if (dp->mp_flags & (P_LOOSE|P_KEEP)) {
+ dp->mp_flags &= ~P_KEEP;
dl[i].mid = 0;
continue;
}
#endif /* _WIN32 */
}
+ /* MIPS has cache coherency issues, this is a no-op everywhere else
+ * Note: for any size >= on-chip cache size, entire on-chip cache is
+ * flushed.
+ */
+ CACHEFLUSH(env->me_map, txn->mt_next_pgno * env->me_psize, DCACHE);
+
for (i = keep; ++i <= pagecount; ) {
dp = dl[i].mptr;
/* This is a page we skipped above */
if (txn->mt_parent) {
MDB_txn *parent = txn->mt_parent;
+ MDB_page **lp;
MDB_ID2L dst, src;
MDB_IDL pspill;
unsigned x, y, len, ps_len;
}
}
+ /* Append our loose page list to parent's */
+ for (lp = &parent->mt_loose_pgs; *lp; lp = &NEXT_LOOSE_PAGE(lp))
+ ;
+ *lp = txn->mt_loose_pgs;
+ parent->mt_loose_count += txn->mt_loose_count;
+
parent->mt_child = NULL;
mdb_midl_free(((MDB_ntxn *)txn)->mnt_pgstate.mf_pghead);
free(txn);
mdb_cursor_init(&mc, txn, MAIN_DBI, NULL);
for (i = 2; i < txn->mt_numdbs; i++) {
if (txn->mt_dbflags[i] & DB_DIRTY) {
+ if (TXN_DBI_CHANGED(txn, i)) {
+ rc = MDB_BAD_DBI;
+ goto fail;
+ }
data.mv_data = &txn->mt_dbs[i];
rc = mdb_cursor_put(&mc, &txn->mt_dbxs[i].md_name, &data, 0);
if (rc)
(rc = mdb_env_write_meta(txn)))
goto fail;
+ /* Free P_LOOSE pages left behind in dirty_list */
+ if (!(env->me_flags & MDB_WRITEMAP))
+ mdb_dlist_free(txn);
+
done:
env->me_pglast = 0;
env->me_txn = NULL;
if (env->me_txns)
UNLOCK_MUTEX_W(env);
- free(txn);
+ if (txn != env->me_txn0)
+ free(txn);
return MDB_SUCCESS;
* @param[out] meta address of where to store the meta information
* @return 0 on success, non-zero on failure.
*/
-static int
+static int ESECT
mdb_env_read_header(MDB_env *env, MDB_meta *meta)
{
MDB_metabuf pbuf;
return 0;
}
-static void
+static void ESECT
mdb_env_init_meta0(MDB_env *env, MDB_meta *meta)
{
meta->mm_magic = MDB_MAGIC;
* @param[out] meta address of where to store the meta information
* @return 0 on success, non-zero on failure.
*/
-static int
+static int ESECT
mdb_env_init_meta(MDB_env *env, MDB_meta *meta)
{
MDB_page *p, *q;
{
MDB_env *env;
MDB_meta meta, metab, *mp;
+ size_t mapsize;
off_t off;
int rc, len, toggle;
char *ptr;
env = txn->mt_env;
mp = env->me_metas[toggle];
+ mapsize = env->me_metas[toggle ^ 1]->mm_mapsize;
+ /* Persist any increases of mapsize config */
+ if (mapsize < env->me_mapsize)
+ mapsize = env->me_mapsize;
if (env->me_flags & MDB_WRITEMAP) {
- /* Persist any increases of mapsize config */
- if (env->me_mapsize > mp->mm_mapsize)
- mp->mm_mapsize = env->me_mapsize;
+ mp->mm_mapsize = mapsize;
mp->mm_dbs[0] = txn->mt_dbs[0];
mp->mm_dbs[1] = txn->mt_dbs[1];
mp->mm_last_pg = txn->mt_next_pgno - 1;
metab.mm_txnid = env->me_metas[toggle]->mm_txnid;
metab.mm_last_pg = env->me_metas[toggle]->mm_last_pg;
- ptr = (char *)&meta;
- if (env->me_mapsize > mp->mm_mapsize) {
- /* Persist any increases of mapsize config */
- meta.mm_mapsize = env->me_mapsize;
- off = offsetof(MDB_meta, mm_mapsize);
- } else {
- off = offsetof(MDB_meta, mm_dbs[0].md_depth);
- }
- len = sizeof(MDB_meta) - off;
-
- ptr += off;
+ meta.mm_mapsize = mapsize;
meta.mm_dbs[0] = txn->mt_dbs[0];
meta.mm_dbs[1] = txn->mt_dbs[1];
meta.mm_last_pg = txn->mt_next_pgno - 1;
meta.mm_txnid = txn->mt_txnid;
+ off = offsetof(MDB_meta, mm_mapsize);
+ ptr = (char *)&meta + off;
+ len = sizeof(MDB_meta) - off;
if (toggle)
off += env->me_psize;
off += PAGEHDRSZ;
env->me_flags |= MDB_FATAL_ERROR;
return rc;
}
+ /* MIPS has cache coherency issues, this is a no-op everywhere else */
+ CACHEFLUSH(env->me_map + off, len, DCACHE);
done:
/* Memory ordering issues are irrelevant; since the entire writer
* is wrapped by wmutex, all of these changes will become visible
return (env->me_metas[0]->mm_txnid < env->me_metas[1]->mm_txnid);
}
-int
+int ESECT
mdb_env_create(MDB_env **env)
{
MDB_env *e;
return MDB_SUCCESS;
}
-static int
-mdb_env_map(MDB_env *env, void *addr, int newsize)
+static int ESECT
+mdb_env_map(MDB_env *env, void *addr)
{
MDB_page *p;
unsigned int flags = env->me_flags;
size_t msize;
if (flags & MDB_RDONLY) {
+ /* Don't set explicit map size, use whatever exists */
msize = 0;
sizelo = 0;
sizehi = 0;
msize = env->me_mapsize;
sizelo = msize & 0xffffffff;
sizehi = msize >> 16 >> 16; /* only needed on Win64 */
- }
- /* Windows won't create mappings for zero length files.
- * Just allocate the maxsize right now.
- */
- if (newsize) {
+ /* Windows won't create mappings for zero length files.
+ * and won't map more than the file size.
+ * Just set the maxsize right now.
+ */
if (SetFilePointer(env->me_fd, sizelo, &sizehi, 0) != (DWORD)sizelo
|| !SetEndOfFile(env->me_fd)
|| SetFilePointer(env->me_fd, 0, NULL, 0) != 0)
return ErrCode();
}
+
mh = CreateFileMapping(env->me_fd, NULL, flags & MDB_WRITEMAP ?
PAGE_READWRITE : PAGE_READONLY,
sizehi, sizelo, NULL);
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_set_mapsize(MDB_env *env, size_t size)
{
/* If env is already open, caller is responsible for making
munmap(env->me_map, env->me_mapsize);
env->me_mapsize = size;
old = (env->me_flags & MDB_FIXEDMAP) ? env->me_map : NULL;
- rc = mdb_env_map(env, old, 1);
+ rc = mdb_env_map(env, old);
if (rc)
return rc;
}
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs)
{
if (env->me_map)
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_set_maxreaders(MDB_env *env, unsigned int readers)
{
if (env->me_map || readers < 1)
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_get_maxreaders(MDB_env *env, unsigned int *readers)
{
if (!env || !readers)
/** Further setup required for opening an LMDB environment
*/
-static int
+static int ESECT
mdb_env_open2(MDB_env *env)
{
unsigned int flags = env->me_flags;
env->me_mapsize = minsize;
}
- rc = mdb_env_map(env, meta.mm_address, newenv || env->me_mapsize != meta.mm_mapsize);
+ rc = mdb_env_map(env, (flags & MDB_FIXEDMAP) ? meta.mm_address : NULL);
if (rc)
return rc;
#endif
/** Downgrade the exclusive lock on the region back to shared */
-static int
+static int ESECT
mdb_env_share_locks(MDB_env *env, int *excl)
{
int rc = 0, toggle = mdb_env_pick_meta(env);
/** Try to get exlusive lock, otherwise shared.
* Maintain *excl = -1: no/unknown lock, 0: shared, 1: exclusive.
*/
-static int
+static int ESECT
mdb_env_excl_lock(MDB_env *env, int *excl)
{
int rc = 0;
* @param[in,out] excl In -1, out lock type: -1 none, 0 shared, 1 exclusive
* @return 0 on success, non-zero on failure.
*/
-static int
+static int ESECT
mdb_env_setup_locks(MDB_env *env, char *lpath, int mode, int *excl)
{
#ifdef _WIN32
# error "Persistent DB flags & env flags overlap, but both go in mm_flags"
#endif
-int
+int ESECT
mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode)
{
int oflags, rc, len, excl = -1;
env->me_path = strdup(path);
env->me_dbxs = calloc(env->me_maxdbs, sizeof(MDB_dbx));
env->me_dbflags = calloc(env->me_maxdbs, sizeof(uint16_t));
- if (!(env->me_dbxs && env->me_path && env->me_dbflags)) {
+ env->me_dbiseqs = calloc(env->me_maxdbs, sizeof(unsigned int));
+ if (!(env->me_dbxs && env->me_path && env->me_dbflags && env->me_dbiseqs)) {
rc = ENOMEM;
goto leave;
}
if (!((flags & MDB_RDONLY) ||
(env->me_pbuf = calloc(1, env->me_psize))))
rc = ENOMEM;
+ if (!(flags & MDB_RDONLY)) {
+ MDB_txn *txn;
+ int tsize = sizeof(MDB_txn), size = tsize + env->me_maxdbs *
+ (sizeof(MDB_db)+sizeof(MDB_cursor)+sizeof(unsigned int)+1);
+ txn = calloc(1, size);
+ if (txn) {
+ txn->mt_dbs = (MDB_db *)((char *)txn + tsize);
+ txn->mt_cursors = (MDB_cursor **)(txn->mt_dbs + env->me_maxdbs);
+ txn->mt_dbiseqs = (unsigned int *)(txn->mt_cursors + env->me_maxdbs);
+ txn->mt_dbflags = (unsigned char *)(txn->mt_dbiseqs + env->me_maxdbs);
+ txn->mt_env = env;
+ env->me_txn0 = txn;
+ } else {
+ rc = ENOMEM;
+ }
+ }
}
leave:
}
/** Destroy resources from mdb_env_open(), clear our readers & DBIs */
-static void
+static void ESECT
mdb_env_close0(MDB_env *env, int excl)
{
int i;
free(env->me_dbxs[i].md_name.mv_data);
free(env->me_pbuf);
+ free(env->me_dbiseqs);
free(env->me_dbflags);
free(env->me_dbxs);
free(env->me_path);
free(env->me_dirty_list);
+ free(env->me_txn0);
mdb_midl_free(env->me_free_pgs);
if (env->me_flags & MDB_ENV_TXKEY) {
}
-void
+void ESECT
mdb_env_close(MDB_env *env)
{
MDB_page *dp;
/* Make sure we're using an up-to-date root */
if (*mc->mc_dbflag & DB_STALE) {
MDB_cursor mc2;
+ if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi))
+ return MDB_BAD_DBI;
mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, NULL);
rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, 0);
if (rc)
if (op == MDB_PREV || op == MDB_PREV_DUP) {
rc = mdb_cursor_prev(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_PREV);
if (op != MDB_PREV || rc != MDB_NOTFOUND) {
- if (rc == MDB_SUCCESS)
+ if (rc == MDB_SUCCESS) {
MDB_GET_KEY(leaf, key);
+ mc->mc_flags &= ~C_EOF;
+ }
return rc;
}
} else {
rc = MDB_INCOMPATIBLE;
break;
}
+ {
+ MDB_node *leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
+ if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ MDB_GET_KEY(leaf, key);
+ rc = mdb_node_read(mc->mc_txn, leaf, data);
+ break;
+ }
+ }
if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) {
rc = EINVAL;
break;
if (mc->mc_dbi > MAIN_DBI && !(*mc->mc_dbflag & DB_DIRTY)) {
MDB_cursor mc2;
MDB_xcursor mcx;
+ if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi))
+ return MDB_BAD_DBI;
mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, &mcx);
rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, MDB_PS_MODIFY);
if (rc)
if ((mc->mc_db->md_flags & MDB_DUPSORT) &&
LEAFSIZE(key, data) > env->me_nodemax)
{
- /* Too big for a node, insert in sub-DB */
+ /* Too big for a node, insert in sub-DB. Set up an empty
+ * "old sub-page" for prep_subDB to expand to a full page.
+ */
fp_flags = P_LEAF|P_DIRTY;
fp = env->me_pbuf;
fp->mp_pad = data->mv_size; /* used if MDB_DUPFIXED */
- fp->mp_lower = fp->mp_upper = olddata.mv_size = PAGEHDRSZ;
+ fp->mp_lower = fp->mp_upper = (PAGEHDRSZ-PAGEBASE);
+ olddata.mv_size = PAGEHDRSZ;
goto prep_subDB;
}
} else {
return MDB_BAD_VALSIZE;
ptr = LEAF2KEY(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], ksize);
memcpy(ptr, key->mv_data, ksize);
+fix_parent:
+ /* if overwriting slot 0 of leaf, need to
+ * update branch key if there is a parent page
+ */
+ if (mc->mc_top && !mc->mc_ki[mc->mc_top]) {
+ unsigned short top = mc->mc_top;
+ mc->mc_top--;
+ /* slot 0 is always an empty key, find real slot */
+ while (mc->mc_top && !mc->mc_ki[mc->mc_top])
+ mc->mc_top--;
+ if (mc->mc_ki[mc->mc_top])
+ rc2 = mdb_update_key(mc, key);
+ else
+ rc2 = MDB_SUCCESS;
+ mc->mc_top = top;
+ if (rc2)
+ return rc2;
+ }
return MDB_SUCCESS;
}
/* Make sub-page header for the dup items, with dummy body */
fp->mp_flags = P_LEAF|P_DIRTY|P_SUBP;
- fp->mp_lower = PAGEHDRSZ;
+ fp->mp_lower = (PAGEHDRSZ-PAGEBASE);
xdata.mv_size = PAGEHDRSZ + dkey.mv_size + data->mv_size;
if (mc->mc_db->md_flags & MDB_DUPFIXED) {
fp->mp_flags |= P_LEAF2;
xdata.mv_size += 2 * (sizeof(indx_t) + NODESIZE) +
(dkey.mv_size & 1) + (data->mv_size & 1);
}
- fp->mp_upper = xdata.mv_size;
- olddata.mv_size = fp->mp_upper; /* pretend olddata is fp */
+ fp->mp_upper = xdata.mv_size - PAGEBASE;
+ olddata.mv_size = xdata.mv_size; /* pretend olddata is fp */
} else if (leaf->mn_flags & F_SUBDATA) {
/* Data is on sub-DB, just store it */
flags |= F_DUPDATA|F_SUBDATA;
if (fp_flags & P_LEAF2) {
memcpy(METADATA(mp), METADATA(fp), NUMKEYS(fp) * fp->mp_pad);
} else {
- memcpy((char *)mp + mp->mp_upper, (char *)fp + fp->mp_upper,
- olddata.mv_size - fp->mp_upper);
+ memcpy((char *)mp + mp->mp_upper + PAGEBASE, (char *)fp + fp->mp_upper + PAGEBASE,
+ olddata.mv_size - fp->mp_upper - PAGEBASE);
for (i=0; i<NUMKEYS(fp); i++)
mp->mp_ptrs[i] = fp->mp_ptrs[i] + offset;
}
data->mv_data = olddata.mv_data;
else if (!(mc->mc_flags & C_SUB))
memcpy(olddata.mv_data, data->mv_data, data->mv_size);
- else
+ else {
memcpy(NODEKEY(leaf), key->mv_data, key->mv_size);
+ goto fix_parent;
+ }
return MDB_SUCCESS;
}
mdb_node_del(mc, 0);
DPRINTF(("allocated new mpage %"Z"u, page size %u",
np->mp_pgno, mc->mc_txn->mt_env->me_psize));
np->mp_flags = flags | P_DIRTY;
- np->mp_lower = PAGEHDRSZ;
- np->mp_upper = mc->mc_txn->mt_env->me_psize;
+ np->mp_lower = (PAGEHDRSZ-PAGEBASE);
+ np->mp_upper = mc->mc_txn->mt_env->me_psize - PAGEBASE;
if (IS_BRANCH(np))
mc->mc_db->md_branch_pages++;
}
}
- base = (char *)mp + mp->mp_upper;
+ base = (char *)mp + mp->mp_upper + PAGEBASE;
memmove(base + sz, base, ptr - mp->mp_upper);
mp->mp_lower -= sizeof(indx_t);
mp->mp_ptrs[i] += delta;
}
- base = (char *)mp + mp->mp_upper;
+ base = (char *)mp + mp->mp_upper + PAGEBASE;
memmove(base + delta, base, ptr - mp->mp_upper + NODESIZE + NODEKSZ(node));
mp->mp_upper += delta;
}
if (mc->mc_txn->mt_flags & MDB_TXN_ERROR)
return MDB_BAD_TXN;
+ if (!(mc->mc_flags & C_INITIALIZED))
+ return EINVAL;
+
+ if (!mc->mc_snum || (mc->mc_flags & C_EOF))
+ return MDB_NOTFOUND;
+
leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
*countp = 1;
mp->mp_ptrs[i] -= delta;
}
- base = (char *)mp + mp->mp_upper;
+ base = (char *)mp + mp->mp_upper + PAGEBASE;
len = ptr - mp->mp_upper + NODESIZE;
memmove(base - delta, base, len);
mp->mp_upper -= delta;
/* if mc points past last node in page, find next sibling */
if (mc->mc_ki[mc->mc_top] >= nkeys) {
rc = mdb_cursor_sibling(mc, 1);
- if (rc == MDB_NOTFOUND)
+ if (rc == MDB_NOTFOUND) {
+ mc->mc_flags |= C_EOF;
rc = MDB_SUCCESS;
+ }
}
/* Adjust other cursors pointing to mp */
m3->mc_flags |= C_DEL;
if (m3->mc_ki[mc->mc_top] > ki)
m3->mc_ki[mc->mc_top]--;
+ else if (mc->mc_db->md_flags & MDB_DUPSORT)
+ m3->mc_xcursor->mx_cursor.mc_flags |= C_EOF;
}
if (m3->mc_ki[mc->mc_top] >= nkeys) {
rc = mdb_cursor_sibling(m3, 1);
- if (rc == MDB_NOTFOUND)
+ if (rc == MDB_NOTFOUND) {
+ m3->mc_flags |= C_EOF;
rc = MDB_SUCCESS;
+ }
}
}
}
}
copy->mp_pgno = mp->mp_pgno;
copy->mp_flags = mp->mp_flags;
- copy->mp_lower = PAGEHDRSZ;
- copy->mp_upper = env->me_psize;
+ copy->mp_lower = (PAGEHDRSZ-PAGEBASE);
+ copy->mp_upper = env->me_psize - PAGEBASE;
/* prepare to insert */
for (i=0, j=0; i<nkeys; i++) {
psize += nsize;
node = NULL;
} else {
- node = (MDB_node *)((char *)mp + copy->mp_ptrs[i]);
+ node = (MDB_node *)((char *)mp + copy->mp_ptrs[i] + PAGEBASE);
psize += NODESIZE + NODEKSZ(node) + sizeof(indx_t);
if (IS_LEAF(mp)) {
if (F_ISSET(node->mn_flags, F_BIGDATA))
sepkey.mv_size = newkey->mv_size;
sepkey.mv_data = newkey->mv_data;
} else {
- node = (MDB_node *)((char *)mp + copy->mp_ptrs[split_indx]);
+ node = (MDB_node *)((char *)mp + copy->mp_ptrs[split_indx] + PAGEBASE);
sepkey.mv_size = node->mn_ksize;
sepkey.mv_data = NODEKEY(node);
}
/* Update index for the new key. */
mc->mc_ki[mc->mc_top] = j;
} else {
- node = (MDB_node *)((char *)mp + copy->mp_ptrs[i]);
+ node = (MDB_node *)((char *)mp + copy->mp_ptrs[i] + PAGEBASE);
rkey.mv_data = NODEKEY(node);
rkey.mv_size = node->mn_ksize;
if (IS_LEAF(mp)) {
mp->mp_lower = copy->mp_lower;
mp->mp_upper = copy->mp_upper;
memcpy(NODEPTR(mp, nkeys-1), NODEPTR(copy, nkeys-1),
- env->me_psize - copy->mp_upper);
+ env->me_psize - copy->mp_upper - PAGEBASE);
/* reset back to original page */
if (newindx < split_indx) {
#define MDB_WBUF (1024*1024)
#endif
+ /** State needed for a compacting copy. */
typedef struct mdb_copy {
pthread_mutex_t mc_mutex;
pthread_cond_t mc_cond;
int mc_status;
volatile int mc_new;
int mc_toggle;
+
} mdb_copy;
-static THREAD_RET
+ /** Dedicated writer thread for compacting copy. */
+static THREAD_RET ESECT
mdb_env_copythr(void *arg)
{
mdb_copy *my = arg;
#undef DO_WRITE
}
-static int
+ /** Tell the writer thread there's a buffer ready to write */
+static int ESECT
mdb_env_cthr_toggle(mdb_copy *my, int st)
{
int toggle = my->mc_toggle ^ 1;
return 0;
}
-static int
+ /** Depth-first tree traversal for compacting copy. */
+static int ESECT
mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags)
{
MDB_cursor mc;
mc.mc_snum++;
mc.mc_ki[mc.mc_top] = 0;
if (IS_BRANCH(mp)) {
+ /* Whenever we advance to a sibling branch page,
+ * we must proceed all the way down to its first leaf.
+ */
mdb_page_copy(mc.mc_pg[mc.mc_top], mp, my->mc_env->me_psize);
goto again;
} else
return rc;
}
-int
-mdb_env_copyfd2(MDB_env *env, HANDLE fd)
+ /** Copy environment with compaction. */
+static int ESECT
+mdb_env_copyfd1(MDB_env *env, HANDLE fd)
{
MDB_meta *mm;
MDB_page *mp;
#ifdef _WIN32
my.mc_mutex = CreateMutex(NULL, FALSE, NULL);
my.mc_cond = CreateEvent(NULL, FALSE, FALSE, NULL);
- my.mc_wbuf[0] = _aligned_malloc(MDB_WBUF*2, env->me_psize);
+ my.mc_wbuf[0] = _aligned_malloc(MDB_WBUF*2, env->me_os_psize);
if (my.mc_wbuf[0] == NULL)
return errno;
#else
pthread_mutex_init(&my.mc_mutex, NULL);
pthread_cond_init(&my.mc_cond, NULL);
- rc = posix_memalign((void **)&my.mc_wbuf[0], env->me_psize, MDB_WBUF*2);
+#ifdef HAVE_MEMALIGN
+ my.mc_wbuf[0] = memalign(env->me_os_psize, MDB_WBUF*2);
+ if (my.mc_wbuf[0] == NULL)
+ return errno;
+#else
+ rc = posix_memalign((void **)&my.mc_wbuf[0], env->me_os_psize, MDB_WBUF*2);
if (rc)
return rc;
#endif
+#endif
+ memset(my.mc_wbuf[0], 0, MDB_WBUF*2);
my.mc_wbuf[1] = my.mc_wbuf[0] + MDB_WBUF;
my.mc_wlen[0] = 0;
my.mc_wlen[1] = 0;
my.mc_toggle = 0;
my.mc_env = env;
my.mc_fd = fd;
+ THREAD_CREATE(thr, mdb_env_copythr, &my);
- /* Do the lock/unlock of the reader mutex before starting the
- * write txn. Otherwise other read txns could block writers.
- */
rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
if (rc)
return rc;
- if (env->me_txns) {
- /* We must start the actual read txn after blocking writers */
- mdb_txn_reset0(txn, "reset-stage1");
-
- /* Temporarily block writers until we snapshot the meta pages */
- LOCK_MUTEX_W(env);
-
- rc = mdb_txn_renew0(txn);
- if (rc) {
- UNLOCK_MUTEX_W(env);
- goto leave;
- }
- }
-
- THREAD_CREATE(thr, mdb_env_copythr, &my);
mp = (MDB_page *)my.mc_wbuf[0];
memset(mp, 0, 2*env->me_psize);
mp->mp_pgno = 0;
pthread_cond_wait(&my.mc_cond, &my.mc_mutex);
pthread_mutex_unlock(&my.mc_mutex);
THREAD_FINISH(thr);
-leave:
+
mdb_txn_abort(txn);
#ifdef _WIN32
CloseHandle(my.mc_cond);
return rc;
}
-int
-mdb_env_copyfd(MDB_env *env, HANDLE fd)
+ /** Copy environment as-is. */
+static int ESECT
+mdb_env_copyfd0(MDB_env *env, HANDLE fd)
{
MDB_txn *txn = NULL;
int rc;
return rc;
}
-static int
-mdb_env_copy0(MDB_env *env, const char *path, int flag)
+int ESECT
+mdb_env_copyfd2(MDB_env *env, HANDLE fd, unsigned int flags)
+{
+ if (flags & MDB_CP_COMPACT)
+ return mdb_env_copyfd1(env, fd);
+ else
+ return mdb_env_copyfd0(env, fd);
+}
+
+int ESECT
+mdb_env_copyfd(MDB_env *env, HANDLE fd)
+{
+ return mdb_env_copyfd2(env, fd, 0);
+}
+
+int ESECT
+mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags)
{
int rc, len;
char *lpath;
goto leave;
}
+ if (env->me_psize >= env->me_os_psize) {
#ifdef O_DIRECT
/* Set O_DIRECT if the file system supports it */
if ((rc = fcntl(newfd, F_GETFL)) != -1)
goto leave;
}
#endif
+ }
- if (flag)
- rc = mdb_env_copyfd2(env, newfd);
- else
- rc = mdb_env_copyfd(env, newfd);
+ rc = mdb_env_copyfd2(env, newfd, flags);
leave:
if (!(env->me_flags & MDB_NOSUBDIR))
return rc;
}
-int
+int ESECT
mdb_env_copy(MDB_env *env, const char *path)
{
- return mdb_env_copy0(env, path, 0);
-}
-
-int
-mdb_env_copy2(MDB_env *env, const char *path)
-{
- return mdb_env_copy0(env, path, 1);
+ return mdb_env_copy2(env, path, 0);
}
-int
+int ESECT
mdb_env_set_flags(MDB_env *env, unsigned int flag, int onoff)
{
if ((flag & CHANGEABLE) != flag)
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_get_flags(MDB_env *env, unsigned int *arg)
{
if (!env || !arg)
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_set_userctx(MDB_env *env, void *ctx)
{
if (!env)
return MDB_SUCCESS;
}
-void *
+void * ESECT
mdb_env_get_userctx(MDB_env *env)
{
return env ? env->me_userctx : NULL;
}
-int
+int ESECT
mdb_env_set_assert(MDB_env *env, MDB_assert_func *func)
{
if (!env)
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_get_path(MDB_env *env, const char **arg)
{
if (!env || !arg)
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *arg)
{
if (!env || !arg)
* @param[out] arg the address of an #MDB_stat structure to receive the stats.
* @return 0, this function always succeeds.
*/
-static int
+static int ESECT
mdb_stat0(MDB_env *env, MDB_db *db, MDB_stat *arg)
{
arg->ms_psize = env->me_psize;
return MDB_SUCCESS;
}
-int
+
+int ESECT
mdb_env_stat(MDB_env *env, MDB_stat *arg)
{
int toggle;
return mdb_stat0(env, &env->me_metas[toggle]->mm_dbs[MAIN_DBI], arg);
}
-int
+int ESECT
mdb_env_info(MDB_env *env, MDB_envinfo *arg)
{
int toggle;
MDB_val key, data;
MDB_dbi i;
MDB_cursor mc;
+ MDB_db dummy;
int rc, dbflag, exact;
- unsigned int unused = 0;
+ unsigned int unused = 0, seq;
size_t len;
if (txn->mt_dbxs[FREE_DBI].md_cmp == NULL) {
return MDB_INCOMPATIBLE;
} else if (rc == MDB_NOTFOUND && (flags & MDB_CREATE)) {
/* Create if requested */
- MDB_db dummy;
data.mv_size = sizeof(MDB_db);
data.mv_data = &dummy;
memset(&dummy, 0, sizeof(dummy));
txn->mt_dbxs[slot].md_name.mv_size = len;
txn->mt_dbxs[slot].md_rel = NULL;
txn->mt_dbflags[slot] = dbflag;
+ /* txn-> and env-> are the same in read txns, use
+ * tmp variable to avoid undefined assignment
+ */
+ seq = ++txn->mt_env->me_dbiseqs[slot];
+ txn->mt_dbiseqs[slot] = seq;
+
memcpy(&txn->mt_dbs[slot], data.mv_data, sizeof(MDB_db));
*dbi = slot;
mdb_default_cmp(txn, slot);
if (dbi <= MAIN_DBI || dbi >= env->me_maxdbs)
return;
ptr = env->me_dbxs[dbi].md_name.mv_data;
- env->me_dbxs[dbi].md_name.mv_data = NULL;
- env->me_dbxs[dbi].md_name.mv_size = 0;
- env->me_dbflags[dbi] = 0;
- free(ptr);
+ /* If there was no name, this was already closed */
+ if (ptr) {
+ env->me_dbxs[dbi].md_name.mv_data = NULL;
+ env->me_dbxs[dbi].md_name.mv_size = 0;
+ env->me_dbflags[dbi] = 0;
+ env->me_dbiseqs[dbi]++;
+ free(ptr);
+ }
}
int mdb_dbi_flags(MDB_txn *txn, MDB_dbi dbi, unsigned int *flags)
if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
return EACCES;
+ if (dbi > MAIN_DBI && TXN_DBI_CHANGED(txn, dbi))
+ return MDB_BAD_DBI;
+
rc = mdb_cursor_open(txn, dbi, &mc);
if (rc)
return rc;
return MDB_SUCCESS;
}
-int mdb_env_get_maxkeysize(MDB_env *env)
+int ESECT
+mdb_env_get_maxkeysize(MDB_env *env)
{
return ENV_MAXKEY(env);
}
-int mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx)
+int ESECT
+mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx)
{
unsigned int i, rdrs;
MDB_reader *mr;
/** Insert pid into list if not already present.
* return -1 if already present.
*/
-static int mdb_pid_insert(MDB_PID_T *ids, MDB_PID_T pid)
+static int ESECT
+mdb_pid_insert(MDB_PID_T *ids, MDB_PID_T pid)
{
/* binary search of pid in list */
unsigned base = 0;
return 0;
}
-int mdb_reader_check(MDB_env *env, int *dead)
+int ESECT
+mdb_reader_check(MDB_env *env, int *dead)
{
unsigned int i, j, rdrs;
MDB_reader *mr;