#ifndef _WIN32
#include <pthread.h>
+#include <signal.h>
#ifdef MDB_USE_POSIX_SEM
# define MDB_USE_HASH 1
#include <semaphore.h>
#else
void *mm_address; /**< address for fixed mapping */
#endif
- pgno_t mm_mapsize; /**< size of mmap region */
+ mdb_size_t mm_mapsize; /**< size of mmap region */
MDB_db mm_dbs[CORE_DBS]; /**< first is free space, 2nd is main db */
/** The size of pages used in this DB */
#define mm_psize mm_dbs[FREE_DBI].md_pad
* @ingroup internal
* @{
*/
-#define DB_DIRTY 0x01 /**< DB was modified or is DUPSORT data */
+#define DB_DIRTY 0x01 /**< DB was written in this txn */
#define DB_STALE 0x02 /**< Named-DB record is older than txnID */
#define DB_NEW 0x04 /**< Named-DB handle opened in this txn */
#define DB_VALID 0x08 /**< DB handle is valid, see also #MDB_VALID */
#define DB_USRVALID 0x10 /**< As #DB_VALID, but not set for #FREE_DBI */
+#define DB_DUPDATA 0x20 /**< DB is #MDB_DUPSORT data */
/** @} */
/** In write txns, array of cursors for each DB */
MDB_cursor **mt_cursors;
unsigned i, j;
int rc = MDB_SUCCESS, level;
- /* Mark pages seen by cursors */
- if (mc->mc_flags & C_UNTRACK)
- mc = NULL; /* will find mc in mt_cursors */
- for (i = txn->mt_numdbs;; mc = txn->mt_cursors[--i]) {
- for (; mc; mc=mc->mc_next) {
- if (!(mc->mc_flags & C_INITIALIZED))
- continue;
+ /* Mark pages seen by cursors: First m0, then tracked cursors */
+ for (i = txn->mt_numdbs;; ) {
+ if (mc->mc_flags & C_INITIALIZED) {
for (m3 = mc;; m3 = &mx->mx_cursor) {
mp = NULL;
for (j=0; j<m3->mc_snum; j++) {
break;
}
}
- if (i == 0)
- break;
+ mc = mc->mc_next;
+ for (; !mc || mc == m0; mc = txn->mt_cursors[--i])
+ if (i == 0)
+ goto mark_done;
}
+mark_done:
if (all) {
/* Mark dirty root pages */
for (i=0; i<txn->mt_numdbs; i++) {
} else
mc->mc_ki[mc->mc_top]--;
- mc->mc_flags &= ~C_EOF;
-
DPRINTF(("==> cursor points to page %"Yu" with %u keys, key index %u",
mdb_dbg_pgno(mp), NUMKEYS(mp), mc->mc_ki[mc->mc_top]));
{
int rc = MDB_SUCCESS;
- if (mc->mc_dbi >= CORE_DBS && !(*mc->mc_dbflag & DB_DIRTY)) {
+ if (mc->mc_dbi >= CORE_DBS && !(*mc->mc_dbflag & (DB_DIRTY|DB_DUPDATA))) {
+ /* Touch DB record of named DB */
MDB_cursor mc2;
MDB_xcursor mcx;
if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi))
}
DPRINTF(("Sub-db -%u root page %"Yu, mx->mx_cursor.mc_dbi,
mx->mx_db.md_root));
- mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DIRTY; /* DB_DIRTY guides mdb_cursor_touch */
+ mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DUPDATA;
if (NEED_CMP_CLONG(mx->mx_dbx.md_cmp, mx->mx_db.md_pad))
mx->mx_dbx.md_cmp = mdb_cmp_clong;
}
mx->mx_cursor.mc_top = 0;
mx->mx_cursor.mc_flags |= C_INITIALIZED;
mx->mx_cursor.mc_ki[0] = 0;
- mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DIRTY; /* DB_DIRTY guides mdb_cursor_touch */
+ mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DUPDATA;
#if UINT_MAX < MDB_SIZE_MAX /* matches mdb_xcursor_init1:NEED_CMP_CLONG() */
mx->mx_dbx.md_cmp = src_mx->mx_dbx.md_cmp;
#endif
MDB_CURSOR_UNREF(mc, 0);
}
if (mc && !mc->mc_backup) {
- /* remove from txn, if tracked */
+ /* Remove from txn, if tracked.
+ * A read-only txn (!C_UNTRACK) may have been freed already,
+ * so do not peek inside it. Only write txns track cursors.
+ */
if ((mc->mc_flags & C_UNTRACK) && mc->mc_txn->mt_cursors) {
MDB_cursor **prev = &mc->mc_txn->mt_cursors[mc->mc_dbi];
while (*prev && *prev != mc) prev = &(*prev)->mc_next;
* run out of space, triggering a split. We need this
* cursor to be consistent until the end of the rebalance.
*/
- mc.mc_flags |= C_UNTRACK;
mc.mc_next = txn->mt_cursors[dbi];
txn->mt_cursors[dbi] = &mc;
rc = mdb_cursor_del(&mc, flags);
} else {
/* find right page's left sibling */
mc->mc_ki[ptop] = mn.mc_ki[ptop];
- mdb_cursor_sibling(mc, 0);
+ rc = mdb_cursor_sibling(mc, 0);
}
}
} else {
mn.mc_top++;
}
if (rc != MDB_SUCCESS) {
+ if (rc == MDB_NOTFOUND) /* improper mdb_cursor_sibling() result */
+ rc = MDB_PROBLEM;
goto done;
}
if (nflags & MDB_APPEND) {
HANDLE mc_fd;
int mc_toggle; /**< Buffer number in provider */
int mc_new; /**< (0-2 buffers to write) | (#MDB_EOF at end) */
- volatile int mc_error; /**< Error code, never cleared if set */
+ /** Error code. Never cleared if set. Both threads can set nonzero
+ * to fail the copy. Not mutex-protected, LMDB expects atomic int.
+ */
+ volatile int mc_error;
} mdb_copy;
/** Dedicated writer thread for compacting copy. */
#define DO_WRITE(rc, fd, ptr, w2, len) rc = WriteFile(fd, ptr, w2, &len, NULL)
#else
int len;
+ sigset_t set;
#define DO_WRITE(rc, fd, ptr, w2, len) len = write(fd, ptr, w2); rc = (len >= 0)
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGPIPE);
#endif
pthread_mutex_lock(&my->mc_mutex);
+#ifndef _WIN32
+ my->mc_error = pthread_sigmask(SIG_BLOCK, &set, NULL);
+#endif
for(;;) {
while (!my->mc_new)
pthread_cond_wait(&my->mc_cond, &my->mc_mutex);
DO_WRITE(rc, my->mc_fd, ptr, wsize, len);
if (!rc) {
rc = ErrCode();
+#ifndef _WIN32
+ if (rc == EPIPE) {
+ int tmp;
+ sigwait(&set, &tmp);
+ }
+#endif
break;
} else if (len > 0) {
rc = MDB_SUCCESS;
return my->mc_error;
}
- /** Depth-first tree traversal for compacting copy. */
+ /** Depth-first tree traversal for compacting copy.
+ * @param[in] my control structure.
+ * @param[in,out] pg database root.
+ * @param[in] flags includes #F_DUPDATA if it is a sorted-duplicate sub-DB.
+ */
static int ESECT
mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags)
{
memset(&dummy, 0, sizeof(dummy));
dummy.md_root = P_INVALID;
dummy.md_flags = flags & PERSISTENT_FLAGS;
- rc = mdb_cursor_put(&mc, &key, &data, F_SUBDATA);
+ WITH_CURSOR_TRACKING(mc,
+ rc = mdb_cursor_put(&mc, &key, &data, F_SUBDATA));
dbflag |= DB_DIRTY;
}
return env->me_txns ? mdb_reader_check0(env, 0, dead) : MDB_SUCCESS;
}
-/** As #mdb_reader_check(). rlocked = <caller locked the reader mutex>. */
+/** As #mdb_reader_check(). \b rlocked is set if caller locked #me_rmutex. */
static int ESECT
mdb_reader_check0(MDB_env *env, int rlocked, int *dead)
{