]> git.sur5r.net Git - openldap/commitdiff
Add MDB_NOTLS envflag.
authorHoward Chu <hyc@symas.com>
Thu, 18 Apr 2013 02:17:03 +0000 (04:17 +0200)
committerHallvard Furuseth <hallvard@openldap.org>
Thu, 18 Apr 2013 02:17:03 +0000 (04:17 +0200)
libraries/liblmdb/lmdb.h
libraries/liblmdb/mdb.c

index b2525edfeba44a24c302bc19b51e9e045a2a1949..4906472f24d7a120636c1b07650e366eb8a8ce55 100644 (file)
@@ -66,6 +66,7 @@
  *
  *     - A thread can only use one transaction at a time, plus any child
  *       transactions.  Each transaction belongs to one thread.  See below.
+ *       The #MDB_NOTLS flag changes this for read-only transactions.
  *
  *     - Use an MDB_env* in the process which opened it, without fork()ing.
  *
@@ -249,6 +250,8 @@ typedef void (MDB_rel_func)(MDB_val *item, void *oldptr, void *newptr, void *rel
 #define MDB_WRITEMAP           0x80000
        /** use asynchronous msync when MDB_WRITEMAP is used */
 #define MDB_MAPASYNC           0x100000
+       /** tie reader locktable slots to #MDB_txn objects instead of to threads */
+#define MDB_NOTLS              0x200000
 /** @} */
 
 /**    @defgroup       mdb_dbi_open    Database Flags
@@ -392,8 +395,8 @@ typedef struct MDB_envinfo {
        size_t  me_mapsize;                             /**< Size of the data memory map */
        size_t  me_last_pgno;                   /**< ID of the last used page */
        size_t  me_last_txnid;                  /**< ID of the last committed transaction */
-       unsigned int me_maxreaders;             /**< maximum number of threads for the environment */
-       unsigned int me_numreaders;             /**< maximum number of threads used in the environment */
+       unsigned int me_maxreaders;             /**< max reader slots in the environment */
+       unsigned int me_numreaders;             /**< max reader slots used in the environment */
 } MDB_envinfo;
 
        /** @brief Return the mdb library version information.
@@ -492,6 +495,15 @@ int  mdb_env_create(MDB_env **env);
         *              database or lose the last transactions. Calling #mdb_env_sync()
         *              ensures on-disk database integrity until next commit.
         *              This flag may be changed at any time using #mdb_env_set_flags().
+        *      <li>#MDB_NOTLS
+        *              Don't use Thread-Local Storage. Tie reader locktable slots to
+        *              #MDB_txn objects instead of to threads. I.e. #mdb_txn_reset() keeps
+        *              the slot reseved for the #MDB_txn object. A thread may use parallel
+        *              read-only transactions. A read-only transaction may span threads if
+        *              the user synchronizes its use. Applications that multiplex many
+        *              user threads over individual OS threads need this option. Such an
+        *              application must also serialize the write transactions in an OS
+        *              thread, since MDB's write locking is unaware of the user threads.
         * </ul>
         * @param[in] mode The UNIX permissions to set on created files. This parameter
         * is ignored on Windows.
@@ -626,13 +638,17 @@ int  mdb_env_get_path(MDB_env *env, const char **path);
         */
 int  mdb_env_set_mapsize(MDB_env *env, size_t size);
 
-       /** @brief Set the maximum number of threads for the environment.
+       /** @brief Set the maximum number of threads/reader slots for the environment.
         *
         * This defines the number of slots in the lock table that is used to track readers in the
         * the environment. The default is 126.
+        * Starting a read-only transaction normally ties a lock table slot to the
+        * current thread until the environment closes or the thread exits. If
+        * MDB_NOTLS is in use, #mdb_txn_begin() instead ties the slot to the
+        * MDB_txn object until it or the #MDB_env object is destroyed.
         * This function may only be called after #mdb_env_create() and before #mdb_env_open().
         * @param[in] env An environment handle returned by #mdb_env_create()
-        * @param[in] readers The maximum number of threads
+        * @param[in] readers The maximum number of reader lock table slots
         * @return A non-zero error value on failure and 0 on success. Some possible
         * errors are:
         * <ul>
@@ -641,7 +657,7 @@ int  mdb_env_set_mapsize(MDB_env *env, size_t size);
         */
 int  mdb_env_set_maxreaders(MDB_env *env, unsigned int readers);
 
