]> git.sur5r.net Git - openldap/blobdiff - libraries/liblmdb/mdb.c
ITS#8117 fix INTEGERDUP compare
[openldap] / libraries / liblmdb / mdb.c
index 5f287f4b182d0bacac0ab3d26668f21621fd2b9a..acb0e449f5dd56cba824dfe6b068ab5bb59fd101 100644 (file)
@@ -5,7 +5,7 @@
  *     BerkeleyDB API, but much simplified.
  */
 /*
- * Copyright 2011-2014 Howard Chu, Symas Corp.
+ * Copyright 2011-2015 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -588,11 +588,11 @@ typedef struct MDB_rxbody {
         *      started from so we can avoid overwriting any data used in that
         *      particular version.
         */
-       txnid_t         mrb_txnid;
+       volatile txnid_t                mrb_txnid;
        /** The process ID of the process owning this reader txn. */
-       MDB_PID_T       mrb_pid;
+       volatile MDB_PID_T      mrb_pid;
        /** The thread ID of the thread owning this txn. */
-       MDB_THR_T       mrb_tid;
+       volatile MDB_THR_T      mrb_tid;
 } MDB_rxbody;
 
        /** The actual reader record, with cacheline padding. */
@@ -640,12 +640,12 @@ typedef struct MDB_txbody {
                 *      This is recorded here only for convenience; the value can always
                 *      be determined by reading the main database meta pages.
                 */
-       txnid_t         mtb_txnid;
+       volatile txnid_t                mtb_txnid;
                /** The number of slots that have been used in the reader table.
                 *      This always records the maximum count, it is not decremented
                 *      when readers release their slots.
                 */
-       unsigned        mtb_numreaders;
+       volatile unsigned       mtb_numreaders;
 } MDB_txbody;
 
        /** The actual reader table definition. */
@@ -916,7 +916,7 @@ typedef struct MDB_meta {
        /** Any persistent environment flags. @ref mdb_env */
 #define        mm_flags        mm_dbs[0].md_flags
        pgno_t          mm_last_pg;                     /**< last used page in file */
-       txnid_t         mm_txnid;                       /**< txnid that committed this page */
+       volatile txnid_t        mm_txnid;       /**< txnid that committed this page */
 } MDB_meta;
 
        /** Buffer for a stack-allocated meta page.
@@ -2491,6 +2491,7 @@ mdb_txn_renew0(MDB_txn *txn)
        int rc, new_notls = 0;
 
        if (txn->mt_flags & MDB_TXN_RDONLY) {
+               txn->mt_flags = MDB_TXN_RDONLY;
                /* Setup db info */
                txn->mt_numdbs = env->me_numdbs;
                txn->mt_dbxs = env->me_dbxs;    /* mostly static anyway */
@@ -2539,7 +2540,10 @@ mdb_txn_renew0(MDB_txn *txn)
                                        return rc;
                                }
                        }
-                       txn->mt_txnid = r->mr_txnid = ti->mti_txnid;
+                       do /* LY: Retry on a race, ITS#7970. */
+                               r->mr_txnid = ti->mti_txnid;
+                       while(r->mr_txnid != ti->mti_txnid);
+                       txn->mt_txnid = r->mr_txnid;
                        txn->mt_u.reader = r;
                        meta = env->me_metas[txn->mt_txnid & 1];
                }
@@ -3129,6 +3133,7 @@ mdb_page_flush(MDB_txn *txn, int keep)
                /* Write up to MDB_COMMIT_PAGES dirty pages at a time. */
                if (pos!=next_pos || n==MDB_COMMIT_PAGES || wsize+size>MAX_WRITE) {
                        if (n) {
+retry_write:
                                /* Write previous page(s) */
 #ifdef MDB_USE_PWRITEV
                                wres = pwritev(env->me_fd, iov, n, wpos);
@@ -3136,8 +3141,11 @@ mdb_page_flush(MDB_txn *txn, int keep)
                                if (n == 1) {
                                        wres = pwrite(env->me_fd, iov[0].iov_base, wsize, wpos);
                                } else {
+retry_seek:
                                        if (lseek(env->me_fd, wpos, SEEK_SET) == -1) {
                                                rc = ErrCode();
+                                               if (rc == EINTR)
+                                                       goto retry_seek;
                                                DPRINTF(("lseek: %s", strerror(rc)));
                                                return rc;
                                        }
@@ -3147,6 +3155,8 @@ mdb_page_flush(MDB_txn *txn, int keep)
                                if (wres != wsize) {
                                        if (wres < 0) {
                                                rc = ErrCode();
+                                               if (rc == EINTR)
+                                                       goto retry_write;
                                                DPRINTF(("Write error: %s", strerror(rc)));
                                        } else {
                                                rc = EIO; /* TODO: Use which error code? */
@@ -3516,7 +3526,8 @@ mdb_env_init_meta(MDB_env *env, MDB_meta *meta)
        int len;
 #define DO_PWRITE(rc, fd, ptr, size, len, pos) do { \
        len = pwrite(fd, ptr, size, pos);       \
-       rc = (len >= 0); } while(0)
+       if (len == -1 && ErrCode() == EINTR) continue; \
+       rc = (len >= 0); break; } while(1)
 #endif
 
        DPUTS("writing new meta page");
@@ -3621,6 +3632,7 @@ mdb_env_write_meta(MDB_txn *txn)
        /* Write to the SYNC fd */
        mfd = env->me_flags & (MDB_NOSYNC|MDB_NOMETASYNC) ?
                env->me_fd : env->me_mfd;
+retry_write:
 #ifdef _WIN32
        {
                memset(&ov, 0, sizeof(ov));
@@ -3633,6 +3645,8 @@ mdb_env_write_meta(MDB_txn *txn)
 #endif
        if (rc != len) {
                rc = rc < 0 ? ErrCode() : EIO;
+               if (rc == EINTR)
+                       goto retry_write;
                DPUTS("write failed, disk error?");
                /* On a failure, the pagecache still contains the new data.
                 * Write some old data back, to prevent it from being used.
@@ -4647,13 +4661,15 @@ mdb_env_close0(MDB_env *env, int excl)
                return;
 
        /* Doing this here since me_dbxs may not exist during mdb_env_close */
-       for (i = env->me_maxdbs; --i > MAIN_DBI; )
-               free(env->me_dbxs[i].md_name.mv_data);
+       if (env->me_dbxs) {
+               for (i = env->me_maxdbs; --i > MAIN_DBI; )
+                       free(env->me_dbxs[i].md_name.mv_data);
+               free(env->me_dbxs);
+       }
 
        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);
@@ -7060,6 +7076,12 @@ mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node)
                                mx->mx_db.md_flags |= MDB_INTEGERKEY;
                }
        }
+#if UINT_MAX < SIZE_MAX
+       if (mc->mc_dbx->md_dcmp == mdb_cmp_int && mx->mx_db.md_pad == sizeof(size_t)) {
+               mc->mc_dbx->md_dcmp = mdb_cmp_clong;
+               mx->mx_dbx.md_cmp = mdb_cmp_clong;
+       }
+#endif
        DPRINTF(("Sub-db -%u root page %"Z"u", mx->mx_cursor.mc_dbi,
                mx->mx_db.md_root));
        mx->mx_dbflag = DB_VALID|DB_DIRTY; /* DB_DIRTY guides mdb_cursor_touch */
@@ -7083,6 +7105,7 @@ mdb_cursor_init(MDB_cursor *mc, MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx)
        mc->mc_snum = 0;
        mc->mc_top = 0;
        mc->mc_pg[0] = 0;
+       mc->mc_ki[0] = 0;
        mc->mc_flags = 0;
        if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) {
                mdb_tassert(txn, mx != NULL);
@@ -7456,7 +7479,7 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst)
                        cdst->mc_ki[cdst->mc_top] = 0;
                        rc = mdb_update_key(cdst, &nullkey);
                        cdst->mc_ki[cdst->mc_top] = ix;
-                       mdb_cassert(csrc, rc == MDB_SUCCESS);
+                       mdb_cassert(cdst, rc == MDB_SUCCESS);
                }
        }
 
@@ -7712,12 +7735,12 @@ mdb_rebalance(MDB_cursor *mc)
                                                m3 = m2;
                                        if (m3 == mc || m3->mc_snum < mc->mc_snum) continue;
                                        if (m3->mc_pg[0] == mp) {
-                                               m3->mc_snum--;
-                                               m3->mc_top--;
                                                for (i=0; i<m3->mc_snum; i++) {
                                                        m3->mc_pg[i] = m3->mc_pg[i+1];
                                                        m3->mc_ki[i] = m3->mc_ki[i+1];
                                                }
+                                               m3->mc_snum--;
+                                               m3->mc_top--;
                                        }
                                }
                        }
@@ -7785,9 +7808,23 @@ mdb_rebalance(MDB_cursor *mc)
                if (mc->mc_ki[ptop] == 0) {
                        rc = mdb_page_merge(&mn, mc);
                } else {
+                       MDB_cursor dummy;
                        oldki += NUMKEYS(mn.mc_pg[mn.mc_top]);
                        mn.mc_ki[mn.mc_top] += mc->mc_ki[mn.mc_top] + 1;
+                       /* We want mdb_rebalance to find mn when doing fixups */
+                       if (mc->mc_flags & C_SUB) {
+                               dummy.mc_next = mc->mc_txn->mt_cursors[mc->mc_dbi];
+                               mc->mc_txn->mt_cursors[mc->mc_dbi] = &dummy;
+                               dummy.mc_xcursor = (MDB_xcursor *)&mn;
+                       } else {
+                               mn.mc_next = mc->mc_txn->mt_cursors[mc->mc_dbi];
+                               mc->mc_txn->mt_cursors[mc->mc_dbi] = &mn;
+                       }
                        rc = mdb_page_merge(mc, &mn);
+                       if (mc->mc_flags & C_SUB)
+                               mc->mc_txn->mt_cursors[mc->mc_dbi] = dummy.mc_next;
+                       else
+                               mc->mc_txn->mt_cursors[mc->mc_dbi] = mn.mc_next;
                        mdb_cursor_copy(&mn, mc);
                }
                mc->mc_flags &= ~C_EOF;
@@ -7814,6 +7851,13 @@ mdb_cursor_del0(MDB_cursor *mc)
                MDB_cursor *m2, *m3;
                MDB_dbi dbi = mc->mc_dbi;
 
+               /* DB is totally empty now, just bail out.
+                * Other cursors adjustments were already done
+                * by mdb_rebalance and aren't needed here.
+                */
+               if (!mc->mc_snum)
+                       return rc;
+
                mp = mc->mc_pg[mc->mc_top];
                nkeys = NUMKEYS(mp);