Add the ability to not block when opening a write transaction

This commit is contained in:
James Rouzier 2017-10-25 19:41:28 -04:00
parent 4f1c846437
commit 7c466e53f0
6 changed files with 46 additions and 6 deletions

14
mdbx.h
View File

@ -327,6 +327,10 @@ typedef int(MDBX_cmp_func)(const MDBX_val *a, const MDBX_val *b);
/* Store multiple data items in one call. Only for MDBX_DUPFIXED. */ /* Store multiple data items in one call. Only for MDBX_DUPFIXED. */
#define MDBX_MULTIPLE 0x80000u #define MDBX_MULTIPLE 0x80000u
/* Transaction Flags*/
/* Do not block when starting a write transaction */
#define MDBX_TRYTXN 0x10000000u
/* Copy Flags */ /* Copy Flags */
/* Compacting copy: Omit free space from copy, and renumber all /* Compacting copy: Omit free space from copy, and renumber all
* pages sequentially. */ * pages sequentially. */
@ -420,8 +424,10 @@ typedef enum MDBX_cursor_op {
#define MDBX_BAD_DBI (-30780) #define MDBX_BAD_DBI (-30780)
/* Unexpected problem - txn should abort */ /* Unexpected problem - txn should abort */
#define MDBX_PROBLEM (-30779) #define MDBX_PROBLEM (-30779)
/* Unexpected problem - txn should abort */
#define MDBX_BUSY (-30778)
/* The last defined error code */ /* The last defined error code */
#define MDBX_LAST_ERRCODE MDBX_PROBLEM #define MDBX_LAST_ERRCODE MDBX_BUSY
/* The mdbx_put() or mdbx_replace() was called for key, /* The mdbx_put() or mdbx_replace() was called for key,
that has more that one associated value. */ that has more that one associated value. */
@ -953,6 +959,9 @@ LIBMDBX_API int mdbx_env_set_assert(MDBX_env *env, MDBX_assert_func *func);
* - MDBX_RDONLY * - MDBX_RDONLY
* This transaction will not perform any write operations. * This transaction will not perform any write operations.
* *
* - MDBX_TRYTXN
* Do not block when starting a write transaction
*
* [out] txn Address where the new MDBX_txn handle will be stored * [out] txn Address where the new MDBX_txn handle will be stored
* *
* Returns A non-zero error value on failure and 0 on success, some * Returns A non-zero error value on failure and 0 on success, some
@ -964,7 +973,8 @@ LIBMDBX_API int mdbx_env_set_assert(MDBX_env *env, MDBX_assert_func *func);
* as well. See mdbx_env_set_mapsize(). * as well. See mdbx_env_set_mapsize().
* - MDBX_READERS_FULL - a read-only transaction was requested and the reader * - MDBX_READERS_FULL - a read-only transaction was requested and the reader
* lock table is full. See mdbx_env_set_maxreaders(). * lock table is full. See mdbx_env_set_maxreaders().
* - MDBX_ENOMEM - out of memory. */ * - MDBX_ENOMEM - out of memory.
* - MDBX_BUSY - a write transaction is already started. */
LIBMDBX_API int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags, LIBMDBX_API int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags,
MDBX_txn **txn); MDBX_txn **txn);

View File