-       /** @brief Get the maximum number of threads for the environment.
+       /** @brief Get the maximum number of threads/reader slots for the environment.
         *
         * @param[in] env An environment handle returned by #mdb_env_create()
         * @param[out] readers Address of an integer to store the number of readers
@@ -672,8 +688,9 @@ int  mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs);
        /** @brief Create a transaction for use with the environment.
         *
         * The transaction handle may be discarded using #mdb_txn_abort() or #mdb_txn_commit().
-        * @note Transactions may not span threads; a transaction must only be used by a
-        * single thread. Also, a thread may only have a single transaction.
+        * @note A transaction and its cursors must only be used by a single
+        * thread, and a thread may only have a single transaction at a time.
+        * If #MDB_NOTLS is in use, this does not apply to read-only transactions.
         * @note Cursors may not span transactions; each cursor must be opened and closed
         * within a single transaction.
         * @param[in] env An environment handle returned by #mdb_env_create()
@@ -730,11 +747,13 @@ void mdb_txn_abort(MDB_txn *txn);
 
        /** @brief Reset a read-only transaction.
         *
-        * This releases the current reader lock but doesn't free the
-        * transaction handle, allowing it to be used again later by #mdb_txn_renew().
-        * It otherwise has the same effect as #mdb_txn_abort() but saves some memory
-        * allocation/deallocation overhead if a thread is going to start a new
-        * read-only transaction again soon.
+        * Abort the transaction like #mdb_txn_abort(), but keep the transaction
+        * handle. #mdb_txn_renew() may reuse the handle. This saves allocation
+        * overhead if the process will start a new read-only transaction soon,
+        * and also locking overhead if #MDB_NOTLS is in use. The reader table
+        * lock is released, but the table slot stays tied to its thread or
+        * #MDB_txn. Use mdb_txn_abort() to discard a reset handle, and to free
+        * its lock table slot if MDB_NOTLS is in use.
         * All cursors opened within the transaction must be closed before the transaction
         * is reset.
         * Reader locks generally don't interfere with writers, but they keep old
@@ -1045,8 +1064,8 @@ int  mdb_del(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data);
 
        /** @brief Create a cursor handle.
         *
-        * Cursors are associated with a specific transaction and database and
-        * may not span threads.
+        * A cursor is associated with a specific transaction and database.
+        * It must be closed before its transaction ends.
         * @param[in] txn A transaction handle returned by #mdb_txn_begin()
         * @param[in] dbi A database handle returned by #mdb_dbi_open()
         * @param[out] cursor Address where the new #MDB_cursor handle will be stored
@@ -1067,8 +1086,9 @@ void mdb_cursor_close(MDB_cursor *cursor);
 
        /** @brief Renew a cursor handle.
         *
-        * Cursors are associated with a specific transaction and database and
-        * may not span threads. Cursors that are only used in read-only
+        * A cursor is associated with a specific transaction and database.
+        * It must be closed before its transaction ends.
+        * Cursors that are only used in read-only
         * transactions may be re-used, to avoid unnecessary malloc/free overhead.
         * The cursor may be associated with a new read-only transaction, and
         * referencing the same database handle as it was created with.
index 471dcd09a43317e511e17c1d4c5509fd3b02d80c..71575d604bf9d08239b2e1b9af18bc3fd57fc8c2 100644 (file)
@@ -406,6 +406,8 @@ typedef uint16_t     indx_t;
  *     slot's address is saved in thread-specific data so that subsequent read
  *     transactions started by the same thread need no further locking to proceed.
  *
+ *     If #MDB_NOTLS is set, the slot address is not saved in thread-specific data.
+ *
  *     No reader table is used if the database is on a read-only filesystem.
  *
  *     Since the database uses multi-version concurrency control, readers don't
@@ -1792,7 +1794,8 @@ mdb_txn_renew0(MDB_txn *txn)
                        txn->mt_txnid = env->me_metas[i]->mm_txnid;
                        txn->mt_u.reader = NULL;
                } else {
-                       MDB_reader *r = pthread_getspecific(env->me_txkey);
+                       MDB_reader *r = (env->me_flags & MDB_NOTLS) ? txn->mt_u.reader :
+                               pthread_getspecific(env->me_txkey);
                        if (r) {
                                if (r->mr_pid != env->me_pid || r->mr_txnid != (txnid_t)-1)
                                        return MDB_BAD_RSLOT;
@@ -1816,7 +1819,8 @@ mdb_txn_renew0(MDB_txn *txn)
                                env->me_numreaders = env->me_txns->mti_numreaders;
                                UNLOCK_MUTEX_R(env);
                                r = &env->me_txns->mti_readers[i];
-                               if ((rc = pthread_setspecific(env->me_txkey, r)) != 0) {
+                               if (!(env->me_flags & MDB_NOTLS) &&
+                                       (rc = pthread_setspecific(env->me_txkey, r)) != 0) {
                                        env->me_txns->mti_readers[i].mr_pid = 0;
                                        return rc;
                                }
@@ -2006,7 +2010,8 @@ mdb_txn_reset0(MDB_txn *txn)
        if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) {
                if (txn->mt_u.reader) {
                        txn->mt_u.reader->mr_txnid = (txnid_t)-1;
-                       txn->mt_u.reader = NULL; /* do not touch mr_txnid again */
+                       if (!(env->me_flags & MDB_NOTLS))
+                               txn->mt_u.reader = NULL; /* txn does not own reader */
                }
                txn->mt_numdbs = 0;     /* mark txn as reset, do not close DBs again */
        } else {
@@ -2091,6 +2096,10 @@ mdb_txn_abort(MDB_txn *txn)
                mdb_txn_abort(txn->mt_child);
 
        mdb_txn_reset0(txn);
+       /* Free reader slot tied to this txn (if MDB_NOTLS && writable FS) */
+       if ((txn->mt_flags & MDB_TXN_RDONLY) && txn->mt_u.reader)
+               txn->mt_u.reader->mr_pid = 0;
+
        free(txn);
 }
 
@@ -3240,7 +3249,7 @@ mdb_env_setup_locks(MDB_env *env, char *lpath, int mode, int *excl)
                        fcntl(env->me_lfd, F_SETFD, fdflags);
 #endif
 
-       {
+       if (!(env->me_flags & MDB_NOTLS)) {
                rc = pthread_key_create(&env->me_txkey, mdb_env_reader_dest);
                if (rc)
                        goto fail;
@@ -3418,7 +3427,7 @@ fail:
         *      environment and re-opening it with the new flags.
         */
 #define        CHANGEABLE      (MDB_NOSYNC|MDB_NOMETASYNC|MDB_MAPASYNC)
-#define        CHANGELESS      (MDB_FIXEDMAP|MDB_NOSUBDIR|MDB_RDONLY|MDB_WRITEMAP)
+#define        CHANGELESS      (MDB_FIXEDMAP|MDB_NOSUBDIR|MDB_RDONLY|MDB_WRITEMAP|MDB_NOTLS)
 
 int
 mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode)