mirror of
https://github.com/isar/libmdbx.git
synced 2025-08-19 19:39:26 +08:00
mdbx: merge branch master
into devel
.
This commit is contained in:
@@ -301,10 +301,10 @@ else()
|
||||
add_extra_test(details_rkl SOURCE extra/details_rkl.c)
|
||||
if(MDBX_BUILD_CXX)
|
||||
if(NOT WIN32 OR NOT MDBX_CXX_STANDARD LESS 17)
|
||||
add_extra_test(cursor_closing)
|
||||
add_extra_test(cursor_closing TIMEOUT 10800)
|
||||
add_extra_test(early_close_dbi)
|
||||
add_extra_test(maindb_ordinal)
|
||||
add_extra_test(dupfix_multiple)
|
||||
add_extra_test(dupfix_multiple TIMEOUT 10800)
|
||||
add_extra_test(doubtless_positioning TIMEOUT 10800)
|
||||
add_extra_test(crunched_delete TIMEOUT 10800)
|
||||
add_extra_test(dbi)
|
||||
|
@@ -313,12 +313,12 @@ bool parse_option(int argc, char *const argv[], int &narg, const char *option, b
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcasecmp(value_cstr, "yes") == 0 || strcasecmp(value_cstr, "1") == 0) {
|
||||
if (strcasecmp(value_cstr, "yes") == 0 || strcasecmp(value_cstr, "1") == 0 || strcasecmp(value_cstr, "on") == 0) {
|
||||
value = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcasecmp(value_cstr, "no") == 0 || strcasecmp(value_cstr, "0") == 0) {
|
||||
if (strcasecmp(value_cstr, "no") == 0 || strcasecmp(value_cstr, "0") == 0 || strcasecmp(value_cstr, "off") == 0) {
|
||||
value = false;
|
||||
return true;
|
||||
}
|
||||
@@ -342,6 +342,7 @@ const struct option_verb mode_bits[] = {{"rdonly", unsigned(MDBX_RDONLY)},
|
||||
{"perturb", unsigned(MDBX_PAGEPERTURB)},
|
||||
{"accede", unsigned(MDBX_ACCEDE)},
|
||||
{"exclusive", unsigned(MDBX_EXCLUSIVE)},
|
||||
{"validation", unsigned(MDBX_VALIDATION)},
|
||||
{nullptr, 0}};
|
||||
|
||||
const struct option_verb table_bits[] = {{"key.reverse", unsigned(MDBX_REVERSEKEY)},
|
||||
|
@@ -5,11 +5,21 @@
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#if MDBX_DEBUG || !defined(NDEBUG) || defined(__APPLE__) || defined(_WIN32)
|
||||
#define NN 1024
|
||||
#if defined(ENABLE_MEMCHECK) || defined(MDBX_CI)
|
||||
#if MDBX_DEBUG || !defined(NDEBUG)
|
||||
#define RELIEF_FACTOR 16
|
||||
#else
|
||||
#define NN 4096
|
||||
#define RELIEF_FACTOR 8
|
||||
#endif
|
||||
#elif MDBX_DEBUG || !defined(NDEBUG) || defined(__APPLE__) || defined(_WIN32)
|
||||
#define RELIEF_FACTOR 4
|
||||
#elif UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul
|
||||
#define RELIEF_FACTOR 2
|
||||
#else
|
||||
#define RELIEF_FACTOR 1
|
||||
#endif
|
||||
|
||||
#define NN (2048 / RELIEF_FACTOR)
|
||||
|
||||
std::string format_va(const char *fmt, va_list ap) {
|
||||
va_list ones;
|
||||
@@ -347,12 +357,7 @@ bool simple(mdbx::env env) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
mdbx_setup_debug_nofmt(MDBX_LOG_NOTICE, MDBX_DBG_ASSERT, logger_nofmt, log_buffer, sizeof(log_buffer));
|
||||
|
||||
int doit() {
|
||||
mdbx::path db_filename = "test-crunched-del";
|
||||
mdbx::env::remove(db_filename);
|
||||
|
||||
@@ -390,3 +395,15 @@ int main(int argc, const char *argv[]) {
|
||||
std::cout << "OK\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
mdbx_setup_debug_nofmt(MDBX_LOG_NOTICE, MDBX_DBG_ASSERT, logger_nofmt, log_buffer, sizeof(log_buffer));
|
||||
try {
|
||||
return doit();
|
||||
} catch (const std::exception &ex) {
|
||||
std::cerr << "Exception: " << ex.what() << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,29 @@
|
||||
#include "mdbx.h++"
|
||||
|
||||
#include <chrono>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#if defined(__cpp_lib_latch) && __cpp_lib_latch >= 201907L
|
||||
#include <latch>
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_MEMCHECK) || defined(MDBX_CI)
|
||||
#if MDBX_DEBUG || !defined(NDEBUG)
|
||||
#define RELIEF_FACTOR 16
|
||||
#else
|
||||
#define RELIEF_FACTOR 8
|
||||
#endif
|
||||
#elif MDBX_DEBUG || !defined(NDEBUG) || defined(__APPLE__) || defined(_WIN32)
|
||||
#define RELIEF_FACTOR 4
|
||||
#elif UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul
|
||||
#define RELIEF_FACTOR 2
|
||||
#else
|
||||
#define RELIEF_FACTOR 1
|
||||
#endif
|
||||
|
||||
#define NN (1000 / RELIEF_FACTOR)
|
||||
|
||||
static void logger_nofmt(MDBX_log_level_t loglevel, const char *function, int line, const char *msg,
|
||||
unsigned length) noexcept {
|
||||
@@ -11,46 +34,353 @@ static void logger_nofmt(MDBX_log_level_t loglevel, const char *function, int li
|
||||
|
||||
static char log_buffer[1024];
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
//--------------------------------------------------------------------------------------------
|
||||
|
||||
mdbx_setup_debug_nofmt(MDBX_LOG_NOTICE, MDBX_DBG_ASSERT, logger_nofmt, log_buffer, sizeof(log_buffer));
|
||||
bool case0(mdbx::env env) {
|
||||
auto txn = env.start_write();
|
||||
auto table = txn.create_map("case0", mdbx::key_mode::usual, mdbx::value_mode::single);
|
||||
auto cursor_1 = txn.open_cursor(table);
|
||||
auto cursor_2 = cursor_1.clone();
|
||||
|
||||
auto nested = env.start_write(txn);
|
||||
auto nested_cursor_1 = nested.open_cursor(table);
|
||||
auto nested_cursor_2 = nested_cursor_1.clone();
|
||||
auto nested_cursor_3 = cursor_1.clone();
|
||||
|
||||
auto deep = env.start_write(nested);
|
||||
auto deep_cursor_1 = deep.open_cursor(table);
|
||||
auto deep_cursor_2 = nested_cursor_1.clone();
|
||||
auto deep_cursor_3 = cursor_1.clone();
|
||||
deep_cursor_1.close();
|
||||
deep.commit();
|
||||
deep_cursor_2.close();
|
||||
|
||||
nested_cursor_1.close();
|
||||
nested.abort();
|
||||
nested_cursor_2.close();
|
||||
|
||||
cursor_1.close();
|
||||
txn.commit();
|
||||
cursor_2.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
|
||||
/* Сценарий:
|
||||
*
|
||||
* 0. Создаём N таблиц, курсор для каждой таблицы и заполняем (1000 ключей, от 1 до 1000 значений в каждом ключе).
|
||||
* 1. Запускаем N-1 фоновых потоков и используем текущий/основной.
|
||||
* 2. В каждом потоке 100500 раз повторяем последовательность действий:
|
||||
* - 100500 раз запускаем читающую транзакцию и выполняем "читающий цикл":
|
||||
* - в читающей транзакции создаем 0..3 курсоров, потом подключаем заранее созданный курсор,
|
||||
* потом еще 0..3 курсоров;
|
||||
* - выполняем по паре поисков через каждый курсор;
|
||||
* - отключаем заранее созданный курсор;
|
||||
* - снова выполняем несколько поисков по каждому курсору;
|
||||
* - псевдослучайно закрываем один из курсоров и один отключаем;
|
||||
* - псевдослучайно выполняем один из путей:
|
||||
* - закрываем все курсоры посредством mdbx_txn_release_all_cursors();
|
||||
* - отсоединяем все курсоры посредством mdbx_txn_release_all_cursors();
|
||||
* - псевдослучайно закрываем один из курсоров и один отключаем;
|
||||
* - ничего не делаем;
|
||||
* - завершаем читающую транзакцию псевдослучайно выбирая между commit и abort;
|
||||
* - закрываем оставшиеся курсоры.
|
||||
* 3. Выполняем "пишущий цикл":
|
||||
* - запускаем пишущую или вложенную транзакцию;
|
||||
* - из оставшихся с предыдущих итераций курсоров половину закрываем,
|
||||
* половину подключаем к транзакции;
|
||||
* - для каждой таблицы с вероятностью 1/2 выполняем "читающий цикл";
|
||||
* - для каждой таблицы с вероятностью 1/2 выполняем "модифицирующий" цикл:
|
||||
* - подключаем курсор, либо создаем при отсутствии подходящих;
|
||||
* - 100 раз выполняем поиск случайных пар ключ/значение;
|
||||
* - при успешном поиске удаляем значение, иначе вставляем;
|
||||
* - с вероятностью 1/2 повторяем "читающий цикл";
|
||||
* - с вероятностью 7/16 запускаем вложенную транзакцию:
|
||||
* - действуем рекурсивно как с пишущей транзакцией;
|
||||
* - в "читающих циклах" немного меняем поведение:
|
||||
* - игнорируем ожидаемые ошибки mdbx_cursor_unbind();
|
||||
* - в 2-3 раза уменьшаем вероятность использования mdbx_txn_release_all_cursors();
|
||||
* - завершаем вложенную транзакцию псевдослучайно выбирая между commit и abort;
|
||||
* - для каждой таблицы с вероятностью 1/2 выполняем "читающий цикл";
|
||||
* - завершаем транзакцию псевдослучайно выбирая между commit и abort;
|
||||
* 4. Ждем завершения фоновых потоков.
|
||||
* 5. Закрываем оставшиеся курсоры и закрываем БД. */
|
||||
|
||||
thread_local size_t salt;
|
||||
|
||||
static size_t prng() {
|
||||
salt = salt * 134775813 + 1;
|
||||
return salt ^ ((salt >> 11) * 1822226723);
|
||||
}
|
||||
|
||||
static inline bool flipcoin() { return prng() & 1; }
|
||||
|
||||
static inline size_t prng(size_t range) { return prng() % range; }
|
||||
|
||||
void case1_shuffle_pool(std::vector<MDBX_cursor *> &pool) {
|
||||
for (size_t n = 1; n < pool.size(); ++n) {
|
||||
const auto i = prng(n);
|
||||
if (i != n)
|
||||
std::swap(pool[n], pool[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void case1_read_pool(std::vector<MDBX_cursor *> &pool) {
|
||||
for (auto c : pool)
|
||||
if (flipcoin())
|
||||
mdbx::cursor(c).find_multivalue(mdbx::slice::wrap(prng(NN)), mdbx::slice::wrap(prng(NN)), false);
|
||||
for (auto c : pool)
|
||||
if (flipcoin())
|
||||
mdbx::cursor(c).find_multivalue(mdbx::slice::wrap(prng(NN)), mdbx::slice::wrap(prng(NN)), false);
|
||||
}
|
||||
|
||||
MDBX_cursor *case1_try_unbind(MDBX_cursor *cursor) {
|
||||
if (cursor) {
|
||||
auto err = mdbx::error(static_cast<MDBX_error_t>(mdbx_cursor_unbind(cursor)));
|
||||
if (err.code() != MDBX_EINVAL)
|
||||
err.success_or_throw();
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
MDBX_cursor *case1_pool_remove(std::vector<MDBX_cursor *> &pool) {
|
||||
switch (pool.size()) {
|
||||
case 0:
|
||||
return nullptr;
|
||||
case 1:
|
||||
if (flipcoin()) {
|
||||
const auto c = pool[0];
|
||||
pool.pop_back();
|
||||
return c;
|
||||
}
|
||||
return nullptr;
|
||||
default:
|
||||
const auto i = prng(pool.size());
|
||||
const auto c = pool[i];
|
||||
pool.erase(pool.begin() + i);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
mdbx::map_handle case1_cycle_dbi(std::deque<mdbx::map_handle> &dbi) {
|
||||
const auto h = dbi.front();
|
||||
dbi.pop_front();
|
||||
dbi.push_back(h);
|
||||
return h;
|
||||
}
|
||||
|
||||
void case1_read_cycle(mdbx::txn txn, std::deque<mdbx::map_handle> &dbi, std::vector<MDBX_cursor *> &pool,
|
||||
mdbx::cursor pre, bool nested = false) {
|
||||
for (auto c : pool)
|
||||
mdbx::cursor(c).bind(txn, case1_cycle_dbi(dbi));
|
||||
pre.bind(txn, case1_cycle_dbi(dbi));
|
||||
|
||||
for (auto n = prng(3 + dbi.size()); n > 0; --n) {
|
||||
auto c = txn.open_cursor(dbi[prng(dbi.size())]);
|
||||
pool.push_back(c.withdraw_handle());
|
||||
}
|
||||
case1_shuffle_pool(pool);
|
||||
case1_read_pool(pool);
|
||||
|
||||
pool.push_back(pre);
|
||||
case1_read_pool(pool);
|
||||
pool.pop_back();
|
||||
|
||||
for (auto n = prng(3 + dbi.size()); n > 0; --n) {
|
||||
auto c = txn.open_cursor(dbi[prng(dbi.size())]);
|
||||
pool.push_back(c.withdraw_handle());
|
||||
}
|
||||
pool.push_back(pre);
|
||||
case1_read_pool(pool);
|
||||
pool.pop_back();
|
||||
|
||||
case1_try_unbind(pre);
|
||||
case1_shuffle_pool(pool);
|
||||
case1_read_pool(pool);
|
||||
|
||||
if (flipcoin()) {
|
||||
mdbx_cursor_close(case1_pool_remove(pool));
|
||||
auto u = case1_try_unbind(case1_pool_remove(pool));
|
||||
case1_read_pool(pool);
|
||||
if (u)
|
||||
pool.push_back(u);
|
||||
} else {
|
||||
auto u = case1_try_unbind(case1_pool_remove(pool));
|
||||
mdbx_cursor_close(case1_pool_remove(pool));
|
||||
case1_read_pool(pool);
|
||||
if (u)
|
||||
pool.push_back(u);
|
||||
}
|
||||
|
||||
switch (prng(nested ? 7 : 3)) {
|
||||
case 0:
|
||||
for (auto i = pool.begin(); i != pool.end();)
|
||||
if (mdbx_cursor_txn(*i))
|
||||
i = pool.erase(i);
|
||||
else
|
||||
++i;
|
||||
txn.close_all_cursors();
|
||||
break;
|
||||
case 1:
|
||||
txn.unbind_all_cursors();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void case1_write_cycle(mdbx::txn_managed txn, std::deque<mdbx::map_handle> &dbi, std::vector<MDBX_cursor *> &pool,
|
||||
mdbx::cursor pre, bool nested = false) {
|
||||
if (flipcoin())
|
||||
case1_cycle_dbi(dbi);
|
||||
if (flipcoin())
|
||||
case1_shuffle_pool(pool);
|
||||
|
||||
for (auto n = prng(dbi.size() + 1); n > 1; n -= 2) {
|
||||
if (!nested)
|
||||
pre.unbind();
|
||||
if (!pre.txn())
|
||||
pre.bind(txn, dbi[prng(dbi.size())]);
|
||||
for (auto i = 0; i < NN; ++i) {
|
||||
auto k = mdbx::default_buffer::wrap(prng(NN));
|
||||
auto v = mdbx::default_buffer::wrap(prng(NN));
|
||||
if (pre.find_multivalue(k, v, false))
|
||||
pre.erase();
|
||||
else
|
||||
pre.upsert(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
if (prng(16) > 8)
|
||||
case1_write_cycle(txn.start_nested(), dbi, pool, pre, true);
|
||||
|
||||
if (flipcoin())
|
||||
txn.commit();
|
||||
else
|
||||
txn.abort();
|
||||
}
|
||||
|
||||
bool case1_thread(mdbx::env env, std::deque<mdbx::map_handle> dbi, mdbx::cursor pre) {
|
||||
salt = size_t(std::chrono::high_resolution_clock::now().time_since_epoch().count());
|
||||
std::vector<MDBX_cursor *> pool;
|
||||
for (auto loop = 0; loop < 333 / RELIEF_FACTOR; ++loop) {
|
||||
for (auto read = 0; read < 333 / RELIEF_FACTOR; ++read) {
|
||||
auto txn = env.start_read();
|
||||
case1_read_cycle(txn, dbi, pool, pre);
|
||||
if (flipcoin())
|
||||
txn.commit();
|
||||
else
|
||||
txn.abort();
|
||||
}
|
||||
|
||||
case1_write_cycle(env.start_write(), dbi, pool, pre);
|
||||
|
||||
for (auto c : pool)
|
||||
mdbx_cursor_close(c);
|
||||
pool.clear();
|
||||
}
|
||||
|
||||
pre.unbind();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool case1(mdbx::env env) {
|
||||
bool ok = true;
|
||||
std::deque<mdbx::map_handle> dbi;
|
||||
std::vector<mdbx::cursor_managed> cursors;
|
||||
#if defined(__cpp_lib_latch) && __cpp_lib_latch >= 201907L
|
||||
static const auto N = 10;
|
||||
#else
|
||||
static const auto N = 3;
|
||||
#endif
|
||||
for (auto t = 0; t < N; ++t) {
|
||||
auto txn = env.start_write();
|
||||
auto table = txn.create_map(std::to_string(t), mdbx::key_mode::ordinal, mdbx::value_mode::multi_samelength);
|
||||
auto cursor = txn.open_cursor(table);
|
||||
for (size_t i = 0; i < NN * 11; ++i)
|
||||
cursor.upsert(mdbx::default_buffer::wrap(prng(NN)), mdbx::default_buffer::wrap(prng(NN)));
|
||||
txn.commit();
|
||||
|
||||
cursors.push_back(std::move(cursor));
|
||||
dbi.push_back(table);
|
||||
}
|
||||
|
||||
#if defined(__cpp_lib_latch) && __cpp_lib_latch >= 201907L
|
||||
std::latch s(1);
|
||||
std::vector<std::thread> threads;
|
||||
for (auto t = 1; t < N; ++t) {
|
||||
case1_cycle_dbi(dbi);
|
||||
threads.push_back(std::thread([&, t]() {
|
||||
s.wait();
|
||||
if (!case1_thread(env, dbi, cursors[t]))
|
||||
ok = false;
|
||||
}));
|
||||
}
|
||||
case1_cycle_dbi(dbi);
|
||||
s.count_down();
|
||||
#endif
|
||||
|
||||
if (!case1_thread(env, dbi, cursors[0]))
|
||||
ok = false;
|
||||
|
||||
#if defined(__cpp_lib_latch) && __cpp_lib_latch >= 201907L
|
||||
for (auto &t : threads)
|
||||
t.join();
|
||||
#endif
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
|
||||
bool case2(mdbx::env env) {
|
||||
bool ok = true;
|
||||
|
||||
auto txn = env.start_write();
|
||||
auto dbi = txn.create_map("case2", mdbx::key_mode::usual, mdbx::value_mode::single);
|
||||
txn.commit_embark_read();
|
||||
auto cursor1 = txn.open_cursor(dbi);
|
||||
auto cursor2 = txn.open_cursor(0);
|
||||
cursor1.move(mdbx::cursor::next, false);
|
||||
cursor2.move(mdbx::cursor::next, false);
|
||||
txn.commit_embark_read();
|
||||
cursor2.bind(txn, dbi);
|
||||
cursor1.bind(txn, 0);
|
||||
cursor1.move(mdbx::cursor::last, false);
|
||||
cursor2.move(mdbx::cursor::last, false);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
|
||||
int doit() {
|
||||
mdbx::path db_filename = "test-cursor-closing";
|
||||
mdbx::env::remove(db_filename);
|
||||
|
||||
mdbx::env_managed env(db_filename, mdbx::env_managed::create_parameters(),
|
||||
mdbx::env::operate_parameters(42, 0, mdbx::env::nested_transactions));
|
||||
|
||||
{
|
||||
auto txn = env.start_write();
|
||||
auto table = txn.create_map("dummy", mdbx::key_mode::usual, mdbx::value_mode::single);
|
||||
auto cursor_1 = txn.open_cursor(table);
|
||||
auto cursor_2 = cursor_1.clone();
|
||||
bool ok = case0(env);
|
||||
ok = case1(env) && ok;
|
||||
ok = case2(env) && ok;
|
||||
|
||||
auto nested = env.start_write(txn);
|
||||
auto nested_cursor_1 = nested.open_cursor(table);
|
||||
auto nested_cursor_2 = nested_cursor_1.clone();
|
||||
auto nested_cursor_3 = cursor_1.clone();
|
||||
|
||||
auto deep = env.start_write(nested);
|
||||
auto deep_cursor_1 = deep.open_cursor(table);
|
||||
auto deep_cursor_2 = nested_cursor_1.clone();
|
||||
auto deep_cursor_3 = cursor_1.clone();
|
||||
deep_cursor_1.close();
|
||||
deep.commit();
|
||||
deep_cursor_2.close();
|
||||
|
||||
nested_cursor_1.close();
|
||||
nested.abort();
|
||||
nested_cursor_2.close();
|
||||
|
||||
cursor_1.close();
|
||||
txn.commit();
|
||||
cursor_2.close();
|
||||
if (ok) {
|
||||
std::cout << "OK\n";
|
||||
return EXIT_SUCCESS;
|
||||
} else {
|
||||
std::cout << "FAIL!\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
mdbx_setup_debug_nofmt(MDBX_LOG_NOTICE, MDBX_DBG_ASSERT, logger_nofmt, log_buffer, sizeof(log_buffer));
|
||||
try {
|
||||
return doit();
|
||||
} catch (const std::exception &ex) {
|
||||
std::cerr << "Exception: " << ex.what() << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::cout << "OK\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@@ -11,28 +11,70 @@ static void logger_nofmt(MDBX_log_level_t loglevel, const char *function, int li
|
||||
fprintf(stdout, "%s:%u %s", function, line, msg);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
mdbx_setup_debug_nofmt(MDBX_LOG_NOTICE, MDBX_DBG_ASSERT, logger_nofmt, log_buffer, sizeof(log_buffer));
|
||||
|
||||
int doit() {
|
||||
mdbx::path db_filename = "test-dbi";
|
||||
mdbx::env::remove(db_filename);
|
||||
|
||||
mdbx::env::operate_parameters operateParameters(100, 10);
|
||||
mdbx::env::operate_parameters operateParameters(100, 10, mdbx::env::nested_transactions);
|
||||
mdbx::env_managed::create_parameters createParameters;
|
||||
{
|
||||
mdbx::env_managed env2(db_filename, createParameters, operateParameters);
|
||||
mdbx::txn_managed txn2 = env2.start_write(false);
|
||||
/* mdbx::map_handle testHandle2 = */ txn2.create_map("fap1", mdbx::key_mode::reverse, mdbx::value_mode::single);
|
||||
txn2.commit();
|
||||
mdbx::env_managed env(db_filename, createParameters, operateParameters);
|
||||
mdbx::txn_managed txn = env.start_write();
|
||||
/* mdbx::map_handle dbi = */ txn.create_map("fap1", mdbx::key_mode::reverse, mdbx::value_mode::single);
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
mdbx::env_managed env(db_filename, createParameters, operateParameters);
|
||||
mdbx::txn_managed txn = env.start_write(false);
|
||||
/* mdbx::map_handle testHandle = */ txn.create_map("fap1", mdbx::key_mode::usual, mdbx::value_mode::single);
|
||||
txn.commit();
|
||||
{
|
||||
// проверяем доступность в родительской транзакции хендла открытого в дочерней транзакции после коммита
|
||||
mdbx::txn_managed txn = env.start_write();
|
||||
mdbx::txn_managed nested = txn.start_nested();
|
||||
mdbx::map_handle dbi = nested.open_map_accede("fap1");
|
||||
nested.commit();
|
||||
MDBX_MAYBE_UNUSED auto stat = txn.get_map_stat(dbi);
|
||||
txn.commit();
|
||||
env.close_map(dbi);
|
||||
}
|
||||
|
||||
{
|
||||
// проверяем НЕ доступность в родительской транзакции хендла открытого в дочерней транзакции после прерывания
|
||||
mdbx::txn_managed txn = env.start_write();
|
||||
mdbx::txn_managed nested = txn.start_nested();
|
||||
mdbx::map_handle dbi = nested.open_map_accede("fap1");
|
||||
nested.abort();
|
||||
MDBX_stat stat;
|
||||
int err = mdbx_dbi_stat(txn, dbi, &stat, sizeof(stat));
|
||||
if (err != MDBX_BAD_DBI) {
|
||||
std::cerr << "unexpected result err-code " << err;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
{
|
||||
// снова проверяем что таблица открывается и хендл доступень в родительской транзакции после коммита открывшей его
|
||||
// дочерней
|
||||
mdbx::txn_managed txn = env.start_write();
|
||||
mdbx::txn_managed nested = txn.start_nested();
|
||||
mdbx::map_handle dbi = nested.open_map_accede("fap1");
|
||||
nested.commit();
|
||||
MDBX_MAYBE_UNUSED auto stat = txn.get_map_stat(dbi);
|
||||
txn.commit();
|
||||
env.close_map(dbi);
|
||||
}
|
||||
|
||||
std::cout << "OK\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
mdbx_setup_debug_nofmt(MDBX_LOG_NOTICE, MDBX_DBG_ASSERT, logger_nofmt, log_buffer, sizeof(log_buffer));
|
||||
try {
|
||||
return doit();
|
||||
} catch (const std::exception &ex) {
|
||||
std::cerr << "Exception: " << ex.what() << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
@@ -211,10 +211,7 @@ static bool test(mdbx::txn txn, mdbx::map_handle dbi) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
int doit() {
|
||||
mdbx::path db_filename = "test-posi";
|
||||
mdbx::env_managed::remove(db_filename);
|
||||
mdbx::env_managed env(db_filename, mdbx::env_managed::create_parameters(), mdbx::env::operate_parameters(3));
|
||||
@@ -243,3 +240,14 @@ int main(int argc, const char *argv[]) {
|
||||
std::cout << "OK\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
try {
|
||||
return doit();
|
||||
} catch (const std::exception &ex) {
|
||||
std::cerr << "Exception: " << ex.what() << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
@@ -25,6 +25,9 @@ int main() {
|
||||
MDBX_val key, data;
|
||||
MDBX_txn *txn = NULL;
|
||||
|
||||
const char *db_filename = "./test-dupfix-addodd";
|
||||
mdbx_env_delete(db_filename, MDBX_ENV_JUST_DELETE);
|
||||
|
||||
rc = mdbx_env_create(&env);
|
||||
if (rc != MDBX_SUCCESS) {
|
||||
fprintf(stderr, "mdbx_env_create: (%d) %s\n", rc, mdbx_strerror(rc));
|
||||
@@ -37,7 +40,7 @@ int main() {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
rc = mdbx_env_open(env, "./example-db", MDBX_NOSUBDIR | MDBX_LIFORECLAIM, 0664);
|
||||
rc = mdbx_env_open(env, db_filename, MDBX_NOSUBDIR | MDBX_LIFORECLAIM, 0664);
|
||||
if (rc != MDBX_SUCCESS) {
|
||||
fprintf(stderr, "mdbx_env_open: (%d) %s\n", rc, mdbx_strerror(rc));
|
||||
exit(EXIT_FAILURE);
|
||||
|
@@ -2,17 +2,28 @@
|
||||
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "mdbx.h++"
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
int doit() {
|
||||
mdbx::path db_filename = "test-dupfix-multiple";
|
||||
mdbx::env_managed::remove(db_filename);
|
||||
mdbx::env_managed env(db_filename, mdbx::env_managed::create_parameters(), mdbx::env::operate_parameters());
|
||||
#if defined(ENABLE_MEMCHECK) || defined(MDBX_CI)
|
||||
#if MDBX_DEBUG || !defined(NDEBUG)
|
||||
#define RELIEF_FACTOR 16
|
||||
#else
|
||||
#define RELIEF_FACTOR 8
|
||||
#endif
|
||||
#elif MDBX_DEBUG || !defined(NDEBUG) || defined(__APPLE__) || defined(_WIN32)
|
||||
#define RELIEF_FACTOR 4
|
||||
#elif UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul
|
||||
#define RELIEF_FACTOR 2
|
||||
#else
|
||||
#define RELIEF_FACTOR 1
|
||||
#endif
|
||||
|
||||
using buffer = mdbx::buffer<mdbx::default_allocator, mdbx::default_capacity_policy>;
|
||||
using buffer = mdbx::default_buffer;
|
||||
|
||||
bool case1_ordering(mdbx::env env) {
|
||||
auto txn = env.start_write();
|
||||
auto map = txn.create_map(nullptr, mdbx::key_mode::ordinal, mdbx::value_mode::multi_ordinal);
|
||||
auto map = txn.create_map("case1", mdbx::key_mode::ordinal, mdbx::value_mode::multi_ordinal);
|
||||
|
||||
txn.insert(map, buffer::key_from_u64(21), buffer::key_from_u64(18));
|
||||
txn.insert(map, buffer::key_from_u64(7), buffer::key_from_u64(19));
|
||||
@@ -30,10 +41,9 @@ int doit() {
|
||||
cursor.to_next().value.as_uint64() != 17 || cursor.to_next().value.as_uint64() != 16 ||
|
||||
cursor.to_next().value.as_uint64() != 15 || cursor.to_next().value.as_uint64() != 14 ||
|
||||
cursor.to_next().value.as_uint64() != 13 || cursor.to_next().value.as_uint64() != 12 ||
|
||||
cursor.to_next(false).done || !cursor.eof()) {
|
||||
std::cerr << "Fail\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
cursor.to_next(false).done || !cursor.eof())
|
||||
return false;
|
||||
|
||||
txn.abort();
|
||||
|
||||
const uint64_t array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 42, 17, 99, 0, 33, 333};
|
||||
@@ -87,10 +97,9 @@ int doit() {
|
||||
/* key = 24 */ cursor.to_next().value.as_uint64() != 15 ||
|
||||
/* key = 25 */ cursor.to_next().value.as_uint64() != 14 ||
|
||||
/* key = 26 */ cursor.to_next().value.as_uint64() != 13 ||
|
||||
/* key = 27 */ cursor.to_next().value.as_uint64() != 12 || cursor.to_next(false).done || !cursor.eof()) {
|
||||
std::cerr << "Fail\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* key = 27 */ cursor.to_next().value.as_uint64() != 12 || cursor.to_next(false).done || !cursor.eof())
|
||||
return false;
|
||||
|
||||
txn.abort();
|
||||
|
||||
txn = env.start_write();
|
||||
@@ -163,40 +172,24 @@ int doit() {
|
||||
cursor.to_next().value.as_uint64() != 0 || cursor.to_next().value.as_uint64() != 33 ||
|
||||
cursor.to_next().value.as_uint64() != 333 ||
|
||||
|
||||
cursor.to_next(false).done || !cursor.eof()) {
|
||||
std::cerr << "Fail\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
cursor.to_next(false).done || !cursor.eof())
|
||||
return false;
|
||||
|
||||
txn.abort();
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// let dir = tempdir().unwrap();
|
||||
// let db = Database::open(&dir).unwrap();
|
||||
|
||||
// let txn = db.begin_rw_txn().unwrap();
|
||||
// let table = txn
|
||||
// .create_table(None, TableFlags::DUP_SORT | TableFlags::DUP_FIXED)
|
||||
// .unwrap();
|
||||
// for (k, v) in [
|
||||
// (b"key1", b"val1"),
|
||||
// (b"key1", b"val2"),
|
||||
// (b"key1", b"val3"),
|
||||
// (b"key2", b"val1"),
|
||||
// (b"key2", b"val2"),
|
||||
// (b"key2", b"val3"),
|
||||
// ] {
|
||||
// txn.put(&table, k, v, WriteFlags::empty()).unwrap();
|
||||
// }
|
||||
|
||||
// let mut cursor = txn.cursor(&table).unwrap();
|
||||
// assert_eq!(cursor.first().unwrap(), Some((*b"key1", *b"val1")));
|
||||
// assert_eq!(cursor.get_multiple().unwrap(), Some(*b"val1val2val3"));
|
||||
// assert_eq!(cursor.next_multiple::<(), ()>().unwrap(), None);
|
||||
|
||||
txn = env.start_write();
|
||||
txn.clear_map(map);
|
||||
map = txn.create_map(nullptr, mdbx::key_mode::usual, mdbx::value_mode::multi_samelength);
|
||||
txn.commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
|
||||
bool case2_batch_read(mdbx::env env) {
|
||||
|
||||
auto txn = env.start_write();
|
||||
auto map = txn.create_map("case2", mdbx::key_mode::usual, mdbx::value_mode::multi_samelength);
|
||||
txn.upsert(map, mdbx::slice("key1"), mdbx::slice("val1"));
|
||||
txn.upsert(map, mdbx::pair("key1", "val2"));
|
||||
txn.upsert(map, mdbx::pair("key1", "val3"));
|
||||
@@ -205,36 +198,110 @@ int doit() {
|
||||
txn.upsert(map, mdbx::pair("key2", "val3"));
|
||||
|
||||
// cursor.close();
|
||||
cursor = txn.open_cursor(map);
|
||||
auto cursor = txn.open_cursor(map);
|
||||
const auto t1 = cursor.to_first();
|
||||
if (!t1 || t1.key != "key1" || t1.value != "val1") {
|
||||
std::cerr << "Fail-t1\n";
|
||||
return EXIT_FAILURE;
|
||||
return false;
|
||||
}
|
||||
const auto t2 = cursor.get_multiple_samelength();
|
||||
if (!t2 || t2.key != "key1" || t2.value != "val1val2val3") {
|
||||
std::cerr << "Fail-t2\n";
|
||||
return EXIT_FAILURE;
|
||||
return false;
|
||||
}
|
||||
// const auto t3 = cursor.get_multiple_samelength("key2");
|
||||
// if (!t3 || t3.key != "key2" || t3.value != "val1val2val3") {
|
||||
// std::cerr << "Fail-t3\n";
|
||||
// return EXIT_FAILURE;
|
||||
// }
|
||||
const auto t4 = cursor.next_multiple_samelength();
|
||||
if (t4) {
|
||||
const auto t3 = cursor.next_multiple_samelength();
|
||||
if (t3) {
|
||||
std::cerr << "Fail-t3\n";
|
||||
return false;
|
||||
}
|
||||
const auto t4 = cursor.seek_multiple_samelength("key2");
|
||||
if (!t4 || t4.key != "key2" || t4.value != "val1val2val3") {
|
||||
std::cerr << "Fail-t4\n";
|
||||
return EXIT_FAILURE;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "OK\n";
|
||||
return EXIT_SUCCESS;
|
||||
txn.clear_map(map);
|
||||
txn.commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
|
||||
size_t salt;
|
||||
|
||||
static size_t prng() {
|
||||
salt = salt * 134775813 + 1;
|
||||
return salt ^ ((salt >> 11) * 1822226723);
|
||||
}
|
||||
|
||||
static inline size_t prng(size_t range) { return prng() % range; }
|
||||
|
||||
static mdbx::default_buffer_pair prng_kv(size_t n, size_t space) {
|
||||
space = (space + !space) * 1024 * 32 / RELIEF_FACTOR;
|
||||
const size_t w = (n ^ 1455614549) * 1664525 + 1013904223;
|
||||
const size_t k = (prng(42 + w % space) ^ 1725278851) * 433750991;
|
||||
const size_t v = prng();
|
||||
return mdbx::default_buffer_pair(mdbx::slice::wrap(k), mdbx::slice::wrap(v));
|
||||
}
|
||||
|
||||
bool case3_put_a_lot(mdbx::env env) {
|
||||
salt = size_t(std::chrono::high_resolution_clock::now().time_since_epoch().count());
|
||||
auto txn = env.start_write();
|
||||
auto map = txn.create_map("case3", mdbx::key_mode::ordinal, mdbx::value_mode::multi_ordinal);
|
||||
for (size_t n = 0; n < 5555555 / RELIEF_FACTOR; ++n)
|
||||
txn.upsert(map, prng_kv(n, 1));
|
||||
txn.commit();
|
||||
|
||||
for (size_t t = 0; t < 555 / RELIEF_FACTOR; ++t) {
|
||||
txn = env.start_write();
|
||||
auto cursor = txn.open_cursor(map);
|
||||
for (size_t n = 0; n < 111; ++n) {
|
||||
auto v = std::vector<size_t>();
|
||||
const auto r = 1 + prng(3);
|
||||
if (r & 1) {
|
||||
const auto k = prng_kv(n + t, 2).key;
|
||||
for (size_t i = prng(42 + prng(111) * prng(111 / RELIEF_FACTOR)); i > 0; --i)
|
||||
v.push_back(prng());
|
||||
txn.put_multiple_samelength(map, k, v, mdbx::upsert);
|
||||
}
|
||||
if (r & 2) {
|
||||
const auto k = prng_kv(n + t, 2).key;
|
||||
if (cursor.seek(k)) {
|
||||
v.clear();
|
||||
for (size_t i = prng(42 + prng(111) * prng(111 / RELIEF_FACTOR)); i > 0; --i)
|
||||
v.push_back(prng());
|
||||
cursor.put_multiple_samelength(k, v, mdbx::upsert);
|
||||
}
|
||||
}
|
||||
}
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int doit() {
|
||||
mdbx::path db_filename = "test-dupfix-multiple";
|
||||
mdbx::env_managed::remove(db_filename);
|
||||
mdbx::env_managed env(db_filename, mdbx::env_managed::create_parameters(), mdbx::env::operate_parameters(3));
|
||||
|
||||
bool ok = case1_ordering(env);
|
||||
ok = case2_batch_read(env) && ok;
|
||||
ok = case3_put_a_lot(env) && ok;
|
||||
|
||||
if (ok) {
|
||||
std::cout << "OK\n";
|
||||
return EXIT_SUCCESS;
|
||||
} else {
|
||||
std::cerr << "Fail\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
try {
|
||||
return doit();
|
||||
} catch (const std::exception &ex) {
|
||||
|
@@ -1,13 +1,11 @@
|
||||
#include "mdbx.h++"
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
static const char *const testkey = "testkey";
|
||||
static uint64_t testval = 11;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
int doit() {
|
||||
mdbx::path db_filename = "test-early_close_dbi";
|
||||
mdbx::env_managed::remove(db_filename);
|
||||
|
||||
@@ -67,13 +65,13 @@ int main(int argc, char *argv[]) {
|
||||
assert(err == MDBX_SUCCESS);
|
||||
err = mdbx_get(transaction, textindex, &mdbxkey, &mdbxval);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
assert(testval == *reinterpret_cast<uint64_t *>(mdbxval.iov_base));
|
||||
assert(testval == mdbx::slice(mdbxval).as_uint64());
|
||||
|
||||
err = mdbx_put(transaction, textindex, &mdbxkey, &mdbxput, MDBX_NOOVERWRITE);
|
||||
assert(err == MDBX_KEYEXIST);
|
||||
err = mdbx_get(transaction, textindex, &mdbxkey, &mdbxval);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
assert(testval == *reinterpret_cast<uint64_t *>(mdbxval.iov_base));
|
||||
assert(testval == mdbx::slice(mdbxval).as_uint64());
|
||||
|
||||
err = mdbx_dbi_flags_ex(transaction, textindex, &dbi_flags, &dbi_state);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
@@ -89,7 +87,7 @@ int main(int argc, char *argv[]) {
|
||||
assert(err == MDBX_SUCCESS);
|
||||
err = mdbx_get(transaction, textindex, &mdbxkey, &mdbxval);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
assert(testval == *reinterpret_cast<uint64_t *>(mdbxval.iov_base));
|
||||
assert(testval == mdbx::slice(mdbxval).as_uint64());
|
||||
|
||||
err = mdbx_dbi_close(environment, textindex);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
@@ -126,3 +124,14 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
try {
|
||||
return doit();
|
||||
} catch (const std::exception &ex) {
|
||||
std::cerr << "Exception: " << ex.what() << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
@@ -75,10 +75,7 @@ static bool basic() {
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
int doit() {
|
||||
auto ok = basic();
|
||||
for (size_t n = 0; n < 1000; ++n) {
|
||||
for (size_t length = 0; ok && length < 111; ++length) {
|
||||
@@ -108,3 +105,14 @@ int main(int argc, const char *argv[]) {
|
||||
std::cout << "OK\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
try {
|
||||
return doit();
|
||||
} catch (const std::exception &ex) {
|
||||
std::cerr << "Exception: " << ex.what() << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
@@ -4,11 +4,8 @@
|
||||
#include "mdbx.h++"
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
mdbx::path db_filename = "test-dupfix-multiple";
|
||||
static int doit() {
|
||||
mdbx::path db_filename = "test-maindb-ordinal";
|
||||
mdbx::env_managed::remove(db_filename);
|
||||
mdbx::env_managed env(db_filename, mdbx::env_managed::create_parameters(), mdbx::env::operate_parameters());
|
||||
|
||||
@@ -51,3 +48,14 @@ int main(int argc, const char *argv[]) {
|
||||
return EXIT_SUCCESS;
|
||||
#endif /* __cpp_lib_string_view >= 201606L */
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
try {
|
||||
return doit();
|
||||
} catch (const std::exception &ex) {
|
||||
std::cerr << "Exception: " << ex.what() << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
@@ -25,12 +25,7 @@ static void logger_nofmt(MDBX_log_level_t loglevel, const char *function, int li
|
||||
fprintf(stdout, "%s:%u %s", function, line, msg);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
mdbx_setup_debug_nofmt(MDBX_LOG_VERBOSE, MDBX_DBG_ASSERT, logger_nofmt, log_buffer, sizeof(log_buffer));
|
||||
|
||||
int doit() {
|
||||
mdbx::path path = "test-open";
|
||||
mdbx::env::remove(path);
|
||||
|
||||
@@ -44,6 +39,13 @@ int main(int argc, const char *argv[]) {
|
||||
txn2.commit();
|
||||
}
|
||||
|
||||
{
|
||||
mdbx::env::operate_parameters operateParameters(100, 10);
|
||||
mdbx::env_managed::create_parameters createParameters;
|
||||
createParameters.geometry.make_dynamic(21 * mdbx::env::geometry::MiB, mdbx::env::geometry::GiB / 2);
|
||||
mdbx::env_managed env(path, createParameters, operateParameters);
|
||||
}
|
||||
|
||||
mdbx::env::operate_parameters operateParameters(100, 10);
|
||||
mdbx::env_managed::create_parameters createParameters;
|
||||
createParameters.geometry.make_dynamic(21 * mdbx::env::geometry::MiB, 84 * mdbx::env::geometry::MiB);
|
||||
@@ -79,4 +81,16 @@ int main(int argc, const char *argv[]) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
mdbx_setup_debug_nofmt(MDBX_LOG_VERBOSE, MDBX_DBG_ASSERT, logger_nofmt, log_buffer, sizeof(log_buffer));
|
||||
try {
|
||||
return doit();
|
||||
} catch (const std::exception &ex) {
|
||||
std::cerr << "Exception: " << ex.what() << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __cpp_lib_latch */
|
||||
|
@@ -1,7 +1,22 @@
|
||||
#include "mdbx.h++"
|
||||
#include MDBX_CONFIG_H
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#if defined(ENABLE_MEMCHECK) || defined(MDBX_CI)
|
||||
#if MDBX_DEBUG || !defined(NDEBUG)
|
||||
#define RELIEF_FACTOR 16
|
||||
#else
|
||||
#define RELIEF_FACTOR 8
|
||||
#endif
|
||||
#elif MDBX_DEBUG || !defined(NDEBUG) || defined(__APPLE__) || defined(_WIN32)
|
||||
#define RELIEF_FACTOR 4
|
||||
#elif UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul
|
||||
#define RELIEF_FACTOR 2
|
||||
#else
|
||||
#define RELIEF_FACTOR 1
|
||||
#endif
|
||||
|
||||
#if !defined(__cpp_lib_latch) && __cpp_lib_latch < 201907L
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
@@ -25,268 +40,311 @@ static void logger_nofmt(MDBX_log_level_t loglevel, const char *function, int li
|
||||
fprintf(stdout, "%s:%u %s", function, line, msg);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
bool ok = true;
|
||||
int err;
|
||||
bool case0(const mdbx::path &path) {
|
||||
mdbx::env_managed::create_parameters createParameters;
|
||||
createParameters.geometry.make_dynamic(21 * mdbx::env::geometry::MiB, 84 * mdbx::env::geometry::MiB);
|
||||
|
||||
mdbx_setup_debug_nofmt(MDBX_LOG_VERBOSE, MDBX_DBG_ASSERT, logger_nofmt, log_buffer, sizeof(log_buffer));
|
||||
mdbx::env::operate_parameters operateParameters(100, 10);
|
||||
operateParameters.options.no_sticky_threads = false;
|
||||
mdbx::env_managed env(path, createParameters, operateParameters);
|
||||
auto txn = env.start_write(false);
|
||||
/* mdbx::map_handle testHandle = */ txn.create_map("xyz", mdbx::key_mode::usual, mdbx::value_mode::single);
|
||||
txn.commit();
|
||||
|
||||
//-------------------------------------
|
||||
txn = env.start_write();
|
||||
MDBX_txn *c_txn = txn;
|
||||
int err = mdbx_txn_reset(txn);
|
||||
assert(err == MDBX_EINVAL);
|
||||
bool ok = err == MDBX_EINVAL;
|
||||
|
||||
err = mdbx_txn_break(txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
|
||||
err = mdbx_txn_commit(txn);
|
||||
assert(err == MDBX_RESULT_TRUE);
|
||||
ok = ok && err == MDBX_RESULT_TRUE;
|
||||
|
||||
//-------------------------------------
|
||||
err = mdbx_txn_begin(env, nullptr, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
assert(c_txn == (const MDBX_txn *)txn);
|
||||
|
||||
err = mdbx_txn_break(txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
|
||||
err = mdbx_txn_reset(txn);
|
||||
assert(err == MDBX_EINVAL);
|
||||
ok = ok && err == MDBX_EINVAL;
|
||||
|
||||
err = mdbx_txn_commit(txn);
|
||||
assert(err == MDBX_RESULT_TRUE);
|
||||
ok = ok && err == MDBX_RESULT_TRUE;
|
||||
|
||||
err = mdbx_txn_abort(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
//-------------------------------------
|
||||
err = mdbx_txn_begin(env, nullptr, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
assert(c_txn == (const MDBX_txn *)txn);
|
||||
txn.commit();
|
||||
|
||||
err = mdbx_txn_reset(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
err = mdbx_txn_break(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
err = mdbx_txn_abort(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
|
||||
//=====================================
|
||||
|
||||
txn = env.start_read();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
txn.make_broken();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
txn.reset_reading();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
txn.abort();
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
txn = env.start_read();
|
||||
txn.reset_reading();
|
||||
txn.make_broken();
|
||||
txn.abort();
|
||||
|
||||
//=====================================
|
||||
|
||||
std::latch s(1);
|
||||
txn = env.start_read();
|
||||
c_txn = txn;
|
||||
|
||||
std::thread t([&]() {
|
||||
s.wait();
|
||||
#if MDBX_TXN_CHECKOWNER
|
||||
err = mdbx_txn_reset(c_txn);
|
||||
assert(err == MDBX_THREAD_MISMATCH);
|
||||
ok = ok && err == MDBX_THREAD_MISMATCH;
|
||||
err = mdbx_txn_break(c_txn);
|
||||
assert(err == MDBX_THREAD_MISMATCH);
|
||||
ok = ok && err == MDBX_THREAD_MISMATCH;
|
||||
err = mdbx_txn_commit(c_txn);
|
||||
assert(err == MDBX_THREAD_MISMATCH);
|
||||
ok = ok && err == MDBX_THREAD_MISMATCH;
|
||||
err = mdbx_txn_abort(c_txn);
|
||||
assert(err == MDBX_THREAD_MISMATCH);
|
||||
ok = ok && err == MDBX_THREAD_MISMATCH;
|
||||
#endif /* MDBX_TXN_CHECKOWNER */
|
||||
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
#if MDBX_TXN_CHECKOWNER
|
||||
assert(err == MDBX_THREAD_MISMATCH);
|
||||
ok = ok && err == MDBX_THREAD_MISMATCH;
|
||||
#else
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
#endif /* MDBX_TXN_CHECKOWNER */
|
||||
});
|
||||
|
||||
s.count_down();
|
||||
t.join();
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool case1(const mdbx::path &path) {
|
||||
mdbx::env::operate_parameters operateParameters(100, 10);
|
||||
operateParameters.options.no_sticky_threads = true;
|
||||
operateParameters.options.nested_write_transactions = true;
|
||||
mdbx::env_managed env(path, operateParameters);
|
||||
|
||||
//-------------------------------------
|
||||
auto txn = env.start_write();
|
||||
MDBX_txn *c_txn = txn;
|
||||
int err = mdbx_txn_reset(txn);
|
||||
assert(err == MDBX_EINVAL);
|
||||
bool ok = err == MDBX_EINVAL;
|
||||
|
||||
err = mdbx_txn_break(txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
|
||||
err = mdbx_txn_commit(txn);
|
||||
assert(err == MDBX_RESULT_TRUE);
|
||||
ok = ok && err == MDBX_RESULT_TRUE;
|
||||
|
||||
//-------------------------------------
|
||||
err = mdbx_txn_begin(env, nullptr, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
assert(c_txn == (const MDBX_txn *)txn);
|
||||
|
||||
err = mdbx_txn_break(txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
|
||||
err = mdbx_txn_reset(txn);
|
||||
assert(err == MDBX_EINVAL);
|
||||
ok = ok && err == MDBX_EINVAL;
|
||||
|
||||
err = mdbx_txn_commit(txn);
|
||||
assert(err == MDBX_RESULT_TRUE);
|
||||
ok = ok && err == MDBX_RESULT_TRUE;
|
||||
|
||||
err = mdbx_txn_abort(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
//-------------------------------------
|
||||
err = mdbx_txn_begin(env, nullptr, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
assert(c_txn == (const MDBX_txn *)txn);
|
||||
txn.commit();
|
||||
|
||||
err = mdbx_txn_reset(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
err = mdbx_txn_break(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
err = mdbx_txn_abort(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
|
||||
//=====================================
|
||||
|
||||
txn = env.start_read();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
txn.make_broken();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
txn.reset_reading();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
txn.abort();
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
txn = env.start_read();
|
||||
txn.reset_reading();
|
||||
txn.make_broken();
|
||||
txn.abort();
|
||||
|
||||
//=====================================
|
||||
|
||||
std::latch s1(1), s2(1), s3(1);
|
||||
txn = env.start_read();
|
||||
c_txn = txn;
|
||||
|
||||
std::thread t([&]() {
|
||||
s1.wait();
|
||||
err = mdbx_txn_break(c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
err = mdbx_txn_reset(c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
txn.renew_reading();
|
||||
s2.count_down();
|
||||
|
||||
s3.wait();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
err = mdbx_txn_commit(c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
c_txn = txn;
|
||||
err = mdbx_txn_commit(c_txn);
|
||||
assert(err == MDBX_THREAD_MISMATCH);
|
||||
ok = ok && err == MDBX_THREAD_MISMATCH;
|
||||
err = mdbx_txn_abort(c_txn);
|
||||
assert(err == MDBX_THREAD_MISMATCH);
|
||||
ok = ok && err == MDBX_THREAD_MISMATCH;
|
||||
err = mdbx_txn_break(c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
err = mdbx_txn_reset(c_txn);
|
||||
assert(err == MDBX_EINVAL);
|
||||
ok = ok && err == MDBX_EINVAL;
|
||||
});
|
||||
|
||||
s1.count_down();
|
||||
s2.wait();
|
||||
txn.commit();
|
||||
txn = env.start_write();
|
||||
s3.count_down();
|
||||
|
||||
t.join();
|
||||
txn.abort();
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool case2(const mdbx::path &path, bool no_sticky_threads) {
|
||||
mdbx::env::operate_parameters operateParameters(100, 10);
|
||||
operateParameters.options.no_sticky_threads = no_sticky_threads;
|
||||
mdbx::env_managed env(path, operateParameters);
|
||||
|
||||
std::latch s(1);
|
||||
std::vector<std::thread> l;
|
||||
for (size_t n = 0; n < 8; ++n)
|
||||
l.push_back(std::thread([&]() {
|
||||
s.wait();
|
||||
for (size_t i = 0; i < 1000000 / RELIEF_FACTOR; ++i) {
|
||||
auto txn = env.start_read();
|
||||
txn.abort();
|
||||
}
|
||||
}));
|
||||
|
||||
s.count_down();
|
||||
for (auto &t : l)
|
||||
t.join();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int doit() {
|
||||
mdbx::path path = "test-txn";
|
||||
mdbx::env::remove(path);
|
||||
mdbx::env::operate_parameters operateParameters(100, 10);
|
||||
|
||||
{
|
||||
mdbx::env_managed::create_parameters createParameters;
|
||||
createParameters.geometry.make_dynamic(21 * mdbx::env::geometry::MiB, 84 * mdbx::env::geometry::MiB);
|
||||
|
||||
operateParameters.options.no_sticky_threads = false;
|
||||
mdbx::env_managed env(path, createParameters, operateParameters);
|
||||
auto txn = env.start_write(false);
|
||||
/* mdbx::map_handle testHandle = */ txn.create_map("xyz", mdbx::key_mode::usual, mdbx::value_mode::single);
|
||||
txn.commit();
|
||||
|
||||
//-------------------------------------
|
||||
txn = env.start_write();
|
||||
MDBX_txn *c_txn = txn;
|
||||
err = mdbx_txn_reset(txn);
|
||||
assert(err == MDBX_EINVAL);
|
||||
ok = ok && err == MDBX_EINVAL;
|
||||
|
||||
err = mdbx_txn_break(txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
|
||||
err = mdbx_txn_commit(txn);
|
||||
assert(err == MDBX_RESULT_TRUE);
|
||||
ok = ok && err == MDBX_RESULT_TRUE;
|
||||
|
||||
//-------------------------------------
|
||||
err = mdbx_txn_begin(env, nullptr, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
assert(c_txn == (const MDBX_txn *)txn);
|
||||
|
||||
err = mdbx_txn_break(txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
|
||||
err = mdbx_txn_reset(txn);
|
||||
assert(err == MDBX_EINVAL);
|
||||
ok = ok && err == MDBX_EINVAL;
|
||||
|
||||
err = mdbx_txn_commit(txn);
|
||||
assert(err == MDBX_RESULT_TRUE);
|
||||
ok = ok && err == MDBX_RESULT_TRUE;
|
||||
|
||||
err = mdbx_txn_abort(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
//-------------------------------------
|
||||
err = mdbx_txn_begin(env, nullptr, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
assert(c_txn == (const MDBX_txn *)txn);
|
||||
txn.commit();
|
||||
|
||||
err = mdbx_txn_reset(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
err = mdbx_txn_break(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
err = mdbx_txn_abort(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
|
||||
//=====================================
|
||||
|
||||
txn = env.start_read();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
txn.make_broken();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
txn.reset_reading();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
txn.abort();
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
txn = env.start_read();
|
||||
txn.reset_reading();
|
||||
txn.make_broken();
|
||||
txn.abort();
|
||||
|
||||
//=====================================
|
||||
|
||||
std::latch s(1);
|
||||
txn = env.start_read();
|
||||
c_txn = txn;
|
||||
|
||||
std::thread t([&]() {
|
||||
s.wait();
|
||||
err = mdbx_txn_reset(c_txn);
|
||||
assert(err == MDBX_THREAD_MISMATCH);
|
||||
ok = ok && err == MDBX_THREAD_MISMATCH;
|
||||
err = mdbx_txn_break(c_txn);
|
||||
assert(err == MDBX_THREAD_MISMATCH);
|
||||
ok = ok && err == MDBX_THREAD_MISMATCH;
|
||||
err = mdbx_txn_commit(c_txn);
|
||||
assert(err == MDBX_THREAD_MISMATCH);
|
||||
ok = ok && err == MDBX_THREAD_MISMATCH;
|
||||
err = mdbx_txn_abort(c_txn);
|
||||
assert(err == MDBX_THREAD_MISMATCH);
|
||||
ok = ok && err == MDBX_THREAD_MISMATCH;
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
});
|
||||
|
||||
s.count_down();
|
||||
t.join();
|
||||
}
|
||||
|
||||
//=====================================
|
||||
//=====================================
|
||||
|
||||
{
|
||||
operateParameters.options.no_sticky_threads = true;
|
||||
operateParameters.options.nested_write_transactions = true;
|
||||
mdbx::env_managed env(path, operateParameters);
|
||||
|
||||
//-------------------------------------
|
||||
auto txn = env.start_write();
|
||||
MDBX_txn *c_txn = txn;
|
||||
err = mdbx_txn_reset(txn);
|
||||
assert(err == MDBX_EINVAL);
|
||||
ok = ok && err == MDBX_EINVAL;
|
||||
|
||||
err = mdbx_txn_break(txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
|
||||
err = mdbx_txn_commit(txn);
|
||||
assert(err == MDBX_RESULT_TRUE);
|
||||
ok = ok && err == MDBX_RESULT_TRUE;
|
||||
|
||||
//-------------------------------------
|
||||
err = mdbx_txn_begin(env, nullptr, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
assert(c_txn == (const MDBX_txn *)txn);
|
||||
|
||||
err = mdbx_txn_break(txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
|
||||
err = mdbx_txn_reset(txn);
|
||||
assert(err == MDBX_EINVAL);
|
||||
ok = ok && err == MDBX_EINVAL;
|
||||
|
||||
err = mdbx_txn_commit(txn);
|
||||
assert(err == MDBX_RESULT_TRUE);
|
||||
ok = ok && err == MDBX_RESULT_TRUE;
|
||||
|
||||
err = mdbx_txn_abort(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
//-------------------------------------
|
||||
err = mdbx_txn_begin(env, nullptr, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
assert(c_txn == (const MDBX_txn *)txn);
|
||||
txn.commit();
|
||||
|
||||
err = mdbx_txn_reset(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
err = mdbx_txn_break(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
err = mdbx_txn_abort(c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
|
||||
//=====================================
|
||||
|
||||
txn = env.start_read();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
txn.make_broken();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
txn.reset_reading();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_BAD_TXN);
|
||||
ok = ok && err == MDBX_BAD_TXN;
|
||||
txn.abort();
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
txn = env.start_read();
|
||||
txn.reset_reading();
|
||||
txn.make_broken();
|
||||
txn.abort();
|
||||
|
||||
//=====================================
|
||||
|
||||
std::latch s1(1), s2(1), s3(1);
|
||||
txn = env.start_read();
|
||||
c_txn = txn;
|
||||
|
||||
std::thread t([&]() {
|
||||
s1.wait();
|
||||
err = mdbx_txn_break(c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
err = mdbx_txn_reset(c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
txn.renew_reading();
|
||||
s2.count_down();
|
||||
|
||||
s3.wait();
|
||||
err = mdbx_txn_begin(env, txn, MDBX_TXN_READWRITE, &c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
err = mdbx_txn_commit(c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
c_txn = txn;
|
||||
err = mdbx_txn_commit(c_txn);
|
||||
assert(err == MDBX_THREAD_MISMATCH);
|
||||
ok = ok && err == MDBX_THREAD_MISMATCH;
|
||||
err = mdbx_txn_abort(c_txn);
|
||||
assert(err == MDBX_THREAD_MISMATCH);
|
||||
ok = ok && err == MDBX_THREAD_MISMATCH;
|
||||
err = mdbx_txn_break(c_txn);
|
||||
assert(err == MDBX_SUCCESS);
|
||||
ok = ok && err == MDBX_SUCCESS;
|
||||
err = mdbx_txn_reset(c_txn);
|
||||
assert(err == MDBX_EINVAL);
|
||||
ok = ok && err == MDBX_EINVAL;
|
||||
});
|
||||
|
||||
s1.count_down();
|
||||
s2.wait();
|
||||
txn.commit();
|
||||
txn = env.start_write();
|
||||
s3.count_down();
|
||||
|
||||
t.join();
|
||||
txn.abort();
|
||||
}
|
||||
bool ok = case0(path);
|
||||
ok = case1(path) && ok;
|
||||
ok = case2(path, false) && ok;
|
||||
ok = case2(path, true) && ok;
|
||||
|
||||
std::cout << (ok ? "OK\n" : "FAIL\n");
|
||||
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
mdbx_setup_debug_nofmt(MDBX_LOG_VERBOSE, MDBX_DBG_ASSERT, logger_nofmt, log_buffer, sizeof(log_buffer));
|
||||
try {
|
||||
return doit();
|
||||
} catch (const std::exception &ex) {
|
||||
std::cerr << "Exception: " << ex.what() << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __cpp_lib_latch */
|
||||
|
@@ -562,7 +562,7 @@ int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
if (pid == 0 || sigbreak)
|
||||
break;
|
||||
|
||||
if (err != EINTR)
|
||||
|
@@ -440,7 +440,7 @@ else
|
||||
fi
|
||||
|
||||
if [ "$EXTRA" != "no" ]; then
|
||||
options=(perturb nomeminit nordahead writemap lifo nostickythreads)
|
||||
options=(perturb nomeminit nordahead writemap lifo nostickythreads validation)
|
||||
else
|
||||
options=(writemap lifo nostickythreads)
|
||||
fi
|
||||
|
Reference in New Issue
Block a user