@ -553,7 +553,8 @@ struct MDBX_txn {
/* Transaction Flags */ /* Transaction Flags */
/* mdbx_txn_begin() flags */ /* mdbx_txn_begin() flags */
#define MDBX_TXN_BEGIN_FLAGS (MDBX_NOMETASYNC | MDBX_NOSYNC | MDBX_RDONLY) #define MDBX_TXN_BEGIN_FLAGS_PERSISTENT (MDBX_NOMETASYNC | MDBX_NOSYNC | MDBX_RDONLY)
#define MDBX_TXN_BEGIN_FLAGS (MDBX_TXN_BEGIN_FLAGS_PERSISTENT | MDBX_TRYTXN)
#define MDBX_TXN_NOMETASYNC \ #define MDBX_TXN_NOMETASYNC \
MDBX_NOMETASYNC /* don't sync meta for this txn on commit */ MDBX_NOMETASYNC /* don't sync meta for this txn on commit */
#define MDBX_TXN_NOSYNC MDBX_NOSYNC /* don't sync this txn on commit */ #define MDBX_TXN_NOSYNC MDBX_NOSYNC /* don't sync this txn on commit */

View File

@ -184,6 +184,13 @@ static int mdbx_robust_lock(MDBX_env *env, pthread_mutex_t *mutex) {
return rc; return rc;
} }
static int mdbx_robust_trylock(MDBX_env *env, pthread_mutex_t *mutex) {
int rc = pthread_mutex_trylock(mutex);
if (unlikely(rc != 0))
rc = mdbx_mutex_failed(env, mutex, rc);
return rc;
}
static int mdbx_robust_unlock(MDBX_env *env, pthread_mutex_t *mutex) { static int mdbx_robust_unlock(MDBX_env *env, pthread_mutex_t *mutex) {
int rc = pthread_mutex_unlock(mutex); int rc = pthread_mutex_unlock(mutex);
if (unlikely(rc != 0)) if (unlikely(rc != 0))
@ -213,6 +220,13 @@ int mdbx_txn_lock(MDBX_env *env) {
return MDBX_IS_ERROR(rc) ? rc : MDBX_SUCCESS; return MDBX_IS_ERROR(rc) ? rc : MDBX_SUCCESS;
} }
int mdbx_txn_trylock(MDBX_env *env) {
mdbx_trace(">>");
int rc = mdbx_robust_trylock(env, &env->me_lck->mti_wmutex);
mdbx_trace("<< rc %d", rc);
return MDBX_IS_ERROR(rc) ? rc : MDBX_SUCCESS;
}
void mdbx_txn_unlock(MDBX_env *env) { void mdbx_txn_unlock(MDBX_env *env) {
mdbx_trace(">>"); mdbx_trace(">>");
int rc = mdbx_robust_unlock(env, &env->me_lck->mti_wmutex); int rc = mdbx_robust_unlock(env, &env->me_lck->mti_wmutex);
@ -313,7 +327,9 @@ static int __cold mdbx_mutex_failed(MDBX_env *env, pthread_mutex_t *mutex,
#endif /* MDBX_USE_ROBUST */ #endif /* MDBX_USE_ROBUST */
mdbx_error("mutex (un)lock failed, %s", mdbx_strerror(rc)); mdbx_error("mutex (un)lock failed, %s", mdbx_strerror(rc));
if (rc != EDEADLK) { if (rc == EBUSY) {
rc = MDBX_BUSY;
} else if (rc != EDEADLK && rc != EBUSY) {
env->me_flags |= MDBX_FATAL_ERROR; env->me_flags |= MDBX_FATAL_ERROR;
rc = MDBX_PANIC; rc = MDBX_PANIC;
} }

View File

@ -132,6 +132,16 @@ int mdbx_txn_lock(MDBX_env *env) {
return GetLastError(); return GetLastError();
} }
int mdbx_txn_trylock(MDBX_env *env) {
if (flock(env->me_fd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_BODY))
return MDBX_SUCCESS;
int rc = GetLastError();
if (rc == ERROR_LOCK_VIOLATION) {
rc = MDBX_BUSY;
}
return rc;
}
void mdbx_txn_unlock(MDBX_env *env) { void mdbx_txn_unlock(MDBX_env *env) {
if (!funlock(env->me_fd, LCK_BODY)) if (!funlock(env->me_fd, LCK_BODY))
mdbx_panic("%s failed: errcode %u", mdbx_func_, GetLastError()); mdbx_panic("%s failed: errcode %u", mdbx_func_, GetLastError());

View File

@ -708,6 +708,7 @@ static const char *__mdbx_strerr(int errnum) {
"DUPFIXED size", "DUPFIXED size",
"MDBX_BAD_DBI: The specified DBI handle was closed/changed unexpectedly", "MDBX_BAD_DBI: The specified DBI handle was closed/changed unexpectedly",
"MDBX_PROBLEM: Unexpected problem - txn should abort", "MDBX_PROBLEM: Unexpected problem - txn should abort",
"MDBX_BUSY: Another write transation is started",
}; };
if (errnum >= MDBX_KEYEXIST && errnum <= MDBX_LAST_ERRCODE) { if (errnum >= MDBX_KEYEXIST && errnum <= MDBX_LAST_ERRCODE) {
@ -2562,7 +2563,7 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) {
} else { } else {
/* Not yet touching txn == env->me_txn0, it may be active */ /* Not yet touching txn == env->me_txn0, it may be active */
mdbx_jitter4testing(false); mdbx_jitter4testing(false);
rc = mdbx_txn_lock(env); rc = F_ISSET(flags, MDBX_TRYTXN) ? mdbx_txn_trylock(env) : mdbx_txn_lock(env);
if (unlikely(rc)) if (unlikely(rc))
return rc; return rc;
@ -2664,6 +2665,7 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags,
MDBX_txn **ret) { MDBX_txn **ret) {
MDBX_txn *txn; MDBX_txn *txn;
MDBX_ntxn *ntxn; MDBX_ntxn *ntxn;
//unsigned pflags;
int rc, size, tsize; int rc, size, tsize;
if (unlikely(!env || !ret)) if (unlikely(!env || !ret))
@ -2718,7 +2720,7 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags,
txn->mt_dbxs = env->me_dbxs; /* static */ txn->mt_dbxs = env->me_dbxs; /* static */
txn->mt_dbs = (MDBX_db *)((char *)txn + tsize); txn->mt_dbs = (MDBX_db *)((char *)txn + tsize);
txn->mt_dbflags = (uint8_t *)txn + size - env->me_maxdbs; txn->mt_dbflags = (uint8_t *)txn + size - env->me_maxdbs;
txn->mt_flags = flags; txn->mt_flags = flags & MDBX_TXN_BEGIN_FLAGS_PERSISTENT;
txn->mt_env = env; txn->mt_env = env;
if (parent) { if (parent) {

View File

@ -506,6 +506,7 @@ int mdbx_rdt_lock(MDBX_env *env);
void mdbx_rdt_unlock(MDBX_env *env); void mdbx_rdt_unlock(MDBX_env *env);
int mdbx_txn_lock(MDBX_env *env); int mdbx_txn_lock(MDBX_env *env);
int mdbx_txn_trylock(MDBX_env *env);
void mdbx_txn_unlock(MDBX_env *env); void mdbx_txn_unlock(MDBX_env *env);
int mdbx_rpid_set(MDBX_env *env); int mdbx_rpid_set(MDBX_env *env);