diff --git a/TODO.md b/TODO.md index d8e2d0b7..b79a5824 100644 --- a/TODO.md +++ b/TODO.md @@ -11,8 +11,7 @@ For the same reason ~~Github~~ is blacklisted forever. So currently most of the links are broken due to noted malicious ~~Github~~ sabotage. - - [Replace SRW-lock on Windows to allow shrink DB with `MDBX_NOTLS` option](https://libmdbx.dqdkfa.ru/dead-github/issues/210). - - [More flexible support of asynchronous runtime/framework(s)](https://libmdbx.dqdkfa.ru/dead-github/issues/200). + - [Replace SRW-lock on Windows to allow shrink DB with `MDBX_NOSTICKYTHREADS` option](https://libmdbx.dqdkfa.ru/dead-github/issues/210). - [Migration guide from LMDB to MDBX](https://libmdbx.dqdkfa.ru/dead-github/issues/199). - [Support for RAW devices](https://libmdbx.dqdkfa.ru/dead-github/issues/124). - [Support MessagePack for Keys & Values](https://libmdbx.dqdkfa.ru/dead-github/issues/115). @@ -22,6 +21,7 @@ So currently most of the links are broken due to noted malicious ~~Github~~ sabo Done ---- + - [More flexible support of asynchronous runtime/framework(s)](https://libmdbx.dqdkfa.ru/dead-github/issues/200). - [Move most of `mdbx_chk` functional to the library API](https://libmdbx.dqdkfa.ru/dead-github/issues/204). - [Simple careful mode for working with corrupted DB](https://libmdbx.dqdkfa.ru/dead-github/issues/223). - [Engage an "overlapped I/O" on Windows](https://libmdbx.dqdkfa.ru/dead-github/issues/224). diff --git a/docs/_restrictions.md b/docs/_restrictions.md index d967cca7..64c54de6 100644 --- a/docs/_restrictions.md +++ b/docs/_restrictions.md @@ -190,18 +190,20 @@ readers without writer" case. ## One thread - One transaction - A thread can only use one transaction at a time, plus any nested - read-write transactions in the non-writemap mode. Each transaction - belongs to one thread. The \ref MDBX_NOTLS flag changes this for read-only - transactions. See below. +A thread can only use one transaction at a time, plus any nested +read-write transactions in the non-writemap mode. Each transaction +belongs to one thread. The \ref MDBX_NOSTICKYTHREADS flag changes this, +see below. - Do not start more than one transaction for a one thread. If you think - about this, it's really strange to do something with two data snapshots - at once, which may be different. MDBX checks and preventing this by - returning corresponding error code (\ref MDBX_TXN_OVERLAPPING, \ref MDBX_BAD_RSLOT, - \ref MDBX_BUSY) unless you using \ref MDBX_NOTLS option on the environment. - Nonetheless, with the `MDBX_NOTLS` option, you must know exactly what you - are doing, otherwise you will get deadlocks or reading an alien data. +Do not start more than one transaction for a one thread. If you think +about this, it's really strange to do something with two data snapshots +at once, which may be different. MDBX checks and preventing this by +returning corresponding error code (\ref MDBX_TXN_OVERLAPPING, +\ref MDBX_BAD_RSLOT, \ref MDBX_BUSY) unless you using +\ref MDBX_NOSTICKYTHREADS option on the environment. +Nonetheless, with the `MDBX_NOSTICKYTHREADS` option, you must know +exactly what you are doing, otherwise you will get deadlocks or reading +an alien data. ## Do not open twice diff --git a/docs/_starting.md b/docs/_starting.md index 30336857..f030ecbf 100644 --- a/docs/_starting.md +++ b/docs/_starting.md @@ -129,20 +129,23 @@ no open MDBX-instance(s) during fork(), or at least close it immediately after necessary) in a child process would be both extreme complicated and so fragile. -Do not start more than one transaction for a one thread. If you think about -this, it's really strange to do something with two data snapshots at once, -which may be different. MDBX checks and preventing this by returning -corresponding error code (\ref MDBX_TXN_OVERLAPPING, \ref MDBX_BAD_RSLOT, \ref MDBX_BUSY) -unless you using \ref MDBX_NOTLS option on the environment. Nonetheless, with the -\ref MDBX_NOTLS option, you must know exactly what you are doing, otherwise you -will get deadlocks or reading an alien data. +Do not start more than one transaction for a one thread. If you think +about this, it's really strange to do something with two data snapshots +at once, which may be different. MDBX checks and preventing this by +returning corresponding error code (\ref MDBX_TXN_OVERLAPPING, +\ref MDBX_BAD_RSLOT, \ref MDBX_BUSY) unless you using +\ref MDBX_NOSTICKYTHREADS option on the environment. Nonetheless, +with the \ref MDBX_NOSTICKYTHREADS option, you must know exactly what +you are doing, otherwise you will get deadlocks or reading an alien +data. -Also note that a transaction is tied to one thread by default using Thread -Local Storage. If you want to pass read-only transactions across threads, -you can use the \ref MDBX_NOTLS option on the environment. Nevertheless, a write -transaction entirely should only be used in one thread from start to finish. -MDBX checks this in a reasonable manner and return the \ref MDBX_THREAD_MISMATCH -error in rules violation. +Also note that a transaction is tied to one thread by default using +Thread Local Storage. If you want to pass transactions across threads, +you can use the \ref MDBX_NOSTICKYTHREADS option on the environment. +Nevertheless, a write transaction must be committed or aborted in the +same thread which it was started. MDBX checks this in a reasonable +manner and return the \ref MDBX_THREAD_MISMATCH error in rules +violation. ## Transactions, rollbacks etc diff --git a/mdbx.h b/mdbx.h index 9476d0dc..d259277f 100644 --- a/mdbx.h +++ b/mdbx.h @@ -1207,28 +1207,80 @@ enum MDBX_env_flags_t { */ MDBX_WRITEMAP = UINT32_C(0x80000), - /** Tie reader locktable slots to read-only transactions - * instead of to threads. + /** Отвязывает транзакции от потоков/threads насколько это возможно. * - * Don't use Thread-Local Storage, instead tie reader locktable slots to - * \ref MDBX_txn objects instead of to threads. So, \ref mdbx_txn_reset() - * keeps the slot reserved for the \ref MDBX_txn object. A thread may use - * parallel read-only transactions. And a read-only transaction may span - * threads if you synchronizes its use. + * Эта опция предназначена для приложений, которые мультиплексируют множество + * пользовательских легковесных потоков выполнения по отдельным потокам + * операционной системы, например как это происходит в средах выполнения + * GoLang и Rust. Таким приложениям также рекомендуется сериализовать + * транзакции записи в одном потоке операционной системы, поскольку блокировка + * записи MDBX использует базовые системные примитивы синхронизации и ничего + * не знает о пользовательских потоках и/или легковесных потоков среды + * выполнения. Как минимум, обязательно требуется обеспечить завершение каждой + * пишущей транзакции строго в том же потоке операционной системы где она была + * запущена. * - * 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 MDBX's write locking is unaware of - * the user threads. + * \note Начиная с версии v0.13 опция `MDBX_NOSTICKYTHREADS` полностью + * заменяет опцию \ref MDBX_NOTLS. * - * \note Regardless to `MDBX_NOTLS` flag a write transaction entirely should - * always be used in one thread from start to finish. MDBX checks this in a - * reasonable manner and return the \ref MDBX_THREAD_MISMATCH error in rules - * violation. + * При использовании `MDBX_NOSTICKYTHREADS` транзакции становятся не + * ассоциированными с создавшими их потоками выполнения. Поэтому в функциях + * API не выполняется проверка соответствия транзакции и текущего потока + * выполнения. Большинство функций работающих с транзакциями и курсорами + * становится возможным вызывать из любых потоков выполнения. Однако, также + * становится невозможно обнаружить ошибки одновременного использования + * транзакций и/или курсоров в разных потоках. * - * This flag affects only at environment opening but can't be changed after. + * Использование `MDBX_NOSTICKYTHREADS` также сужает возможности по изменению + * размера БД, так как теряется возможность отслеживать работающие с БД потоки + * выполнения и приостанавливать их на время снятия отображения БД в ОЗУ. В + * частности, по этой причине на Windows уменьшение файла БД не возможно до + * закрытия БД последним работающим с ней процессом или до последующего + * открытия БД в режиме чтения-записи. + * + * \warning Вне зависимости от \ref MDBX_NOSTICKYTHREADS и \ref MDBX_NOTLS не + * допускается одновременно использование объектов API из разных потоков + * выполнения! Обеспечение всех мер для исключения одновременного + * использования объектов API из разных потоков выполнения целиком ложится на + * вас! + * + * \warning Транзакции записи могут быть завершены только в том же потоке + * выполнения где они были запущены. Это ограничение следует из требований + * большинства операционных систем о том, что захваченный примитив + * синхронизации (мьютекс, семафор, критическая секция) должен освобождаться + * только захватившим его потоком выполнения. + * + * \warning Создание курсора в контексте транзакции, привязка курсора к + * транзакции, отвязка курсора от транзакции и закрытие привязанного к + * транзакции курсора, являются операциями использующими как сам курсор так и + * соответствующую транзакцию. Аналогично, завершение или прерывание + * транзакции является операцией использующей как саму транзакцию, так и все + * привязанные к ней курсоры. Во избежание повреждения внутренних структур + * данных, непредсказуемого поведения, разрушение БД и потери данных следует + * не допускать возможности одновременного использования каких-либо курсора + * или транзакций из разных потоков выполнения. + * + * Читающие транзакции при использовании `MDBX_NOSTICKYTHREADS` перестают + * использовать TLS (Thread Local Storage), а слоты блокировок MVCC-снимков в + * таблице читателей привязываются только к транзакциям. Завершение каких-либо + * потоков не приводит к снятию блокировок MVCC-снимков до явного завершения + * транзакций, либо до завершения соответствующего процесса в целом. + * + * Для пишущих транзакций не выполняется проверка соответствия текущего потока + * выполнения и потока создавшего транзакцию. Однако, фиксация или прерывание + * пишущих транзакций должны выполняться строго в потоке запустившим + * транзакцию, так как эти операции связаны с захватом и освобождением + * примитивов синхронизации (мьютексов, критических секций), для которых + * большинство операционных систем требует освобождение только потоком + * захватившим ресурс. + * + * Этот флаг вступает в силу при открытии среды и не может быть изменен после. */ - MDBX_NOTLS = UINT32_C(0x200000), + MDBX_NOSTICKYTHREADS = UINT32_C(0x200000), +#ifndef _MSC_VER /* avoid madness MSVC */ + /** \deprecated Please use \ref MDBX_NOSTICKYTHREADS instead. */ + MDBX_NOTLS MDBX_DEPRECATED = MDBX_NOSTICKYTHREADS, +#endif /* avoid madness MSVC */ /** Don't do readahead. * @@ -2121,11 +2173,12 @@ enum MDBX_option_t { * track readers in the the environment. The default is about 100 for 4K * system page size. Starting a read-only transaction normally ties a lock * table slot to the current thread until the environment closes or the thread - * exits. If \ref MDBX_NOTLS is in use, \ref mdbx_txn_begin() instead ties the - * slot to the \ref MDBX_txn object until it or the \ref MDBX_env object is - * destroyed. This option may only set after \ref mdbx_env_create() and before - * \ref mdbx_env_open(), and has an effect only when the database is opened by - * the first process interacts with the database. + * exits. If \ref MDBX_NOSTICKYTHREADS is in use, \ref mdbx_txn_begin() + * instead ties the slot to the \ref MDBX_txn object until it or the \ref + * MDBX_env object is destroyed. This option may only set after \ref + * mdbx_env_create() and before \ref mdbx_env_open(), and has an effect only + * when the database is opened by the first process interacts with the + * database. * * \see mdbx_env_set_maxreaders() \see mdbx_env_get_maxreaders() */ MDBX_opt_max_readers, @@ -2389,7 +2442,7 @@ LIBMDBX_API int mdbx_env_get_option(const MDBX_env *env, * * Flags set by mdbx_env_set_flags() are also used: * - \ref MDBX_ENV_DEFAULTS, \ref MDBX_NOSUBDIR, \ref MDBX_RDONLY, - * \ref MDBX_EXCLUSIVE, \ref MDBX_WRITEMAP, \ref MDBX_NOTLS, + * \ref MDBX_EXCLUSIVE, \ref MDBX_WRITEMAP, \ref MDBX_NOSTICKYTHREADS, * \ref MDBX_NORDAHEAD, \ref MDBX_NOMEMINIT, \ref MDBX_COALESCE, * \ref MDBX_LIFORECLAIM. See \ref env_flags section. * @@ -3385,7 +3438,7 @@ LIBMDBX_API int mdbx_env_get_fd(const MDBX_env *env, mdbx_filehandle_t *fd); * 2) Temporary close memory mapped is required to change * geometry, but there read transaction(s) is running * and no corresponding thread(s) could be suspended - * since the \ref MDBX_NOTLS mode is used. + * since the \ref MDBX_NOSTICKYTHREADS mode is used. * \retval MDBX_EACCESS The environment opened in read-only. * \retval MDBX_MAP_FULL Specified size smaller than the space already * consumed by the environment. @@ -3504,11 +3557,11 @@ mdbx_limits_txnsize_max(intptr_t pagesize); * track readers in the the environment. The default is about 100 for 4K system * page size. Starting a read-only transaction normally ties a lock table slot * to the current thread until the environment closes or the thread exits. If - * \ref MDBX_NOTLS is in use, \ref mdbx_txn_begin() instead ties the slot to the - * \ref MDBX_txn object until it or the \ref MDBX_env object is destroyed. - * This function may only be called after \ref mdbx_env_create() and before - * \ref mdbx_env_open(), and has an effect only when the database is opened by - * the first process interacts with the database. + * \ref MDBX_NOSTICKYTHREADS is in use, \ref mdbx_txn_begin() instead ties the + * slot to the \ref MDBX_txn object until it or the \ref MDBX_env object is + * destroyed. This function may only be called after \ref mdbx_env_create() and + * before \ref mdbx_env_open(), and has an effect only when the database is + * opened by the first process interacts with the database. * \see mdbx_env_get_maxreaders() * * \param [in] env An environment handle returned @@ -3702,8 +3755,8 @@ mdbx_env_get_userctx(const MDBX_env *env); * \see mdbx_txn_begin() * * \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 \ref MDBX_NOTLS - * is in use, this does not apply to read-only transactions. + * and a thread may only have a single transaction at a time unless + * the \ref MDBX_NOSTICKYTHREADS is used. * * \note Cursors may not span transactions. * @@ -3764,8 +3817,8 @@ LIBMDBX_API int mdbx_txn_begin_ex(MDBX_env *env, MDBX_txn *parent, * \see mdbx_txn_begin_ex() * * \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 \ref MDBX_NOTLS - * is in use, this does not apply to read-only transactions. + * and a thread may only have a single transaction at a time unless + * the \ref MDBX_NOSTICKYTHREADS is used. * * \note Cursors may not span transactions. * @@ -4140,10 +4193,11 @@ LIBMDBX_API int mdbx_txn_break(MDBX_txn *txn); * Abort the read-only transaction like \ref mdbx_txn_abort(), but keep the * transaction handle. Therefore \ref mdbx_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 \ref MDBX_NOTLS is in use. The - * reader table lock is released, but the table slot stays tied to its thread - * or \ref MDBX_txn. Use \ref mdbx_txn_abort() to discard a reset handle, and to - * free its lock table slot if \ref MDBX_NOTLS is in use. + * transaction soon, and also locking overhead if \ref MDBX_NOSTICKYTHREADS is + * in use. The reader table lock is released, but the table slot stays tied to + * its thread or \ref MDBX_txn. Use \ref mdbx_txn_abort() to discard a reset + * handle, and to free its lock table slot if \ref MDBX_NOSTICKYTHREADS is in + * use. * * Cursors opened within the transaction must not be used again after this * call, except with \ref mdbx_cursor_renew() and \ref mdbx_cursor_close(). diff --git a/mdbx.h++ b/mdbx.h++ index 5bc42809..f8f6df4c 100644 --- a/mdbx.h++ +++ b/mdbx.h++ @@ -3679,8 +3679,8 @@ public: /// \brief Operate options. struct LIBMDBX_API_TYPE operate_options { - /// \copydoc MDBX_NOTLS - bool orphan_read_transactions{false}; + /// \copydoc MDBX_NOSTICKYTHREADS + bool no_sticky_threads{false}; /// \brief Разрешает вложенные транзакции ценой отключения /// \ref MDBX_WRITEMAP и увеличением накладных расходов. bool nested_write_transactions{false}; diff --git a/src/bits.md b/src/bits.md index d8166d16..abcedf8b 100644 --- a/src/bits.md +++ b/src/bits.md @@ -21,7 +21,7 @@ N | MASK | ENV | TXN | DB | PUT | DBI | NOD 18|0004 0000|NOMETASYNC |TXN_NOMETASYNC|CREATE |APPENDDUP | | | | | 19|0008 0000|WRITEMAP |<= | |MULTIPLE | | | | <= | 20|0010 0000|UTTERLY | | | | | | | <= | -21|0020 0000|NOTLS |<= | | | | | | | +21|0020 0000|NOSTICKYTHR|<= | | | | | | | 22|0040 0000|EXCLUSIVE | | | | | | | | 23|0080 0000|NORDAHEAD | | | | | | | | 24|0100 0000|NOMEMINIT |TXN_PREPARE | | | | | | | diff --git a/src/core.c b/src/core.c index 9038b8ef..2f5a7a2f 100644 --- a/src/core.c +++ b/src/core.c @@ -1580,7 +1580,7 @@ __cold int rthc_register(MDBX_env *const env) { rthc_limit *= 2; } - if ((env->me_flags & MDBX_NOTLS) == 0) { + if ((env->me_flags & MDBX_NOSTICKYTHREADS) == 0) { rc = thread_key_create(&env->me_txkey); if (unlikely(rc != MDBX_SUCCESS)) goto bailout; @@ -3275,7 +3275,7 @@ enum { #define TXN_END_UPDATE 0x10 /* update env state (DBIs) */ #define TXN_END_FREE 0x20 /* free txn unless it is MDBX_env.me_txn0 */ #define TXN_END_EOTDONE 0x40 /* txn's cursors already closed */ -#define TXN_END_SLOT 0x80 /* release any reader slot if MDBX_NOTLS */ +#define TXN_END_SLOT 0x80 /* release any reader slot if NOSTICKYTHREADS */ static int txn_end(MDBX_txn *txn, const unsigned mode); static __always_inline pgr_t page_get_inline(const uint16_t ILL, @@ -6562,60 +6562,63 @@ __cold static int dxb_resize(MDBX_env *const env, const pgno_t used_pgno, size_bytes == env->me_dxb_mmap.filesize) goto bailout; + /* При использовании MDBX_NOSTICKYTHREADS с транзакциями могут работать любые + * потоки и у нас нет информации о том, какие именно. Поэтому нет возможности + * выполнить remap-действия требующие приостановки работающих с БД потоков. */ + if ((env->me_flags & MDBX_NOSTICKYTHREADS) == 0) { #if defined(_WIN32) || defined(_WIN64) - if ((env->me_flags & MDBX_NOTLS) == 0 && - ((size_bytes < env->me_dxb_mmap.current && mode > implicit_grow) || - limit_bytes != env->me_dxb_mmap.limit)) { - /* 1) Windows allows only extending a read-write section, but not a - * corresponding mapped view. Therefore in other cases we must suspend - * the local threads for safe remap. - * 2) At least on Windows 10 1803 the entire mapped section is unavailable - * for short time during NtExtendSection() or VirtualAlloc() execution. - * 3) Under Wine runtime environment on Linux a section extending is not - * supported. - * - * THEREFORE LOCAL THREADS SUSPENDING IS ALWAYS REQUIRED! */ - array_onstack.limit = ARRAY_LENGTH(array_onstack.handles); - array_onstack.count = 0; - suspended = &array_onstack; - rc = osal_suspend_threads_before_remap(env, &suspended); - if (rc != MDBX_SUCCESS) { - ERROR("failed suspend-for-remap: errcode %d", rc); - goto bailout; - } - mresize_flags |= (mode < explicit_resize) - ? MDBX_MRESIZE_MAY_UNMAP - : MDBX_MRESIZE_MAY_UNMAP | MDBX_MRESIZE_MAY_MOVE; - } -#else /* Windows */ - MDBX_lockinfo *const lck = env->me_lck_mmap.lck; - if (mode == explicit_resize && limit_bytes != env->me_dxb_mmap.limit && - !(env->me_flags & MDBX_NOTLS)) { - mresize_flags |= MDBX_MRESIZE_MAY_UNMAP | MDBX_MRESIZE_MAY_MOVE; - if (lck) { - int err = osal_rdt_lock(env) /* lock readers table until remap done */; - if (unlikely(MDBX_IS_ERROR(err))) { - rc = err; + if ((size_bytes < env->me_dxb_mmap.current && mode > implicit_grow) || + limit_bytes != env->me_dxb_mmap.limit) { + /* 1) Windows allows only extending a read-write section, but not a + * corresponding mapped view. Therefore in other cases we must suspend + * the local threads for safe remap. + * 2) At least on Windows 10 1803 the entire mapped section is unavailable + * for short time during NtExtendSection() or VirtualAlloc() execution. + * 3) Under Wine runtime environment on Linux a section extending is not + * supported. + * + * THEREFORE LOCAL THREADS SUSPENDING IS ALWAYS REQUIRED! */ + array_onstack.limit = ARRAY_LENGTH(array_onstack.handles); + array_onstack.count = 0; + suspended = &array_onstack; + rc = osal_suspend_threads_before_remap(env, &suspended); + if (rc != MDBX_SUCCESS) { + ERROR("failed suspend-for-remap: errcode %d", rc); goto bailout; } + mresize_flags |= (mode < explicit_resize) + ? MDBX_MRESIZE_MAY_UNMAP + : MDBX_MRESIZE_MAY_UNMAP | MDBX_MRESIZE_MAY_MOVE; + } +#else /* Windows */ + MDBX_lockinfo *const lck = env->me_lck_mmap.lck; + if (mode == explicit_resize && limit_bytes != env->me_dxb_mmap.limit) { + mresize_flags |= MDBX_MRESIZE_MAY_UNMAP | MDBX_MRESIZE_MAY_MOVE; + if (lck) { + int err = osal_rdt_lock(env) /* lock readers table until remap done */; + if (unlikely(MDBX_IS_ERROR(err))) { + rc = err; + goto bailout; + } - /* looking for readers from this process */ - const size_t snap_nreaders = - atomic_load32(&lck->mti_numreaders, mo_AcquireRelease); - eASSERT(env, mode == explicit_resize); - for (size_t i = 0; i < snap_nreaders; ++i) { - if (lck->mti_readers[i].mr_pid.weak == env->me_pid && - lck->mti_readers[i].mr_tid.weak != osal_thread_self()) { - /* the base address of the mapping can't be changed since - * the other reader thread from this process exists. */ - osal_rdt_unlock(env); - mresize_flags &= ~(MDBX_MRESIZE_MAY_UNMAP | MDBX_MRESIZE_MAY_MOVE); - break; + /* looking for readers from this process */ + const size_t snap_nreaders = + atomic_load32(&lck->mti_numreaders, mo_AcquireRelease); + eASSERT(env, mode == explicit_resize); + for (size_t i = 0; i < snap_nreaders; ++i) { + if (lck->mti_readers[i].mr_pid.weak == env->me_pid && + lck->mti_readers[i].mr_tid.weak != osal_thread_self()) { + /* the base address of the mapping can't be changed since + * the other reader thread from this process exists. */ + osal_rdt_unlock(env); + mresize_flags &= ~(MDBX_MRESIZE_MAY_UNMAP | MDBX_MRESIZE_MAY_MOVE); + break; + } } } } - } #endif /* ! Windows */ + } const pgno_t aligned_munlock_pgno = (mresize_flags & (MDBX_MRESIZE_MAY_UNMAP | MDBX_MRESIZE_MAY_MOVE)) @@ -8616,26 +8619,30 @@ static int meta_sync(const MDBX_env *env, const meta_ptr_t head) { return rc; } +static __inline bool env_txn0_owned(const MDBX_env *env) { + return (env->me_flags & MDBX_NOSTICKYTHREADS) + ? (env->me_txn0->mt_owner != 0) + : (env->me_txn0->mt_owner == osal_thread_self()); +} + __cold static int env_sync(MDBX_env *env, bool force, bool nonblock) { - bool locked = false; + if (unlikely(env->me_flags & MDBX_RDONLY)) + return MDBX_EACCESS; + + const bool txn0_owned = env_txn0_owned(env); + bool should_unlock = false; int rc = MDBX_RESULT_TRUE /* means "nothing to sync" */; retry:; unsigned flags = env->me_flags & ~(MDBX_NOMETASYNC | MDBX_SHRINK_ALLOWED); - if (unlikely((flags & (MDBX_RDONLY | MDBX_FATAL_ERROR | MDBX_ENV_ACTIVE)) != + if (unlikely((flags & (MDBX_FATAL_ERROR | MDBX_ENV_ACTIVE)) != MDBX_ENV_ACTIVE)) { - rc = MDBX_EACCESS; - if (!(flags & MDBX_ENV_ACTIVE)) - rc = MDBX_EPERM; - if (flags & MDBX_FATAL_ERROR) - rc = MDBX_PANIC; + rc = (flags & MDBX_FATAL_ERROR) ? MDBX_PANIC : MDBX_EPERM; goto bailout; } - const bool inside_txn = - (!locked && env->me_txn0->mt_owner == osal_thread_self()); const meta_troika_t troika = - (inside_txn | locked) ? env->me_txn0->tw.troika : meta_tap(env); + (txn0_owned | should_unlock) ? env->me_txn0->tw.troika : meta_tap(env); const meta_ptr_t head = meta_recent(env, &troika); const uint64_t unsynced_pages = atomic_load64(&env->me_lck->mti_unsynced_pages, mo_Relaxed); @@ -8646,7 +8653,7 @@ retry:; goto bailout; } - if (locked && (env->me_flags & MDBX_WRITEMAP) && + if (should_unlock && (env->me_flags & MDBX_WRITEMAP) && unlikely(head.ptr_c->mm_geo.next > bytes2pgno(env, env->me_dxb_mmap.current))) { @@ -8676,8 +8683,8 @@ retry:; osal_monotime() - eoos_timestamp >= autosync_period)) flags &= MDBX_WRITEMAP /* clear flags for full steady sync */; - if (!inside_txn) { - if (!locked) { + if (!txn0_owned) { + if (!should_unlock) { #if MDBX_ENABLE_PGOP_STAT unsigned wops = 0; #endif /* MDBX_ENABLE_PGOP_STAT */ @@ -8723,7 +8730,7 @@ retry:; if (unlikely(err != MDBX_SUCCESS)) return err; - locked = true; + should_unlock = true; #if MDBX_ENABLE_PGOP_STAT env->me_lck->mti_pgop_stat.wops.weak += wops; #endif /* MDBX_ENABLE_PGOP_STAT */ @@ -8737,8 +8744,8 @@ retry:; flags |= MDBX_SHRINK_ALLOWED; } - eASSERT(env, inside_txn || locked); - eASSERT(env, !inside_txn || (flags & MDBX_SHRINK_ALLOWED) == 0); + eASSERT(env, txn0_owned || should_unlock); + eASSERT(env, !txn0_owned || (flags & MDBX_SHRINK_ALLOWED) == 0); if (!head.is_steady && unlikely(env->me_stuck_meta >= 0) && troika.recent != (uint8_t)env->me_stuck_meta) { @@ -8765,7 +8772,7 @@ retry:; rc = meta_sync(env, head); bailout: - if (locked) + if (should_unlock) osal_txn_unlock(env); return rc; } @@ -8854,7 +8861,7 @@ static void txn_valgrind(MDBX_env *env, MDBX_txn *txn) { if (env->me_pid != osal_getpid()) { /* resurrect after fork */ return; - } else if (env->me_txn0 && env->me_txn0->mt_owner == osal_thread_self()) { + } else if (env->me_txn && env_txn0_owned(env)) { /* inside write-txn */ last = meta_recent(env, &env->me_txn0->tw.troika).ptr_v->mm_geo.next; } else if (env->me_flags & MDBX_RDONLY) { @@ -8950,7 +8957,7 @@ static bind_rslot_result bind_rslot(MDBX_env *env, const uintptr_t tid) { safe64_reset(&result.rslot->mr_txnid, true); if (slot == nreaders) env->me_lck->mti_numreaders.weak = (uint32_t)++nreaders; - result.rslot->mr_tid.weak = (env->me_flags & MDBX_NOTLS) ? 0 : tid; + result.rslot->mr_tid.weak = (env->me_flags & MDBX_NOSTICKYTHREADS) ? 0 : tid; atomic_store32(&result.rslot->mr_pid, env->me_pid, mo_AcquireRelease); osal_rdt_unlock(env); @@ -8970,12 +8977,12 @@ __cold int mdbx_thread_register(const MDBX_env *env) { return (env->me_flags & MDBX_EXCLUSIVE) ? MDBX_EINVAL : MDBX_EPERM; if (unlikely((env->me_flags & MDBX_ENV_TXKEY) == 0)) { - eASSERT(env, !env->me_lck_mmap.lck || (env->me_flags & MDBX_NOTLS)); - return MDBX_EINVAL /* MDBX_NOTLS mode */; + eASSERT(env, env->me_flags & MDBX_NOSTICKYTHREADS); + return MDBX_EINVAL /* MDBX_NOSTICKYTHREADS mode */; } - eASSERT(env, (env->me_flags & (MDBX_NOTLS | MDBX_ENV_TXKEY | - MDBX_EXCLUSIVE)) == MDBX_ENV_TXKEY); + eASSERT(env, (env->me_flags & (MDBX_NOSTICKYTHREADS | MDBX_ENV_TXKEY)) == + MDBX_ENV_TXKEY); MDBX_reader *r = thread_rthc_get(env->me_txkey); if (unlikely(r != NULL)) { eASSERT(env, r->mr_pid.weak == env->me_pid); @@ -8986,7 +8993,7 @@ __cold int mdbx_thread_register(const MDBX_env *env) { } const uintptr_t tid = osal_thread_self(); - if (env->me_txn0 && unlikely(env->me_txn0->mt_owner == tid) && env->me_txn) + if (env->me_txn && unlikely(env->me_txn0->mt_owner == tid)) return MDBX_TXN_OVERLAPPING; return bind_rslot((MDBX_env *)env, tid).err; } @@ -9000,12 +9007,12 @@ __cold int mdbx_thread_unregister(const MDBX_env *env) { return MDBX_RESULT_TRUE; if (unlikely((env->me_flags & MDBX_ENV_TXKEY) == 0)) { - eASSERT(env, !env->me_lck_mmap.lck || (env->me_flags & MDBX_NOTLS)); - return MDBX_RESULT_TRUE /* MDBX_NOTLS mode */; + eASSERT(env, env->me_flags & MDBX_NOSTICKYTHREADS); + return MDBX_RESULT_TRUE /* MDBX_NOSTICKYTHREADS mode */; } - eASSERT(env, (env->me_flags & (MDBX_NOTLS | MDBX_ENV_TXKEY | - MDBX_EXCLUSIVE)) == MDBX_ENV_TXKEY); + eASSERT(env, (env->me_flags & (MDBX_NOSTICKYTHREADS | MDBX_ENV_TXKEY)) == + MDBX_ENV_TXKEY); MDBX_reader *r = thread_rthc_get(env->me_txkey); if (unlikely(r == NULL)) return MDBX_RESULT_TRUE /* not registered */; @@ -9220,7 +9227,7 @@ static bool check_meta_coherency(const MDBX_env *env, } /* Common code for mdbx_txn_begin() and mdbx_txn_renew(). */ -static int txn_renew(MDBX_txn *txn, const unsigned flags) { +static int txn_renew(MDBX_txn *txn, unsigned flags) { MDBX_env *env = txn->mt_env; int rc; @@ -9245,14 +9252,15 @@ static int txn_renew(MDBX_txn *txn, const unsigned flags) { 0); const uintptr_t tid = osal_thread_self(); + flags |= env->me_flags & (MDBX_NOSTICKYTHREADS | MDBX_WRITEMAP); if (flags & MDBX_TXN_RDONLY) { - eASSERT(env, (flags & ~(MDBX_TXN_RO_BEGIN_FLAGS | MDBX_WRITEMAP)) == 0); - txn->mt_flags = - MDBX_TXN_RDONLY | (env->me_flags & (MDBX_NOTLS | MDBX_WRITEMAP)); + eASSERT(env, (flags & ~(MDBX_TXN_RO_BEGIN_FLAGS | MDBX_WRITEMAP | + MDBX_NOSTICKYTHREADS)) == 0); + txn->mt_flags = flags; MDBX_reader *r = txn->to.reader; STATIC_ASSERT(sizeof(uintptr_t) <= sizeof(r->mr_tid)); if (likely(env->me_flags & MDBX_ENV_TXKEY)) { - eASSERT(env, !(env->me_flags & MDBX_NOTLS)); + eASSERT(env, !(env->me_flags & MDBX_NOSTICKYTHREADS)); r = thread_rthc_get(env->me_txkey); if (likely(r)) { if (unlikely(!r->mr_pid.weak) && @@ -9265,7 +9273,8 @@ static int txn_renew(MDBX_txn *txn, const unsigned flags) { } } } else { - eASSERT(env, !env->me_lck_mmap.lck || (env->me_flags & MDBX_NOTLS)); + eASSERT(env, + !env->me_lck_mmap.lck || (env->me_flags & MDBX_NOSTICKYTHREADS)); } if (likely(r)) { @@ -9313,9 +9322,9 @@ static int txn_renew(MDBX_txn *txn, const unsigned flags) { mo_Relaxed); safe64_write(&r->mr_txnid, head.txnid); eASSERT(env, r->mr_pid.weak == osal_getpid()); - eASSERT(env, - r->mr_tid.weak == - ((env->me_flags & MDBX_NOTLS) ? 0 : osal_thread_self())); + eASSERT(env, r->mr_tid.weak == ((env->me_flags & MDBX_NOSTICKYTHREADS) + ? 0 + : osal_thread_self())); eASSERT(env, r->mr_txnid.weak == head.txnid || (r->mr_txnid.weak >= SAFE64_INVALID_THRESHOLD && head.txnid < env->me_lck->mti_oldest_reader.weak)); @@ -9374,12 +9383,12 @@ static int txn_renew(MDBX_txn *txn, const unsigned flags) { tASSERT(txn, db_check_flags(txn->mt_dbs[MAIN_DBI].md_flags)); } else { eASSERT(env, (flags & ~(MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_SPILLS | - MDBX_WRITEMAP)) == 0); + MDBX_WRITEMAP | MDBX_NOSTICKYTHREADS)) == 0); if (unlikely(txn->mt_owner == tid || /* not recovery mode */ env->me_stuck_meta >= 0)) return MDBX_BUSY; MDBX_lockinfo *const lck = env->me_lck_mmap.lck; - if (lck && (env->me_flags & MDBX_NOTLS) == 0 && + if (lck && (env->me_flags & MDBX_NOSTICKYTHREADS) == 0 && (mdbx_static.flags & MDBX_DBG_LEGACY_OVERLAP) == 0) { const size_t snap_nreaders = atomic_load32(&lck->mti_numreaders, mo_AcquireRelease); @@ -9639,7 +9648,8 @@ static int txn_renew(MDBX_txn *txn, const unsigned flags) { * since Wine don't support section extending, * i.e. in both cases unmap+map are required. */ used_bytes < env->me_dbgeo.upper && env->me_dbgeo.grow)) && - /* avoid recursive use SRW */ (txn->mt_flags & MDBX_NOTLS) == 0) { + /* avoid recursive use SRW */ (txn->mt_flags & + MDBX_NOSTICKYTHREADS) == 0) { txn->mt_flags |= MDBX_SHRINK_ALLOWED; osal_srwlock_AcquireShared(&env->me_remap_guard); } @@ -9673,15 +9683,13 @@ static __always_inline int check_txn(const MDBX_txn *txn, int bad_bits) { return MDBX_BAD_TXN; tASSERT(txn, (txn->mt_flags & MDBX_TXN_FINISHED) || - (txn->mt_flags & MDBX_NOTLS) == - ((txn->mt_flags & MDBX_TXN_RDONLY) - ? txn->mt_env->me_flags & MDBX_NOTLS - : 0)); + (txn->mt_flags & MDBX_NOSTICKYTHREADS) == + (txn->mt_env->me_flags & MDBX_NOSTICKYTHREADS)); #if MDBX_TXN_CHECKOWNER - STATIC_ASSERT(MDBX_NOTLS > MDBX_TXN_FINISHED + MDBX_TXN_RDONLY); - if (unlikely(txn->mt_owner != osal_thread_self()) && - (txn->mt_flags & (MDBX_NOTLS | MDBX_TXN_FINISHED | MDBX_TXN_RDONLY)) < - (MDBX_TXN_FINISHED | MDBX_TXN_RDONLY)) + STATIC_ASSERT((long)MDBX_NOSTICKYTHREADS > (long)MDBX_TXN_FINISHED); + if ((txn->mt_flags & (MDBX_NOSTICKYTHREADS | MDBX_TXN_FINISHED)) < + MDBX_TXN_FINISHED && + unlikely(txn->mt_owner != osal_thread_self())) return txn->mt_owner ? MDBX_THREAD_MISMATCH : MDBX_BAD_TXN; #endif /* MDBX_TXN_CHECKOWNER */ @@ -9762,7 +9770,6 @@ int mdbx_txn_begin_ex(MDBX_env *env, MDBX_txn *parent, MDBX_txn_flags_t flags, ~flags)) /* write txn in RDONLY env */ return MDBX_EACCESS; - flags |= env->me_flags & MDBX_WRITEMAP; MDBX_txn *txn = nullptr; if (parent) { /* Nested transactions: Max 1 child, write txns only, no writemap */ @@ -9781,10 +9788,11 @@ int mdbx_txn_begin_ex(MDBX_env *env, MDBX_txn *parent, MDBX_txn_flags_t flags, } tASSERT(parent, audit_ex(parent, 0, false) == 0); - flags |= parent->mt_flags & (MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_SPILLS); + flags |= parent->mt_flags & (MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_SPILLS | + MDBX_NOSTICKYTHREADS | MDBX_WRITEMAP); } else if (flags & MDBX_TXN_RDONLY) { - if (env->me_txn0 && - unlikely(env->me_txn0->mt_owner == osal_thread_self()) && env->me_txn && + if ((env->me_flags & MDBX_NOSTICKYTHREADS) == 0 && env->me_txn && + unlikely(env->me_txn0->mt_owner == osal_thread_self()) && (mdbx_static.flags & MDBX_DBG_LEGACY_OVERLAP) == 0) return MDBX_TXN_OVERLAPPING; } else { @@ -9967,12 +9975,13 @@ int mdbx_txn_begin_ex(MDBX_env *env, MDBX_txn *parent, MDBX_txn_flags_t flags, eASSERT(env, txn->mt_flags == (MDBX_TXN_RDONLY | MDBX_TXN_FINISHED)); else if (flags & MDBX_TXN_RDONLY) eASSERT(env, (txn->mt_flags & - ~(MDBX_NOTLS | MDBX_TXN_RDONLY | MDBX_WRITEMAP | + ~(MDBX_NOSTICKYTHREADS | MDBX_TXN_RDONLY | MDBX_WRITEMAP | /* Win32: SRWL flag */ MDBX_SHRINK_ALLOWED)) == 0); else { - eASSERT(env, (txn->mt_flags & - ~(MDBX_WRITEMAP | MDBX_SHRINK_ALLOWED | MDBX_NOMETASYNC | - MDBX_SAFE_NOSYNC | MDBX_TXN_SPILLS)) == 0); + eASSERT(env, + (txn->mt_flags & + ~(MDBX_NOSTICKYTHREADS | MDBX_WRITEMAP | MDBX_SHRINK_ALLOWED | + MDBX_NOMETASYNC | MDBX_SAFE_NOSYNC | MDBX_TXN_SPILLS)) == 0); assert(!txn->tw.spilled.list && !txn->tw.spilled.least_removed); } txn->mt_signature = MDBX_MT_SIGNATURE; @@ -10409,6 +10418,13 @@ int mdbx_txn_abort(MDBX_txn *txn) { if (unlikely(rc != MDBX_SUCCESS)) return rc; + if ((txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_NOSTICKYTHREADS)) == + MDBX_NOSTICKYTHREADS && + unlikely(txn->mt_owner != osal_thread_self())) { + mdbx_txn_break(txn); + return MDBX_THREAD_MISMATCH; + } + return txn_abort(txn); } @@ -12093,6 +12109,12 @@ int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency) { if (unlikely(txn->mt_flags & MDBX_TXN_RDONLY)) goto done; + if ((txn->mt_flags & MDBX_NOSTICKYTHREADS) && + unlikely(txn->mt_owner != osal_thread_self())) { + rc = MDBX_THREAD_MISMATCH; + goto fail; + } + if (txn->mt_child) { rc = mdbx_txn_commit_ex(txn->mt_child, NULL); tASSERT(txn, txn->mt_child == NULL); @@ -13757,9 +13779,9 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, if (unlikely(rc != MDBX_SUCCESS)) return rc; - const bool need_lock = - !env->me_txn0 || env->me_txn0->mt_owner != osal_thread_self(); - const bool inside_txn = !need_lock && env->me_txn; + const bool txn0_owned = env->me_txn0 && env_txn0_owned(env); + const bool inside_txn = txn0_owned && env->me_txn; + bool should_unlock = false; #if MDBX_DEBUG if (growth_step < 0) { @@ -13770,13 +13792,12 @@ __cold int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, #endif /* MDBX_DEBUG */ intptr_t reasonable_maxsize = 0; - bool should_unlock = false; if (env->me_map) { /* env already mapped */ if (unlikely(env->me_flags & MDBX_RDONLY)) return MDBX_EACCESS; - if (need_lock) { + if (!txn0_owned) { int err = osal_txn_lock(env, false); if (unlikely(err != MDBX_SUCCESS)) return err; @@ -16024,6 +16045,9 @@ __cold int mdbx_env_close_ex(MDBX_env *env, bool dont_sync) { #endif /* Windows */ } + if (env->me_txn0 && env->me_txn0->mt_owner == osal_thread_self()) + osal_txn_unlock(env); + eASSERT(env, env->me_signature.weak == 0); rc = env_close(env, false) ? MDBX_PANIC : rc; ENSURE(env, osal_fastmutex_destroy(&env->me_dbi_lock) == MDBX_SUCCESS); @@ -22997,8 +23021,8 @@ __cold int mdbx_env_set_flags(MDBX_env *env, MDBX_env_flags_t flags, if (unlikely(env->me_flags & MDBX_RDONLY)) return MDBX_EACCESS; - const bool lock_needed = (env->me_flags & MDBX_ENV_ACTIVE) && - env->me_txn0->mt_owner != osal_thread_self(); + const bool lock_needed = + (env->me_flags & MDBX_ENV_ACTIVE) && !env_txn0_owned(env); bool should_unlock = false; if (lock_needed) { rc = osal_txn_lock(env, false); @@ -23233,8 +23257,7 @@ __cold int mdbx_env_stat_ex(const MDBX_env *env, const MDBX_txn *txn, if (unlikely(err != MDBX_SUCCESS)) return err; - if (env->me_txn0 && env->me_txn0->mt_owner == osal_thread_self() && - env->me_txn) + if (env->me_txn && env_txn0_owned(env)) /* inside write-txn */ return stat_acc(env->me_txn, dest, bytes); @@ -26209,7 +26232,7 @@ __cold int mdbx_env_set_option(MDBX_env *env, const MDBX_option_t option, return err; const bool lock_needed = ((env->me_flags & MDBX_ENV_ACTIVE) && env->me_txn0 && - env->me_txn0->mt_owner != osal_thread_self()); + !env_txn0_owned(env)); bool should_unlock = false; switch (option) { case MDBX_opt_sync_bytes: @@ -26324,7 +26347,7 @@ __cold int mdbx_env_set_option(MDBX_env *env, const MDBX_option_t option, return MDBX_EACCESS; value = osal_16dot16_to_monotime((uint32_t)value); if (value != env->me_options.gc_time_limit) { - if (env->me_txn && env->me_txn0->mt_owner != osal_thread_self()) + if (env->me_txn && lock_needed) return MDBX_EPERM; env->me_options.gc_time_limit = value; if (!env->me_options.flags.non_auto.rp_augment_limit) diff --git a/src/internals.h b/src/internals.h index bb6b5e76..f5213d85 100644 --- a/src/internals.h +++ b/src/internals.h @@ -842,8 +842,9 @@ MDBX_INTERNAL_FUNC int osal_ipclock_destroy(osal_ipclock_t *ipc); * read transactions started by the same thread need no further locking to * proceed. * - * If MDBX_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. + * If MDBX_NOSTICKYTHREADS 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 * actually need any locking. This table is used to keep track of which @@ -1786,8 +1787,8 @@ log2n_powerof2(size_t value_uintptr) { MDBX_NOMEMINIT | MDBX_COALESCE | MDBX_PAGEPERTURB | MDBX_ACCEDE | \ MDBX_VALIDATION) #define ENV_CHANGELESS_FLAGS \ - (MDBX_NOSUBDIR | MDBX_RDONLY | MDBX_WRITEMAP | MDBX_NOTLS | MDBX_NORDAHEAD | \ - MDBX_LIFORECLAIM | MDBX_EXCLUSIVE) + (MDBX_NOSUBDIR | MDBX_RDONLY | MDBX_WRITEMAP | MDBX_NOSTICKYTHREADS | \ + MDBX_NORDAHEAD | MDBX_LIFORECLAIM | MDBX_EXCLUSIVE) #define ENV_USABLE_FLAGS (ENV_CHANGEABLE_FLAGS | ENV_CHANGELESS_FLAGS) #if !defined(__cplusplus) || CONSTEXPR_ENUM_FLAGS_OPERATIONS diff --git a/src/lck-windows.c b/src/lck-windows.c index bc63170d..241800aa 100644 --- a/src/lck-windows.c +++ b/src/lck-windows.c @@ -326,7 +326,7 @@ static int suspend_and_append(mdbx_handle_array_t **array, MDBX_INTERNAL_FUNC int osal_suspend_threads_before_remap(MDBX_env *env, mdbx_handle_array_t **array) { - eASSERT(env, (env->me_flags & MDBX_NOTLS) == 0); + eASSERT(env, (env->me_flags & MDBX_NOSTICKYTHREADS) == 0); const uintptr_t CurrentTid = GetCurrentThreadId(); int rc; if (env->me_lck_mmap.lck) { diff --git a/src/mdbx.c++ b/src/mdbx.c++ index 74690740..51a0f35f 100644 --- a/src/mdbx.c++ +++ b/src/mdbx.c++ @@ -1216,8 +1216,8 @@ env::operate_parameters::make_flags(bool accede, bool use_subdirectory) const { flags |= MDBX_NOSUBDIR; if (options.exclusive) flags |= MDBX_EXCLUSIVE; - if (options.orphan_read_transactions) - flags |= MDBX_NOTLS; + if (options.no_sticky_threads) + flags |= MDBX_NOSTICKYTHREADS; if (options.disable_readahead) flags |= MDBX_NORDAHEAD; if (options.disable_clear_memory) @@ -1275,9 +1275,10 @@ env::reclaiming_options::reclaiming_options(MDBX_env_flags_t flags) noexcept coalesce((flags & MDBX_COALESCE) ? true : false) {} env::operate_options::operate_options(MDBX_env_flags_t flags) noexcept - : orphan_read_transactions( - ((flags & (MDBX_NOTLS | MDBX_EXCLUSIVE)) == MDBX_NOTLS) ? true - : false), + : no_sticky_threads(((flags & (MDBX_NOSTICKYTHREADS | MDBX_EXCLUSIVE)) == + MDBX_NOSTICKYTHREADS) + ? true + : false), nested_write_transactions((flags & (MDBX_WRITEMAP | MDBX_RDONLY)) ? false : true), exclusive((flags & MDBX_EXCLUSIVE) ? true : false), @@ -1831,8 +1832,8 @@ __cold ::std::ostream &operator<<(::std::ostream &out, static const char comma[] = ", "; const char *delimiter = ""; out << "{"; - if (it.orphan_read_transactions) { - out << delimiter << "orphan_read_transactions"; + if (it.no_sticky_threads) { + out << delimiter << "no_sticky_threads"; delimiter = comma; } if (it.nested_write_transactions) { diff --git a/test/config.c++ b/test/config.c++ index 99577f87..d0e14e86 100644 --- a/test/config.c++ +++ b/test/config.c++ @@ -378,7 +378,8 @@ const struct option_verb mode_bits[] = { {"nosync-safe", unsigned(MDBX_SAFE_NOSYNC)}, {"nometasync", unsigned(MDBX_NOMETASYNC)}, {"writemap", unsigned(MDBX_WRITEMAP)}, - {"notls", unsigned(MDBX_NOTLS)}, + {"nostickythreads", unsigned(MDBX_NOSTICKYTHREADS)}, + {"no-sticky-threads", unsigned(MDBX_NOSTICKYTHREADS)}, {"nordahead", unsigned(MDBX_NORDAHEAD)}, {"nomeminit", unsigned(MDBX_NOMEMINIT)}, {"lifo", unsigned(MDBX_LIFORECLAIM)}, diff --git a/test/long_stochastic.sh b/test/long_stochastic.sh index 12b493cb..c74623d5 100755 --- a/test/long_stochastic.sh +++ b/test/long_stochastic.sh @@ -385,9 +385,9 @@ else fi if [ "$EXTRA" != "no" ]; then - options=(writemap lifo notls perturb nomeminit nordahead) + options=(writemap lifo nostickythreads perturb nomeminit nordahead) else - options=(writemap lifo notls) + options=(writemap lifo nostickythreads) fi syncmodes=("" ,+nosync-safe ,+nosync-utterly ,+nometasync) function join { local IFS="$1"; shift; echo "$*"; } diff --git a/test/main.c++ b/test/main.c++ index 16664a2c..e4081b7b 100644 --- a/test/main.c++ +++ b/test/main.c++ @@ -106,7 +106,7 @@ MDBX_NORETURN void usage(void) { " writemap == MDBX_WRITEMAP\n" " nosync-utterly == MDBX_UTTERLY_NOSYNC\n" " perturb == MDBX_PAGEPERTURB\n" - " notls == MDBX_NOTLS\n" + " nostickythreads== MDBX_NOSTICKYTHREADS\n" " nordahead == MDBX_NORDAHEAD\n" " nomeminit == MDBX_NOMEMINIT\n" " --random-writemap[=YES|no] Toggle MDBX_WRITEMAP randomly\n" diff --git a/test/stochastic_small.sh b/test/stochastic_small.sh index 20785a22..136fc7a7 100755 --- a/test/stochastic_small.sh +++ b/test/stochastic_small.sh @@ -351,7 +351,7 @@ else fi syncmodes=("" ,+nosync-safe ,+nosync-utterly) -options=(writemap lifo notls perturb) +options=(writemap lifo nostickythreads perturb) function join { local IFS="$1"; shift; echo "$*"; }