summaryrefslogtreecommitdiff
path: root/libs/libmdbx/src/test
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2020-01-07 22:56:32 +0300
committerGeorge Hazan <ghazan@miranda.im>2020-01-07 22:56:39 +0300
commit7524acda0a8778572262429f27b359d7ee91fc90 (patch)
tree6443228a439b83c5bc6d15923a9ccd91fa4718e3 /libs/libmdbx/src/test
parent62c842df372abe0349f025ac4cbbc8426526ac8b (diff)
libmdbx: update to 0.5.0
Diffstat (limited to 'libs/libmdbx/src/test')
-rw-r--r--libs/libmdbx/src/test/CMakeLists.txt36
-rw-r--r--libs/libmdbx/src/test/append.cc5
-rw-r--r--libs/libmdbx/src/test/base.h9
-rw-r--r--libs/libmdbx/src/test/cases.cc13
-rw-r--r--libs/libmdbx/src/test/chrono.cc7
-rw-r--r--libs/libmdbx/src/test/chrono.h3
-rw-r--r--libs/libmdbx/src/test/config.cc159
-rw-r--r--libs/libmdbx/src/test/config.h14
-rw-r--r--libs/libmdbx/src/test/copy.cc2
-rw-r--r--libs/libmdbx/src/test/dead.cc2
-rw-r--r--libs/libmdbx/src/test/hill.cc124
-rw-r--r--libs/libmdbx/src/test/jitter.cc42
-rw-r--r--libs/libmdbx/src/test/keygen.cc105
-rw-r--r--libs/libmdbx/src/test/keygen.h21
-rw-r--r--libs/libmdbx/src/test/log.cc177
-rw-r--r--libs/libmdbx/src/test/log.h45
-rw-r--r--libs/libmdbx/src/test/long_stochastic.sh98
-rw-r--r--libs/libmdbx/src/test/main.cc193
-rw-r--r--libs/libmdbx/src/test/osal-unix.cc434
-rw-r--r--libs/libmdbx/src/test/osal-windows.cc92
-rw-r--r--libs/libmdbx/src/test/osal.h4
-rw-r--r--libs/libmdbx/src/test/pcrf/CMakeLists.txt4
-rw-r--r--libs/libmdbx/src/test/pcrf/pcrf_test.c83
-rw-r--r--libs/libmdbx/src/test/test.cc236
-rw-r--r--libs/libmdbx/src/test/test.h149
-rw-r--r--libs/libmdbx/src/test/test.vcxproj209
-rw-r--r--libs/libmdbx/src/test/try.cc2
-rw-r--r--libs/libmdbx/src/test/ttl.cc41
-rw-r--r--libs/libmdbx/src/test/utils.cc5
-rw-r--r--libs/libmdbx/src/test/utils.h13
30 files changed, 1572 insertions, 755 deletions
diff --git a/libs/libmdbx/src/test/CMakeLists.txt b/libs/libmdbx/src/test/CMakeLists.txt
index bb4abd5d6d..0cc22182c9 100644
--- a/libs/libmdbx/src/test/CMakeLists.txt
+++ b/libs/libmdbx/src/test/CMakeLists.txt
@@ -1,10 +1,10 @@
-set(TARGET mdbx_test)
-project(${TARGET})
+if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+ set(TEST_OSAL windows)
+else()
+ set(TEST_OSAL unix)
+endif()
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-declarations")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-cast-qual")
-
-add_executable(${TARGET}
+add_executable(mdbx_test
base.h
cases.cc
chrono.cc
@@ -21,7 +21,7 @@ add_executable(${TARGET}
log.h
main.cc
osal.h
- osal-unix.cc
+ osal-${TEST_OSAL}.cc
test.cc
test.h
try.cc
@@ -29,9 +29,25 @@ add_executable(${TARGET}
utils.h
append.cc
ttl.cc
+ nested.cc
)
-target_link_libraries(${TARGET}
- mdbx
- )
+set_target_properties(mdbx_test PROPERTIES
+ INTERPROCEDURAL_OPTIMIZATION $<BOOL:${INTERPROCEDURAL_OPTIMIZATION}>
+ CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON)
+
+if(CC_HAS_FASTMATH)
+ target_compile_options(mdbx_test PRIVATE "-ffast-math")
+endif()
+if(CC_HAS_VISIBILITY AND (LTO_ENABLED OR INTERPROCEDURAL_OPTIMIZATION))
+ set_target_properties(mdbx_test PROPERTIES LINK_FLAGS "-fvisibility=hidden")
+endif()
+
+target_link_libraries(mdbx_test mdbx ${LIB_MATH} ${CMAKE_THREAD_LIBS_INIT})
+if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+ target_link_libraries(mdbx_test winmm.lib)
+endif()
+if(UNIX AND NOT SUBPROJECT)
+ add_subdirectory(pcrf)
+endif()
diff --git a/libs/libmdbx/src/test/append.cc b/libs/libmdbx/src/test/append.cc
index 273f68b810..a30351dcde 100644
--- a/libs/libmdbx/src/test/append.cc
+++ b/libs/libmdbx/src/test/append.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -15,7 +15,6 @@
#include "test.h"
bool testcase_append::run() {
- MDBX_dbi dbi;
int err = db_open__begin__table_create_open_clean(dbi);
if (unlikely(err != MDBX_SUCCESS)) {
log_notice("append: bailout-prepare due '%s'", mdbx_strerror(err));
@@ -54,7 +53,7 @@ bool testcase_append::run() {
}
log_trace("append: append-a %" PRIu64, serial);
- generate_pair(serial, key, data);
+ generate_pair(serial);
int cmp = inserted_number ? mdbx_cmp(txn_guard.get(), dbi, &key->value,
&last_key->value)
: 1;
diff --git a/libs/libmdbx/src/test/base.h b/libs/libmdbx/src/test/base.h
index 5ca134a81d..05dfd1c4e3 100644
--- a/libs/libmdbx/src/test/base.h
+++ b/libs/libmdbx/src/test/base.h
@@ -80,14 +80,19 @@
#include <unordered_set>
#include <vector>
+#define MDBX_INTERNAL_FUNC
+#define MDBX_INTERNAL_VAR extern
+#define MDBX_TOOLS /* Avoid using internal mdbx_assert() */
#include "../mdbx.h"
-#include "../src/defs.h"
-#include "../src/osal.h"
+#include "../src/elements/defs.h"
+#include "../src/elements/osal.h"
#if !defined(__thread) && (defined(_MSC_VER) || defined(__DMC__))
#define __thread __declspec(thread)
#endif /* __thread */
+#include "../src/elements/options.h"
+
#ifdef _MSC_VER
#pragma warning(pop)
#pragma warning(disable : 4201) /* nonstandard extension used : \
diff --git a/libs/libmdbx/src/test/cases.cc b/libs/libmdbx/src/test/cases.cc
index a98834a457..b01169004b 100644
--- a/libs/libmdbx/src/test/cases.cc
+++ b/libs/libmdbx/src/test/cases.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -17,7 +17,6 @@
void configure_actor(unsigned &last_space_id, const actor_testcase testcase,
const char *space_id_cstr, const actor_params &params) {
unsigned wait4id = 0;
-
if (params.waitfor_nops) {
for (auto i = global::actors.rbegin(); i != global::actors.rend(); ++i) {
if (i->is_waitable(params.waitfor_nops)) {
@@ -61,15 +60,15 @@ void testcase_setup(const char *casename, actor_params &params,
unsigned &last_space_id) {
if (strcmp(casename, "basic") == 0) {
log_notice(">>> testcase_setup(%s)", casename);
- configure_actor(last_space_id, ac_jitter, nullptr, params);
- configure_actor(last_space_id, ac_hill, nullptr, params);
- configure_actor(last_space_id, ac_ttl, nullptr, params);
- configure_actor(last_space_id, ac_jitter, nullptr, params);
+ configure_actor(last_space_id, ac_nested, nullptr, params);
configure_actor(last_space_id, ac_hill, nullptr, params);
configure_actor(last_space_id, ac_ttl, nullptr, params);
- configure_actor(last_space_id, ac_try, nullptr, params);
configure_actor(last_space_id, ac_copy, nullptr, params);
configure_actor(last_space_id, ac_append, nullptr, params);
+ configure_actor(last_space_id, ac_jitter, nullptr, params);
+ configure_actor(last_space_id, ac_try, nullptr, params);
+ configure_actor(last_space_id, ac_jitter, nullptr, params);
+ configure_actor(last_space_id, ac_try, nullptr, params);
log_notice("<<< testcase_setup(%s): done", casename);
} else {
failure("unknown testcase `%s`", casename);
diff --git a/libs/libmdbx/src/test/chrono.cc b/libs/libmdbx/src/test/chrono.cc
index 38cb321a81..315d379088 100644
--- a/libs/libmdbx/src/test/chrono.cc
+++ b/libs/libmdbx/src/test/chrono.cc
@@ -16,7 +16,10 @@
namespace chrono {
+#ifndef NSEC_PER_SEC
#define NSEC_PER_SEC 1000000000u
+#endif /* NSEC_PER_SEC */
+
uint32_t ns2fractional(uint32_t ns) {
assert(ns < NSEC_PER_SEC);
/* LY: здесь и далее используется "длинное деление", которое
@@ -30,7 +33,9 @@ uint32_t fractional2ns(uint32_t fractional) {
return (fractional * (uint64_t)NSEC_PER_SEC) >> 32;
}
+#ifndef USEC_PER_SEC
#define USEC_PER_SEC 1000000u
+#endif /* USEC_PER_SEC */
uint32_t us2fractional(uint32_t us) {
assert(us < USEC_PER_SEC);
return ((uint64_t)us << 32) / USEC_PER_SEC;
@@ -40,7 +45,9 @@ uint32_t fractional2us(uint32_t fractional) {
return (fractional * (uint64_t)USEC_PER_SEC) >> 32;
}
+#ifndef MSEC_PER_SEC
#define MSEC_PER_SEC 1000u
+#endif /* MSEC_PER_SEC */
uint32_t ms2fractional(uint32_t ms) {
assert(ms < MSEC_PER_SEC);
return ((uint64_t)ms << 32) / MSEC_PER_SEC;
diff --git a/libs/libmdbx/src/test/chrono.h b/libs/libmdbx/src/test/chrono.h
index 11675195ac..07cdef66cf 100644
--- a/libs/libmdbx/src/test/chrono.h
+++ b/libs/libmdbx/src/test/chrono.h
@@ -15,7 +15,6 @@
#pragma once
#include "base.h"
-#include "log.h"
#include "utils.h"
namespace chrono {
@@ -24,7 +23,7 @@ namespace chrono {
typedef union time {
uint64_t fixedpoint;
- struct {
+ __anonymous_struct_extension__ struct {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
uint32_t fractional;
union {
diff --git a/libs/libmdbx/src/test/config.cc b/libs/libmdbx/src/test/config.cc
index dd150e9a91..eedfd7b2d4 100644
--- a/libs/libmdbx/src/test/config.cc
+++ b/libs/libmdbx/src/test/config.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -294,14 +294,21 @@ bool parse_option(int argc, char *const argv[], int &narg, const char *option,
//-----------------------------------------------------------------------------
-const struct option_verb mode_bits[] = {
- {"rdonly", MDBX_RDONLY}, {"mapasync", MDBX_MAPASYNC},
- {"utterly", MDBX_UTTERLY_NOSYNC}, {"nosubdir", MDBX_NOSUBDIR},
- {"nosync", MDBX_NOSYNC}, {"nometasync", MDBX_NOMETASYNC},
- {"writemap", MDBX_WRITEMAP}, {"notls", MDBX_NOTLS},
- {"nordahead", MDBX_NORDAHEAD}, {"nomeminit", MDBX_NOMEMINIT},
- {"coalesce", MDBX_COALESCE}, {"lifo", MDBX_LIFORECLAIM},
- {"perturb", MDBX_PAGEPERTURB}, {nullptr, 0}};
+const struct option_verb mode_bits[] = {{"rdonly", MDBX_RDONLY},
+ {"mapasync", MDBX_MAPASYNC},
+ {"nosync-utterly", MDBX_UTTERLY_NOSYNC},
+ {"nosubdir", MDBX_NOSUBDIR},
+ {"nosync-safe", MDBX_SAFE_NOSYNC},
+ {"nometasync", MDBX_NOMETASYNC},
+ {"writemap", MDBX_WRITEMAP},
+ {"notls", MDBX_NOTLS},
+ {"nordahead", MDBX_NORDAHEAD},
+ {"nomeminit", MDBX_NOMEMINIT},
+ {"coalesce", MDBX_COALESCE},
+ {"lifo", MDBX_LIFORECLAIM},
+ {"perturb", MDBX_PAGEPERTURB},
+ {"accede", MDBX_ACCEDE},
+ {nullptr, 0}};
const struct option_verb table_bits[] = {
{"key.reverse", MDBX_REVERSEKEY},
@@ -314,7 +321,7 @@ const struct option_verb table_bits[] = {
static void dump_verbs(const char *caption, size_t bits,
const struct option_verb *verbs) {
- log_info("%s: 0x%" PRIx64 " = ", caption, (uint64_t)bits);
+ log_verbose("%s: 0x%" PRIx64 " = ", caption, (uint64_t)bits);
const char *comma = "";
while (verbs->mask && bits) {
@@ -330,7 +337,7 @@ static void dump_verbs(const char *caption, size_t bits,
}
static void dump_duration(const char *caption, unsigned duration) {
- log_info("%s: ", caption);
+ log_verbose("%s: ", caption);
if (duration) {
if (duration > 24 * 3600)
logging::feed("%u_", duration / (24 * 3600));
@@ -347,84 +354,92 @@ void dump(const char *title) {
logging::local_suffix indent(title);
for (auto i = global::actors.begin(); i != global::actors.end(); ++i) {
- log_info("#%u, testcase %s, space_id/table %u\n", i->actor_id,
- testcase2str(i->testcase), i->space_id);
+ log_verbose("#%u, testcase %s, space_id/table %u\n", i->actor_id,
+ testcase2str(i->testcase), i->space_id);
indent.push();
if (i->params.loglevel) {
- log_info("log: level %u, %s\n", i->params.loglevel,
- i->params.pathname_log.empty() ? "console"
- : i->params.pathname_log.c_str());
+ log_verbose("log: level %u, %s\n", i->params.loglevel,
+ i->params.pathname_log.empty()
+ ? "console"
+ : i->params.pathname_log.c_str());
}
- log_info("database: %s, size %" PRIuPTR "[%" PRIiPTR "..%" PRIiPTR
- ", %i %i, %i]\n",
- i->params.pathname_db.c_str(), i->params.size_now,
- i->params.size_lower, i->params.size_upper,
- i->params.shrink_threshold, i->params.growth_step,
- i->params.pagesize);
+ log_verbose("database: %s, size %" PRIuPTR "[%" PRIiPTR "..%" PRIiPTR
+ ", %i %i, %i]\n",
+ i->params.pathname_db.c_str(), i->params.size_now,
+ i->params.size_lower, i->params.size_upper,
+ i->params.shrink_threshold, i->params.growth_step,
+ i->params.pagesize);
dump_verbs("mode", i->params.mode_flags, mode_bits);
dump_verbs("table", i->params.table_flags, table_bits);
if (i->params.test_nops)
- log_info("iterations/records %u\n", i->params.test_nops);
+ log_verbose("iterations/records %u\n", i->params.test_nops);
else
dump_duration("duration", i->params.test_duration);
if (i->params.nrepeat)
- log_info("repeat %u\n", i->params.nrepeat);
+ log_verbose("repeat %u\n", i->params.nrepeat);
else
- log_info("repeat ETERNALLY\n");
+ log_verbose("repeat ETERNALLY\n");
- log_info("threads %u\n", i->params.nthreads);
+ log_verbose("threads %u\n", i->params.nthreads);
- log_info(
+ log_verbose(
"keygen.params: case %s, width %u, mesh %u, rotate %u, offset %" PRIu64
", split %u/%u\n",
keygencase2str(i->params.keygen.keycase), i->params.keygen.width,
i->params.keygen.mesh, i->params.keygen.rotate, i->params.keygen.offset,
i->params.keygen.split,
i->params.keygen.width - i->params.keygen.split);
- log_info("keygen.seed: %u\n", i->params.keygen.seed);
- log_info("key: minlen %u, maxlen %u\n", i->params.keylen_min,
- i->params.keylen_max);
- log_info("data: minlen %u, maxlen %u\n", i->params.datalen_min,
- i->params.datalen_max);
+ log_verbose("keygen.seed: %u\n", i->params.keygen.seed);
+ log_verbose("key: minlen %u, maxlen %u\n", i->params.keylen_min,
+ i->params.keylen_max);
+ log_verbose("data: minlen %u, maxlen %u\n", i->params.datalen_min,
+ i->params.datalen_max);
- log_info("batch: read %u, write %u\n", i->params.batch_read,
- i->params.batch_write);
+ log_verbose("batch: read %u, write %u\n", i->params.batch_read,
+ i->params.batch_write);
if (i->params.waitfor_nops)
- log_info("wait: actor %u for %u ops\n", i->wait4id,
- i->params.waitfor_nops);
+ log_verbose("wait: actor %u for %u ops\n", i->wait4id,
+ i->params.waitfor_nops);
else if (i->params.delaystart)
dump_duration("delay", i->params.delaystart);
else
- log_info("no-delay\n");
+ log_verbose("no-delay\n");
if (i->params.inject_writefaultn)
- log_info("inject-writefault on %u ops\n", i->params.inject_writefaultn);
+ log_verbose("inject-writefault on %u ops\n",
+ i->params.inject_writefaultn);
else
- log_info("no-inject-writefault\n");
+ log_verbose("no-inject-writefault\n");
- log_info("limits: readers %u, tables %u\n", i->params.max_readers,
- i->params.max_tables);
+ log_verbose("limits: readers %u, tables %u, txn-bytes %zu\n",
+ i->params.max_readers, i->params.max_tables,
+ mdbx_limits_txnsize_max(i->params.pagesize));
+
+ log_verbose("drop table: %s\n", i->params.drop_table ? "Yes" : "No");
+ log_verbose("ignore MDBX_MAP_FULL error: %s\n",
+ i->params.ignore_dbfull ? "Yes" : "No");
+ log_verbose("verifying by speculum: %s\n",
+ i->params.speculum ? "Yes" : "No");
- log_info("drop table: %s\n", i->params.drop_table ? "Yes" : "No");
- log_info("ignore MDBX_MAP_FULL error: %s\n",
- i->params.ignore_dbfull ? "Yes" : "No");
indent.pop();
}
dump_duration("timeout", global::config::timeout_duration_seconds);
- log_info("cleanup: before %s, after %s\n",
- global::config::cleanup_before ? "Yes" : "No",
- global::config::cleanup_after ? "Yes" : "No");
-
- log_info("failfast: %s\n", global::config::failfast ? "Yes" : "No");
- log_info("progress indicator: %s\n",
- global::config::progress_indicator ? "Yes" : "No");
+ log_verbose("cleanup: before %s, after %s\n",
+ global::config::cleanup_before ? "Yes" : "No",
+ global::config::cleanup_after ? "Yes" : "No");
+
+ log_verbose("failfast: %s\n", global::config::failfast ? "Yes" : "No");
+ log_verbose("progress indicator: %s\n",
+ global::config::progress_indicator ? "Yes" : "No");
+ log_verbose("console mode: %s\n",
+ global::config::console_mode ? "Yes" : "No");
}
} /* namespace config */
@@ -452,26 +467,31 @@ const std::string actor_config::serialize(const char *prefix) const {
checksum.push(params.pathname_db);
result.append(params.pathname_db);
- result.append("|");
+ result.push_back('|');
checksum.push(params.pathname_log);
result.append(params.pathname_log);
- result.append("|");
+ result.push_back('|');
static_assert(std::is_pod<actor_params_pod>::value,
"actor_params_pod should by POD");
result.append(data2hex(static_cast<const actor_params_pod *>(&params),
sizeof(actor_params_pod), checksum));
- result.append("|");
+ result.push_back('|');
static_assert(std::is_pod<actor_config_pod>::value,
"actor_config_pod should by POD");
result.append(data2hex(static_cast<const actor_config_pod *>(this),
sizeof(actor_config_pod), checksum));
- result.append("|");
+ result.push_back('|');
+ result.push_back(global::config::progress_indicator ? 'Y' : 'N');
+ checksum.push(global::config::progress_indicator);
+ result.push_back(global::config::console_mode ? 'Y' : 'N');
+ checksum.push(global::config::console_mode);
+ result.push_back('|');
result.append(osal_serialize(checksum));
- result.append("|");
+ result.push_back('|');
result.append(std::to_string(checksum.value));
return result;
@@ -535,6 +555,20 @@ bool actor_config::deserialize(const char *str, actor_config &config) {
TRACE("<< actor_config::deserialize: slash-5\n");
return false;
}
+ if ((str[0] == 'Y' || str[0] == 'N') && (str[1] == 'Y' || str[1] == 'N')) {
+ global::config::progress_indicator = str[0] == 'Y';
+ checksum.push(global::config::progress_indicator);
+ global::config::console_mode = str[1] == 'Y';
+ checksum.push(global::config::console_mode);
+ str = slash + 1;
+
+ slash = strchr(str, '|');
+ if (!slash) {
+ TRACE("<< actor_config::deserialize: slash-6\n");
+ return false;
+ }
+ }
+
if (!config.osal_deserialize(str, slash, checksum)) {
TRACE("<< actor_config::deserialize: osal\n");
return false;
@@ -556,10 +590,7 @@ unsigned actor_params::mdbx_keylen_min() const {
}
unsigned actor_params::mdbx_keylen_max() const {
- return (table_flags & MDBX_INTEGERKEY)
- ? 8
- : std::min((unsigned)mdbx_limits_keysize_max(pagesize),
- (unsigned)UINT16_MAX);
+ return (unsigned)mdbx_limits_keysize_max(pagesize, table_flags);
}
unsigned actor_params::mdbx_datalen_min() const {
@@ -567,10 +598,6 @@ unsigned actor_params::mdbx_datalen_min() const {
}
unsigned actor_params::mdbx_datalen_max() const {
- return (table_flags & MDBX_INTEGERDUP)
- ? 8
- : std::min((table_flags & MDBX_DUPSORT)
- ? (unsigned)mdbx_limits_keysize_max(pagesize)
- : (unsigned)MDBX_MAXDATASIZE,
- (unsigned)UINT16_MAX);
+ return std::min((unsigned)UINT16_MAX,
+ (unsigned)mdbx_limits_valsize_max(pagesize, table_flags));
}
diff --git a/libs/libmdbx/src/test/config.h b/libs/libmdbx/src/test/config.h
index 89889d8eb6..9e083f3f22 100644
--- a/libs/libmdbx/src/test/config.h
+++ b/libs/libmdbx/src/test/config.h
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -29,12 +29,13 @@ enum actor_testcase {
ac_try,
ac_copy,
ac_append,
- ac_ttl
+ ac_ttl,
+ ac_nested
};
enum actor_status {
as_unknown,
- as_debuging,
+ as_debugging,
as_running,
as_successful,
as_killed,
@@ -123,8 +124,6 @@ inline bool parse_option_intptr(int argc, char *const argv[], int &narg,
#pragma pack(push, 1)
struct keygen_params_pod {
- keygen_case keycase;
-
/* Параметры генератора пар key-value.
*
* Ключи и значения генерируются по задаваемым параметрам на основе "плоской"
@@ -231,11 +230,10 @@ struct keygen_params_pod {
uint8_t split;
uint32_t seed;
uint64_t offset;
+ keygen_case keycase;
};
struct actor_params_pod {
- unsigned loglevel;
-
unsigned mode_flags;
unsigned table_flags;
intptr_t size_lower;
@@ -264,8 +262,10 @@ struct actor_params_pod {
unsigned max_tables;
keygen_params_pod keygen;
+ uint8_t loglevel;
bool drop_table;
bool ignore_dbfull;
+ bool speculum;
};
struct actor_config_pod {
diff --git a/libs/libmdbx/src/test/copy.cc b/libs/libmdbx/src/test/copy.cc
index e239d41e34..ff53153e1a 100644
--- a/libs/libmdbx/src/test/copy.cc
+++ b/libs/libmdbx/src/test/copy.cc
@@ -1,4 +1,4 @@
-#include "test.h"
+#include "test.h"
void testcase_copy::copy_db(const bool with_compaction) {
int err = osal_removefile(copy_pathname);
diff --git a/libs/libmdbx/src/test/dead.cc b/libs/libmdbx/src/test/dead.cc
index a1a8b5f9de..8f83bbeb2e 100644
--- a/libs/libmdbx/src/test/dead.cc
+++ b/libs/libmdbx/src/test/dead.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
diff --git a/libs/libmdbx/src/test/hill.cc b/libs/libmdbx/src/test/hill.cc
index 1b03ddf0fc..9d9890951a 100644
--- a/libs/libmdbx/src/test/hill.cc
+++ b/libs/libmdbx/src/test/hill.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -15,12 +15,13 @@
#include "test.h"
bool testcase_hill::run() {
- MDBX_dbi dbi;
int err = db_open__begin__table_create_open_clean(dbi);
if (unlikely(err != MDBX_SUCCESS)) {
log_notice("hill: bailout-prepare due '%s'", mdbx_strerror(err));
- return true;
+ return false;
}
+ speculum.clear();
+ speculum_commited.clear();
/* LY: тест "холмиком":
* - сначала наполняем таблицу циклическими CRUD-манипуляциями,
@@ -63,6 +64,7 @@ bool testcase_hill::run() {
uint64_t commited_serial = serial_count;
unsigned txn_nops = 0;
+ bool rc = false;
while (should_continue()) {
const keygen::serial_t a_serial = serial_count;
if (unlikely(!keyvalue_maker.increment(serial_count, 1))) {
@@ -78,53 +80,74 @@ bool testcase_hill::run() {
log_trace("uphill: insert-a (age %" PRIu64 ") %" PRIu64, age_shift,
a_serial);
generate_pair(a_serial, a_key, a_data_1, age_shift);
- err = mdbx_put(txn_guard.get(), dbi, &a_key->value, &a_data_1->value,
- insert_flags);
+
+ err = insert(a_key, a_data_1, insert_flags);
if (unlikely(err != MDBX_SUCCESS)) {
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
log_notice("uphill: bailout at insert-a due '%s'", mdbx_strerror(err));
txn_restart(true, false);
serial_count = commited_serial;
+ speculum = speculum_commited;
break;
}
failure_perror("mdbx_put(insert-a.1)", err);
}
+ if (!speculum_verify()) {
+ log_notice("uphill: bailout after insert-a, before commit");
+ goto bailout;
+ }
if (++txn_nops >= config.params.batch_write) {
err = breakable_restart();
if (unlikely(err != MDBX_SUCCESS)) {
log_notice("uphill: bailout at commit due '%s'", mdbx_strerror(err));
serial_count = commited_serial;
+ speculum = speculum_commited;
break;
}
+ speculum_commited = speculum;
commited_serial = a_serial;
txn_nops = 0;
+ if (!speculum_verify()) {
+ log_notice("uphill: bailout after insert-a, after commit");
+ goto bailout;
+ }
}
// создаем вторую запись из пары
log_trace("uphill: insert-b %" PRIu64, b_serial);
generate_pair(b_serial, b_key, b_data, 0);
- err = mdbx_put(txn_guard.get(), dbi, &b_key->value, &b_data->value,
- insert_flags);
+ err = insert(b_key, b_data, insert_flags);
if (unlikely(err != MDBX_SUCCESS)) {
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
log_notice("uphill: bailout at insert-b due '%s'", mdbx_strerror(err));
txn_restart(true, false);
serial_count = commited_serial;
+ speculum = speculum_commited;
break;
}
failure_perror("mdbx_put(insert-b)", err);
}
+ if (!speculum_verify()) {
+ log_notice("uphill: bailout after insert-b, before commit");
+ goto bailout;
+ }
if (++txn_nops >= config.params.batch_write) {
err = breakable_restart();
if (unlikely(err != MDBX_SUCCESS)) {
log_notice("uphill: bailout at commit due '%s'", mdbx_strerror(err));
serial_count = commited_serial;
+ speculum = speculum_commited;
break;
}
+ speculum_commited = speculum;
commited_serial = a_serial;
txn_nops = 0;
+ if (!speculum_verify()) {
+ log_notice("uphill: bailout after insert-b, after commit");
+ goto bailout;
+ }
}
// обновляем данные в первой записи
@@ -132,52 +155,73 @@ bool testcase_hill::run() {
a_serial);
generate_pair(a_serial, a_key, a_data_0, 0);
checkdata("uphill: update-a", dbi, a_key->value, a_data_1->value);
- err = mdbx_replace(txn_guard.get(), dbi, &a_key->value, &a_data_0->value,
- &a_data_1->value, update_flags);
+ err = replace(a_key, a_data_0, a_data_1, update_flags);
if (unlikely(err != MDBX_SUCCESS)) {
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
log_notice("uphill: bailout at update-a due '%s'", mdbx_strerror(err));
txn_restart(true, false);
serial_count = commited_serial;
+ speculum = speculum_commited;
break;
}
failure_perror("mdbx_replace(update-a: 1->0)", err);
}
+ if (!speculum_verify()) {
+ log_notice("uphill: bailout after update-a, before commit");
+ goto bailout;
+ }
if (++txn_nops >= config.params.batch_write) {
err = breakable_restart();
if (unlikely(err != MDBX_SUCCESS)) {
log_notice("uphill: bailout at commit due '%s'", mdbx_strerror(err));
serial_count = commited_serial;
+ speculum = speculum_commited;
break;
}
+ speculum_commited = speculum;
commited_serial = a_serial;
txn_nops = 0;
+ if (!speculum_verify()) {
+ log_notice("uphill: bailout after update-a, after commit");
+ goto bailout;
+ }
}
// удаляем вторую запись
log_trace("uphill: delete-b %" PRIu64, b_serial);
checkdata("uphill: delete-b", dbi, b_key->value, b_data->value);
- err = mdbx_del(txn_guard.get(), dbi, &b_key->value, &b_data->value);
+ err = remove(b_key, b_data);
if (unlikely(err != MDBX_SUCCESS)) {
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
log_notice("uphill: bailout at delete-b due '%s'", mdbx_strerror(err));
txn_restart(true, false);
serial_count = commited_serial;
+ speculum = speculum_commited;
break;
}
failure_perror("mdbx_del(b)", err);
}
+ if (!speculum_verify()) {
+ log_notice("uphill: bailout after delete-b, before commit");
+ goto bailout;
+ }
if (++txn_nops >= config.params.batch_write) {
err = breakable_restart();
if (unlikely(err != MDBX_SUCCESS)) {
log_notice("uphill: bailout at commit due '%s'", mdbx_strerror(err));
serial_count = commited_serial;
+ speculum = speculum_commited;
break;
}
+ speculum_commited = speculum;
commited_serial = a_serial;
txn_nops = 0;
+ if (!speculum_verify()) {
+ log_notice("uphill: bailout after delete-b, after commit");
+ goto bailout;
+ }
}
report(1);
@@ -204,101 +248,145 @@ bool testcase_hill::run() {
generate_pair(a_serial, a_key, a_data_0, 0);
generate_pair(a_serial, a_key, a_data_1, age_shift);
checkdata("downhill: update-a", dbi, a_key->value, a_data_0->value);
- err = mdbx_replace(txn_guard.get(), dbi, &a_key->value, &a_data_1->value,
- &a_data_0->value, update_flags);
+ err = replace(a_key, a_data_1, a_data_0, update_flags);
if (unlikely(err != MDBX_SUCCESS)) {
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
log_notice("downhill: bailout at update-a due '%s'",
mdbx_strerror(err));
txn_end(true);
+ speculum = speculum_commited;
break;
}
failure_perror("mdbx_put(update-a: 0->1)", err);
}
+ if (!speculum_verify()) {
+ log_notice("downhill: bailout after update-a, before commit");
+ break;
+ }
if (++txn_nops >= config.params.batch_write) {
err = breakable_restart();
if (unlikely(err != MDBX_SUCCESS)) {
log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
+ speculum = speculum_commited;
break;
}
+ speculum_commited = speculum;
txn_nops = 0;
+ if (!speculum_verify()) {
+ log_notice("downhill: bailout after update-a, after commit");
+ break;
+ }
}
// создаем вторую запись из пары
log_trace("downhill: insert-b %" PRIu64, b_serial);
generate_pair(b_serial, b_key, b_data, 0);
- err = mdbx_put(txn_guard.get(), dbi, &b_key->value, &b_data->value,
- insert_flags);
+ err = insert(b_key, b_data, insert_flags);
if (unlikely(err != MDBX_SUCCESS)) {
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
log_notice("downhill: bailout at insert-a due '%s'",
mdbx_strerror(err));
txn_end(true);
+ speculum = speculum_commited;
break;
}
failure_perror("mdbx_put(insert-b)", err);
}
+ if (!speculum_verify()) {
+ log_notice("downhill: bailout after insert-b, before commit");
+ break;
+ }
if (++txn_nops >= config.params.batch_write) {
err = breakable_restart();
if (unlikely(err != MDBX_SUCCESS)) {
log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
+ speculum = speculum_commited;
break;
}
+ speculum_commited = speculum;
txn_nops = 0;
+ if (!speculum_verify()) {
+ log_notice("downhill: bailout after insert-b, after commit");
+ break;
+ }
}
// удаляем первую запись
log_trace("downhill: delete-a (age %" PRIu64 ") %" PRIu64, age_shift,
a_serial);
checkdata("downhill: delete-a", dbi, a_key->value, a_data_1->value);
- err = mdbx_del(txn_guard.get(), dbi, &a_key->value, &a_data_1->value);
+ err = remove(a_key, a_data_1);
if (unlikely(err != MDBX_SUCCESS)) {
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
log_notice("downhill: bailout at delete-a due '%s'",
mdbx_strerror(err));
txn_end(true);
+ speculum = speculum_commited;
break;
}
failure_perror("mdbx_del(a)", err);
}
+ if (!speculum_verify()) {
+ log_notice("downhill: bailout after delete-a, before commit");
+ break;
+ }
if (++txn_nops >= config.params.batch_write) {
err = breakable_restart();
if (unlikely(err != MDBX_SUCCESS)) {
log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
+ speculum = speculum_commited;
break;
}
+ speculum_commited = speculum;
txn_nops = 0;
+ if (!speculum_verify()) {
+ log_notice("downhill: bailout after delete-a, after commit");
+ break;
+ }
}
// удаляем вторую запись
log_trace("downhill: delete-b %" PRIu64, b_serial);
checkdata("downhill: delete-b", dbi, b_key->value, b_data->value);
- err = mdbx_del(txn_guard.get(), dbi, &b_key->value, &b_data->value);
+ err = remove(b_key, b_data);
if (unlikely(err != MDBX_SUCCESS)) {
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
log_notice("downhill: bailout at delete-b due '%s'",
mdbx_strerror(err));
txn_end(true);
+ speculum = speculum_commited;
break;
}
failure_perror("mdbx_del(b)", err);
}
+ if (!speculum_verify()) {
+ log_notice("downhill: bailout after delete-b, before commit");
+ break;
+ }
if (++txn_nops >= config.params.batch_write) {
err = breakable_restart();
if (unlikely(err != MDBX_SUCCESS)) {
log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
+ speculum = speculum_commited;
break;
}
+ speculum_commited = speculum;
txn_nops = 0;
+ if (!speculum_verify()) {
+ log_notice("downhill: bailout after delete-b, after commit");
+ goto bailout;
+ }
}
report(1);
}
+ rc = speculum_verify();
+bailout:
if (txn_guard) {
err = breakable_commit();
if (unlikely(err != MDBX_SUCCESS))
@@ -312,10 +400,10 @@ bool testcase_hill::run() {
err = breakable_commit();
if (unlikely(err != MDBX_SUCCESS)) {
log_notice("hill: bailout-clean due '%s'", mdbx_strerror(err));
- return true;
+ return rc;
}
} else
db_table_close(dbi);
}
- return true;
+ return rc;
}
diff --git a/libs/libmdbx/src/test/jitter.cc b/libs/libmdbx/src/test/jitter.cc
index 82d1d764ff..c7b8bccb51 100644
--- a/libs/libmdbx/src/test/jitter.cc
+++ b/libs/libmdbx/src/test/jitter.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -15,10 +15,26 @@
#include "test.h"
bool testcase_jitter::run() {
+ int err;
+ size_t upper_limit = config.params.size_upper;
+ if (upper_limit < 1)
+ upper_limit = config.params.size_now * 2;
+
while (should_continue()) {
jitter_delay();
db_open();
+ if (upper_limit < 1) {
+ MDBX_envinfo info;
+ err = mdbx_env_info_ex(db_guard.get(), txn_guard.get(), &info,
+ sizeof(info));
+ if (err)
+ failure_perror("mdbx_env_info_ex()", err);
+ upper_limit = (info.mi_geo.upper < INTPTR_MAX)
+ ? (intptr_t)info.mi_geo.upper
+ : INTPTR_MAX;
+ }
+
if (flipcoin()) {
jitter_delay();
txn_begin(true);
@@ -27,19 +43,29 @@ bool testcase_jitter::run() {
txn_end(flipcoin());
}
+ const bool coin4size = flipcoin();
jitter_delay();
txn_begin(mode_readonly());
jitter_delay();
if (!mode_readonly()) {
fetch_canary();
update_canary(1);
- /* TODO:
- * - db_setsize()
- * ...
- */
+ err = mdbx_env_set_geometry(
+ db_guard.get(), -1, -1,
+ coin4size ? upper_limit * 2 / 3 : upper_limit * 3 / 2, -1, -1, -1);
+ if (err != MDBX_SUCCESS && err != MDBX_RESULT_TRUE &&
+ err != MDBX_MAP_FULL && err != MDBX_TOO_LARGE)
+ failure_perror("mdbx_env_set_geometry-1", err);
}
txn_end(flipcoin());
+ err = mdbx_env_set_geometry(
+ db_guard.get(), -1, -1,
+ !coin4size ? upper_limit * 2 / 3 : upper_limit * 3 / 2, -1, -1, -1);
+ if (err != MDBX_SUCCESS && err != MDBX_RESULT_TRUE &&
+ err != MDBX_MAP_FULL && err != MDBX_TOO_LARGE)
+ failure_perror("mdbx_env_set_geometry-2", err);
+
if (flipcoin()) {
jitter_delay();
txn_begin(true);
@@ -48,6 +74,12 @@ bool testcase_jitter::run() {
}
jitter_delay();
+ err =
+ mdbx_env_set_geometry(db_guard.get(), -1, -1, upper_limit, -1, -1, -1);
+ if (err != MDBX_SUCCESS && err != MDBX_RESULT_TRUE &&
+ err != MDBX_MAP_FULL && err != MDBX_TOO_LARGE)
+ failure_perror("mdbx_env_set_geometry-3", err);
+
db_close();
/* just 'align' nops with other tests with batching */
diff --git a/libs/libmdbx/src/test/keygen.cc b/libs/libmdbx/src/test/keygen.cc
index 0110b049bf..c2098e6ef9 100644
--- a/libs/libmdbx/src/test/keygen.cc
+++ b/libs/libmdbx/src/test/keygen.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -72,7 +72,7 @@ serial_t injective(const serial_t serial,
}
void __hot maker::pair(serial_t serial, const buffer &key, buffer &value,
- serial_t value_age) {
+ serial_t value_age, const bool keylen_changeable) {
assert(mapping.width >= serial_minwith && mapping.width <= serial_maxwith);
assert(mapping.split <= mapping.width);
assert(mapping.mesh <= mapping.width);
@@ -131,14 +131,65 @@ void __hot maker::pair(serial_t serial, const buffer &key, buffer &value,
log_trace("keygen-pair: key %" PRIu64 ", value %" PRIu64, key_serial,
value_serial);
- mk(key_serial, key_essentials, *key);
- mk(value_serial, value_essentials, *value);
+ mk_begin(key_serial, key_essentials, *key);
+ mk_begin(value_serial, value_essentials, *value);
+
+#if 0 /* unused for now */
+ if (key->value.iov_len + value->value.iov_len > pair_maxlen) {
+ unsigned extra = key->value.iov_len + value->value.iov_len - pair_maxlen;
+ if (keylen_changeable &&
+ key->value.iov_len > std::max(8u, (unsigned)key_essentials.minlen)) {
+#if defined(__GNUC__) || defined(__clang__)
+ const bool coin = __builtin_parityll(serial) != 0;
+#else
+ const bool coin = INT64_C(0xF2CEECA9989BD96A) * int64_t(serial) < 0;
+#endif
+ if (coin) {
+ const unsigned gap =
+ key->value.iov_len - std::max(8u, (unsigned)key_essentials.minlen);
+ const unsigned chop = std::min(gap, extra);
+ log_trace("keygen-pair: chop %u key-len %u -> %u", chop,
+ (unsigned)key->value.iov_len,
+ (unsigned)key->value.iov_len - chop);
+ key->value.iov_len -= chop;
+ extra -= chop;
+ }
+ }
+ if (extra && value->value.iov_len >
+ std::max(8u, (unsigned)value_essentials.minlen)) {
+ const unsigned gap = value->value.iov_len -
+ std::max(8u, (unsigned)value_essentials.minlen);
+ const unsigned chop = std::min(gap, extra);
+ log_trace("keygen-pair: chop %u value-len %u -> %u", chop,
+ (unsigned)value->value.iov_len,
+ (unsigned)value->value.iov_len - chop);
+ value->value.iov_len -= chop;
+ extra -= chop;
+ }
+ if (keylen_changeable && extra &&
+ key->value.iov_len > std::max(8u, (unsigned)key_essentials.minlen)) {
+ const unsigned gap =
+ key->value.iov_len - std::max(8u, (unsigned)key_essentials.minlen);
+ const unsigned chop = std::min(gap, extra);
+ log_trace("keygen-pair: chop %u key-len %u -> %u", chop,
+ (unsigned)key->value.iov_len,
+ (unsigned)key->value.iov_len - chop);
+ key->value.iov_len -= chop;
+ extra -= chop;
+ }
+ }
+#else
+ (void)keylen_changeable;
+#endif /* unused for now */
+
+ mk_continue(key_serial, key_essentials, *key);
+ mk_continue(value_serial, value_essentials, *value);
if (log_enabled(logging::trace)) {
- char dump_key[128], dump_value[128];
+ char dump_key[4096], dump_value[4096];
log_trace("keygen-pair: key %s, value %s",
- mdbx_dkey(&key->value, dump_key, sizeof(dump_key)),
- mdbx_dkey(&value->value, dump_value, sizeof(dump_value)));
+ mdbx_dump_val(&key->value, dump_key, sizeof(dump_key)),
+ mdbx_dump_val(&value->value, dump_value, sizeof(dump_value)));
}
}
@@ -146,19 +197,22 @@ void maker::setup(const config::actor_params_pod &actor, unsigned actor_id,
unsigned thread_number) {
key_essentials.flags =
actor.table_flags & (MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT);
- assert(actor.keylen_min <= UINT8_MAX);
- key_essentials.minlen = (uint8_t)actor.keylen_min;
- assert(actor.keylen_max <= UINT16_MAX);
- key_essentials.maxlen = (uint16_t)actor.keylen_max;
+ assert(actor.keylen_min <= UINT16_MAX);
+ key_essentials.minlen = (uint16_t)actor.keylen_min;
+ assert(actor.keylen_max <= UINT32_MAX);
+ key_essentials.maxlen = std::min(
+ (uint32_t)actor.keylen_max,
+ (uint32_t)mdbx_limits_keysize_max(actor.pagesize, key_essentials.flags));
value_essentials.flags =
actor.table_flags & (MDBX_INTEGERDUP | MDBX_REVERSEDUP);
- assert(actor.datalen_min <= UINT8_MAX);
- value_essentials.minlen = (uint8_t)actor.datalen_min;
- assert(actor.datalen_max <= UINT16_MAX);
- value_essentials.maxlen = (uint16_t)actor.datalen_max;
+ assert(actor.datalen_min <= UINT16_MAX);
+ value_essentials.minlen = (uint16_t)actor.datalen_min;
+ assert(actor.datalen_max <= UINT32_MAX);
+ value_essentials.maxlen = std::min(
+ (uint32_t)actor.datalen_max,
+ (uint32_t)mdbx_limits_valsize_max(actor.pagesize, key_essentials.flags));
- assert(thread_number < 2);
(void)thread_number;
mapping = actor.keygen;
salt = (actor.keygen.seed + actor_id) * UINT64_C(14653293970879851569);
@@ -226,18 +280,25 @@ buffer alloc(size_t limit) {
return buffer(ptr);
}
-void __hot maker::mk(const serial_t serial, const essentials &params,
- result &out) {
+void __hot maker::mk_begin(const serial_t serial, const essentials &params,
+ result &out) {
assert(out.limit >= params.maxlen);
assert(params.maxlen >= params.minlen);
assert(params.maxlen >= length(serial));
- out.value.iov_base = out.bytes;
out.value.iov_len =
(params.maxlen > params.minlen)
? params.minlen + serial % (params.maxlen - params.minlen)
: params.minlen;
+ if ((params.flags & (MDBX_INTEGERKEY | MDBX_INTEGERDUP)) == 0 &&
+ out.value.iov_len < 8)
+ out.value.iov_len = std::max(length(serial), out.value.iov_len);
+}
+
+void __hot maker::mk_continue(const serial_t serial, const essentials &params,
+ result &out) {
+ out.value.iov_base = out.bytes;
if (params.flags & (MDBX_INTEGERKEY | MDBX_INTEGERDUP)) {
assert(params.maxlen == params.minlen);
assert(params.minlen == 4 || params.minlen == 8);
@@ -251,17 +312,13 @@ void __hot maker::mk(const serial_t serial, const essentials &params,
unaligned::store(out.bytes + out.value.iov_len - 8, htobe64(serial));
} else {
out.u64 = htobe64(serial);
- if (out.value.iov_len < 8) {
- out.value.iov_len = std::max(length(serial), out.value.iov_len);
+ if (out.value.iov_len < 8)
out.value.iov_base = out.bytes + 8 - out.value.iov_len;
- }
}
} else {
out.u64 = htole64(serial);
if (out.value.iov_len > 8)
memset(out.bytes + 8, '\0', out.value.iov_len - 8);
- else
- out.value.iov_len = std::max(length(serial), out.value.iov_len);
}
assert(out.value.iov_len >= params.minlen);
diff --git a/libs/libmdbx/src/test/keygen.h b/libs/libmdbx/src/test/keygen.h
index d0299e1e7d..d25b88349f 100644
--- a/libs/libmdbx/src/test/keygen.h
+++ b/libs/libmdbx/src/test/keygen.h
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -93,7 +93,7 @@ struct result {
//-----------------------------------------------------------------------------
-struct buffer_deleter : public std::unary_function<void, result *> {
+struct buffer_deleter /* : public std::unary_function<void, result *> */ {
void operator()(result *buffer) const { free(buffer); }
};
@@ -107,18 +107,25 @@ class maker {
serial_t salt;
struct essentials {
- uint8_t minlen;
- uint8_t flags;
- uint16_t maxlen;
+ uint16_t minlen;
+ uint16_t flags;
+ uint32_t maxlen;
} key_essentials, value_essentials;
- static void mk(const serial_t serial, const essentials &params, result &out);
+ static void mk_begin(const serial_t serial, const essentials &params,
+ result &out);
+ static void mk_continue(const serial_t serial, const essentials &params,
+ result &out);
+ static void mk(const serial_t serial, const essentials &params, result &out) {
+ mk_begin(serial, params, out);
+ mk_continue(serial, params, out);
+ }
public:
maker() { memset(this, 0, sizeof(*this)); }
void pair(serial_t serial, const buffer &key, buffer &value,
- serial_t value_age);
+ serial_t value_age, const bool keylen_changeable);
void setup(const config::actor_params_pod &actor, unsigned actor_id,
unsigned thread_number);
void make_ordered();
diff --git a/libs/libmdbx/src/test/log.cc b/libs/libmdbx/src/test/log.cc
index 79544e11bb..2b22e28785 100644
--- a/libs/libmdbx/src/test/log.cc
+++ b/libs/libmdbx/src/test/log.cc
@@ -20,7 +20,7 @@ void failure(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
fflushall();
- logging::output(logging::failure, fmt, ap);
+ logging::output_nocheckloglevel_ap(logging::failure, fmt, ap);
va_end(ap);
fflushall();
exit(EXIT_FAILURE);
@@ -37,29 +37,18 @@ void __noreturn failure_perror(const char *what, int errnum) {
//-----------------------------------------------------------------------------
-static void mdbx_logger(int type, const char *function, int line,
+static void mdbx_logger(int priority, const char *function, int line,
const char *msg, va_list args) {
- logging::loglevel level = logging::info;
- if (type & MDBX_DBG_EXTRA)
- level = logging::extra;
- if (type & MDBX_DBG_TRACE)
- level = logging::trace;
- if (type & MDBX_DBG_PRINT)
- level = logging::verbose;
-
if (!function)
function = "unknown";
- if (type & MDBX_DBG_ASSERT) {
- log_error("mdbx: assertion failure: %s, %d", function, line);
- level = logging::failure;
- }
- if (logging::output(
- level,
- strncmp(function, "mdbx_", 5) == 0 ? "%s: " : "mdbx: %s: ", function))
- logging::feed_ap(msg, args);
- if (type & MDBX_DBG_ASSERT)
- abort();
+ if (priority == MDBX_LOG_FATAL)
+ log_error("mdbx: fatal failure: %s, %d", function, line);
+
+ logging::output_nocheckloglevel(
+ logging::loglevel(priority),
+ strncmp(function, "mdbx_", 5) == 0 ? "%s: " : "mdbx %s: ", function);
+ logging::feed_ap(msg, args);
}
namespace logging {
@@ -69,19 +58,16 @@ static std::string suffix;
static loglevel level;
static FILE *last;
-void setlevel(loglevel _level) {
- level = (_level > error) ? failure : _level;
- int mdbx_dbg_opts = MDBX_DBG_ASSERT | MDBX_DBG_JITTER | MDBX_DBG_DUMP;
- if (level <= trace)
- mdbx_dbg_opts |= MDBX_DBG_TRACE;
- if (level <= verbose)
- mdbx_dbg_opts |= MDBX_DBG_PRINT;
- int rc = mdbx_setup_debug(mdbx_dbg_opts, mdbx_logger);
+void setlevel(loglevel priority) {
+ level = priority;
+ int rc = mdbx_setup_debug(priority,
+ MDBX_DBG_ASSERT | MDBX_DBG_AUDIT | MDBX_DBG_JITTER,
+ mdbx_logger);
log_trace("set mdbx debug-opts: 0x%02x", rc);
}
-void setup(loglevel _level, const std::string &_prefix) {
- setlevel(_level);
+void setup(loglevel priority, const std::string &_prefix) {
+ setlevel(priority);
prefix = _prefix;
}
@@ -95,10 +81,10 @@ const char *level2str(const loglevel alevel) {
return "extra";
case trace:
return "trace";
+ case debug:
+ return "debug";
case verbose:
return "verbose";
- case info:
- return "info";
case notice:
return "notice";
case warning:
@@ -111,26 +97,28 @@ const char *level2str(const loglevel alevel) {
}
bool output(const loglevel priority, const char *format, ...) {
- if (priority < level)
+ if (lower(priority, level))
return false;
va_list ap;
va_start(ap, format);
- output(priority, format, ap);
+ output_nocheckloglevel_ap(priority, format, ap);
va_end(ap);
return true;
}
-bool output(const logging::loglevel priority, const char *format, va_list ap) {
+void output_nocheckloglevel_ap(const logging::loglevel priority,
+ const char *format, va_list ap) {
if (last) {
putc('\n', last);
fflush(last);
+ if (last == stderr) {
+ putc('\n', stdout);
+ fflush(stdout);
+ }
last = nullptr;
}
- if (priority < level)
- return false;
-
chrono::time now = chrono::now_realtime();
struct tm tm;
#ifdef _MSC_VER
@@ -144,14 +132,14 @@ bool output(const logging::loglevel priority, const char *format, va_list ap) {
last = stdout;
fprintf(last,
- "[ %02d%02d%02d-%02d:%02d:%02d.%06d_%05u %-10s %.4s ] %s" /* TODO */,
+ "[ %02d%02d%02d-%02d:%02d:%02d.%06d_%05lu %-10s %.4s ] %s" /* TODO */,
tm.tm_year - 100, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min,
- tm.tm_sec, chrono::fractional2us(now.fractional), osal_getpid(),
+ tm.tm_sec, chrono::fractional2us(now.fractional), (long)osal_getpid(),
prefix.c_str(), level2str(priority), suffix.c_str());
va_list ones;
memset(&ones, 0, sizeof(ones)) /* zap MSVC and other stupid compilers */;
- if (priority >= error)
+ if (same_or_higher(priority, error))
va_copy(ones, ap);
vfprintf(last, format, ap);
@@ -178,29 +166,36 @@ bool output(const logging::loglevel priority, const char *format, va_list ap) {
break;
}
- if (priority >= error) {
+ if (same_or_higher(priority, error)) {
if (last != stderr) {
- fprintf(stderr, "[ %05u %-10s %.4s ] %s", osal_getpid(), prefix.c_str(),
- level2str(priority), suffix.c_str());
+ fprintf(stderr, "[ %05lu %-10s %.4s ] %s", (long)osal_getpid(),
+ prefix.c_str(), level2str(priority), suffix.c_str());
vfprintf(stderr, format, ones);
- if (end != '\n')
- putc('\n', stderr);
- fflush(stderr);
+ if (end == '\n')
+ fflush(stderr);
+ else
+ last = stderr;
}
va_end(ones);
}
-
- return true;
}
bool feed_ap(const char *format, va_list ap) {
if (!last)
return false;
+ if (last == stderr) {
+ va_list ones;
+ va_copy(ones, ap);
+ vfprintf(stdout, format, ones);
+ va_end(ones);
+ }
vfprintf(last, format, ap);
size_t len = strlen(format);
if (len && format[len - 1] == '\n') {
fflush(last);
+ if (last == stderr)
+ fflush(stdout);
last = nullptr;
}
return true;
@@ -242,73 +237,123 @@ void local_suffix::pop() {
local_suffix::~local_suffix() { suffix.erase(trim_pos); }
+void progress_canary(bool active) {
+ static chrono::time progress_timestamp;
+ chrono::time now = chrono::now_motonic();
+
+ if (now.fixedpoint - progress_timestamp.fixedpoint <
+ chrono::from_ms(42).fixedpoint)
+ return;
+
+ if (osal_progress_push(active)) {
+ progress_timestamp = now;
+ return;
+ }
+
+ if (progress_timestamp.fixedpoint == 0) {
+ putc('>', stderr);
+ progress_timestamp = now;
+ } else if (global::config::console_mode) {
+ if (active) {
+ static int last_point = -1;
+ int point = (now.fixedpoint >> 29) & 3;
+ if (point != last_point) {
+ progress_timestamp = now;
+ fprintf(stderr, "%c\b", "-\\|/"[last_point = point]);
+ }
+ } else if (now.fixedpoint - progress_timestamp.fixedpoint >
+ chrono::from_seconds(2).fixedpoint) {
+ progress_timestamp = now;
+ fprintf(stderr, "%c\b", "@*"[now.utc & 1]);
+ }
+ } else {
+ static int count;
+ if (active && now.fixedpoint - progress_timestamp.fixedpoint >
+ chrono::from_seconds(1).fixedpoint) {
+ putc('.', stderr);
+ progress_timestamp = now;
+ ++count;
+ } else if (now.fixedpoint - progress_timestamp.fixedpoint >
+ chrono::from_seconds(5).fixedpoint) {
+ putc("@*"[now.utc & 1], stderr);
+ progress_timestamp = now;
+ ++count;
+ }
+ if (count == 60) {
+ count = 0;
+ putc('\n', stderr);
+ }
+ }
+ fflush(stderr);
+}
+
} // namespace logging
void log_extra(const char *msg, ...) {
- if (logging::extra >= logging::level) {
+ if (logging::same_or_higher(logging::extra, logging::level)) {
va_list ap;
va_start(ap, msg);
- logging::output(logging::extra, msg, ap);
+ logging::output_nocheckloglevel_ap(logging::extra, msg, ap);
va_end(ap);
} else
logging::last = nullptr;
}
void log_trace(const char *msg, ...) {
- if (logging::trace >= logging::level) {
+ if (logging::same_or_higher(logging::trace, logging::level)) {
va_list ap;
va_start(ap, msg);
- logging::output(logging::trace, msg, ap);
+ logging::output_nocheckloglevel_ap(logging::trace, msg, ap);
va_end(ap);
} else
logging::last = nullptr;
}
-void log_verbose(const char *msg, ...) {
- if (logging::verbose >= logging::level) {
+void log_debug(const char *msg, ...) {
+ if (logging::same_or_higher(logging::debug, logging::level)) {
va_list ap;
va_start(ap, msg);
- logging::output(logging::verbose, msg, ap);
+ logging::output_nocheckloglevel_ap(logging::debug, msg, ap);
va_end(ap);
} else
logging::last = nullptr;
}
-void log_info(const char *msg, ...) {
- if (logging::info >= logging::level) {
+void log_verbose(const char *msg, ...) {
+ if (logging::same_or_higher(logging::verbose, logging::level)) {
va_list ap;
va_start(ap, msg);
- logging::output(logging::info, msg, ap);
+ logging::output_nocheckloglevel_ap(logging::verbose, msg, ap);
va_end(ap);
} else
logging::last = nullptr;
}
void log_notice(const char *msg, ...) {
- if (logging::notice >= logging::level) {
+ if (logging::same_or_higher(logging::notice, logging::level)) {
va_list ap;
va_start(ap, msg);
- logging::output(logging::notice, msg, ap);
+ logging::output_nocheckloglevel_ap(logging::notice, msg, ap);
va_end(ap);
} else
logging::last = nullptr;
}
void log_warning(const char *msg, ...) {
- if (logging::warning >= logging::level) {
+ if (logging::same_or_higher(logging::warning, logging::level)) {
va_list ap;
va_start(ap, msg);
- logging::output(logging::warning, msg, ap);
+ logging::output_nocheckloglevel_ap(logging::warning, msg, ap);
va_end(ap);
} else
logging::last = nullptr;
}
void log_error(const char *msg, ...) {
- if (logging::error >= logging::level) {
+ if (logging::same_or_higher(logging::error, logging::level)) {
va_list ap;
va_start(ap, msg);
- logging::output(logging::error, msg, ap);
+ logging::output_nocheckloglevel_ap(logging::error, msg, ap);
va_end(ap);
} else
logging::last = nullptr;
@@ -319,7 +364,7 @@ void log_trouble(const char *where, const char *what, int errnum) {
}
bool log_enabled(const logging::loglevel priority) {
- return (priority >= logging::level);
+ return logging::same_or_higher(priority, logging::level);
}
void log_flush(void) { fflushall(); }
diff --git a/libs/libmdbx/src/test/log.h b/libs/libmdbx/src/test/log.h
index 7d6b4012f1..bb8f997b36 100644
--- a/libs/libmdbx/src/test/log.h
+++ b/libs/libmdbx/src/test/log.h
@@ -15,6 +15,7 @@
#pragma once
#include "base.h"
+#include "chrono.h"
void __noreturn usage(void);
void __noreturn __printf_args(1, 2) failure(const char *fmt, ...);
@@ -24,27 +25,47 @@ const char *test_strerror(int errnum);
namespace logging {
enum loglevel {
- extra,
- trace,
- verbose,
- info,
- notice,
- warning,
- error,
- failure,
+ extra = MDBX_LOG_EXTRA,
+ trace = MDBX_LOG_TRACE,
+ debug = MDBX_LOG_DEBUG,
+ verbose = MDBX_LOG_VERBOSE,
+ notice = MDBX_LOG_NOTICE,
+ warning = MDBX_LOG_WARN,
+ error = MDBX_LOG_ERROR,
+ failure = MDBX_LOG_FATAL
};
+inline bool lower(loglevel left, loglevel right) {
+ static_assert(MDBX_LOG_EXTRA > MDBX_LOG_FATAL, "WTF?");
+ return left > right;
+}
+
+inline bool same_or_higher(loglevel left, loglevel right) {
+ return left <= right;
+}
+
const char *level2str(const loglevel level);
-void setup(loglevel level, const std::string &prefix);
+void setup(loglevel priority, const std::string &prefix);
void setup(const std::string &prefix);
-void setlevel(loglevel level);
+void setlevel(loglevel priority);
-bool output(const loglevel priority, const char *format, va_list ap);
+void output_nocheckloglevel_ap(const loglevel priority, const char *format,
+ va_list ap);
bool __printf_args(2, 3)
output(const loglevel priority, const char *format, ...);
bool feed_ap(const char *format, va_list ap);
bool __printf_args(1, 2) feed(const char *format, ...);
+void inline __printf_args(2, 3)
+ output_nocheckloglevel(const loglevel priority, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ output_nocheckloglevel_ap(priority, format, ap);
+ va_end(ap);
+}
+
+void progress_canary(bool active);
+
class local_suffix {
protected:
size_t trim_pos;
@@ -66,8 +87,8 @@ public:
void __printf_args(1, 2) log_extra(const char *msg, ...);
void __printf_args(1, 2) log_trace(const char *msg, ...);
+void __printf_args(1, 2) log_debug(const char *msg, ...);
void __printf_args(1, 2) log_verbose(const char *msg, ...);
-void __printf_args(1, 2) log_info(const char *msg, ...);
void __printf_args(1, 2) log_notice(const char *msg, ...);
void __printf_args(1, 2) log_warning(const char *msg, ...);
void __printf_args(1, 2) log_error(const char *msg, ...);
diff --git a/libs/libmdbx/src/test/long_stochastic.sh b/libs/libmdbx/src/test/long_stochastic.sh
index 954d18268a..58ec6d5c6e 100644
--- a/libs/libmdbx/src/test/long_stochastic.sh
+++ b/libs/libmdbx/src/test/long_stochastic.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
if ! which make cc c++ tee lz4 >/dev/null; then
echo "Please install the following prerequisites: make cc c++ tee lz4" >&2
exit 1
@@ -7,19 +7,42 @@ fi
set -euo pipefail
UNAME="$(uname -s 2>/dev/null || echo Unknown)"
+
+## NOTE: Valgrind could produce some false-positive warnings
+## in multi-process environment with shared memory.
+## For instance, when the process "A" explicitly marks a memory
+## region as "undefined", the process "B" fill it,
+## and after this process "A" read such region, etc.
+#VALGRIND="valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt"
+
+###############################################################################
+# 1. clean data from prev runs and examine available RAM
+
+if [[ -v VALGRIND && ! -z "$VALGRIND" ]]; then
+ rm -f valgrind-*.log
+else
+ VALGRIND=time
+fi
+
+WANNA_MOUNT=0
case ${UNAME} in
Linux)
MAKE=make
if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
+ for old_test_dir in $(ls -d /dev/shm/mdbx-test.[0-9]*); do
+ rm -rf $old_test_dir
+ done
TESTDB_DIR="/dev/shm/mdbx-test.$$"
fi
mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/*
+
if LC_ALL=C free | grep -q -i available; then
ram_avail_mb=$(($(LC_ALL=C free | grep -i Mem: | tr -s [:blank:] ' ' | cut -d ' ' -f 7) / 1024))
else
ram_avail_mb=$(($(LC_ALL=C free | grep -i Mem: | tr -s [:blank:] ' ' | cut -d ' ' -f 4) / 1024))
fi
;;
+
FreeBSD)
MAKE=gmake
if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
@@ -27,18 +50,46 @@ case ${UNAME} in
umount $old_test_dir && rm -r $old_test_dir
done
TESTDB_DIR="/tmp/mdbx-test.$$"
- rm -rf $TESTDB_DIR && mkdir -p $TESTDB_DIR && mount -t tmpfs tmpfs $TESTDB_DIR
+ rm -rf $TESTDB_DIR && mkdir -p $TESTDB_DIR
+ WANNA_MOUNT=1
else
mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/*
fi
+
ram_avail_mb=$(($(LC_ALL=C vmstat -s | grep -ie '[0-9] pages free$' | cut -d p -f 1) * ($(LC_ALL=C vmstat -s | grep -ie '[0-9] bytes per page$' | cut -d b -f 1) / 1024) / 1024))
;;
+
+ Darwin)
+ MAKE=make
+ if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
+ for vol in $(ls -d /Volumes/mdx[0-9]*[0-9]tst); do
+ disk=$(mount | grep $vol | cut -d ' ' -f 1)
+ echo "umount: volume $vol disk $disk"
+ hdiutil unmount $vol -force
+ hdiutil detach $disk
+ done
+ TESTDB_DIR="/Volumes/mdx$$tst"
+ WANNA_MOUNT=1
+ else
+ mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/*
+ fi
+
+ pagesize=$(($(LC_ALL=C vm_stat | grep -o 'page size of [0-9]\+ bytes' | cut -d' ' -f 4) / 1024))
+ freepages=$(LC_ALL=C vm_stat | grep '^Pages free:' | grep -o '[0-9]\+\.$' | cut -d'.' -f 1)
+ ram_avail_mb=$((pagesize * freepages / 1024))
+ echo "pagesize ${pagesize}K, freepages ${freepages}, ram_avail_mb ${ram_avail_mb}"
+
+ ;;
+
*)
echo "FIXME: ${UNAME} not supported by this script"
exit 2
;;
esac
+###############################################################################
+# 2. estimate reasonable RAM space for test-db
+
echo "=== ${ram_avail_mb}M RAM available"
ram_reserve4logs_mb=1234
if [ $ram_avail_mb -lt $ram_reserve4logs_mb ]; then
@@ -70,16 +121,47 @@ if [ $db_size_mb -gt 3072 ]; then
fi
echo "=== use ${db_size_mb}M for DB"
-${MAKE} TESTDB=${TESTDB_DIR}/smoke.db TESTLOG=${TESTDB_DIR}/smoke.log check
+###############################################################################
+# 3. Create test-directory in ramfs/tmpfs, i.e. create/format/mount if required
+case ${UNAME} in
+ Linux)
+ ;;
+
+ FreeBSD)
+ if [[ WANNA_MOUNT ]]; then
+ mount -t tmpfs tmpfs $TESTDB_DIR
+ fi
+ ;;
+
+ Darwin)
+ if [[ WANNA_MOUNT ]]; then
+ ramdisk_size_mb=$((42 + db_size_mb * 2 + ram_reserve4logs_mb))
+ number_of_sectors=$((ramdisk_size_mb * 2048))
+ ramdev=$(hdiutil attach -nomount ram://${number_of_sectors})
+ diskutil erasevolume ExFAT "mdx$$tst" ${ramdev}
+ fi
+ ;;
+
+ *)
+ echo "FIXME: ${UNAME} not supported by this script"
+ exit 2
+ ;;
+esac
+
+###############################################################################
+# 4. Run basic test, i.e. `make check`
+
+${MAKE} TEST_DB=${TESTDB_DIR}/smoke.db TEST_LOG=${TESTDB_DIR}/smoke.log check
rm -f ${TESTDB_DIR}/*
###############################################################################
+# 5. run stochastic iterations
function rep9 { printf "%*s" $1 '' | tr ' ' '9'; }
function join { local IFS="$1"; shift; echo "$*"; }
function bit2option { local -n arr=$1; (( ($2&(1<<$3)) != 0 )) && echo -n '+' || echo -n '-'; echo "${arr[$3]}"; }
-options=(writemap coalesce lifo)
+options=(writemap coalesce lifo notls)
function bits2list {
local -n arr=$1
@@ -95,13 +177,13 @@ function probe {
echo "=============================================== $(date)"
echo "${caption}: $*"
rm -f ${TESTDB_DIR}/* \
- && ./mdbx_test --ignore-dbfull --repeat=42 --pathname=${TESTDB_DIR}/long.db "$@" | lz4 > ${TESTDB_DIR}/long.log.lz4 \
- && ./mdbx_chk -nvvv ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \
- && ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ./mdbx_chk -nvvv ${TESTDB_DIR}/long.db-copy | tee ${TESTDB_DIR}/long-chk-copy.log) \
+ && ${VALGRIND} ./mdbx_test --ignore-dbfull --repeat=42 --pathname=${TESTDB_DIR}/long.db "$@" | lz4 > ${TESTDB_DIR}/long.log.lz4 \
+ && ${VALGRIND} ./mdbx_chk -nvvv ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \
+ && ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ${VALGRIND} ./mdbx_chk -nvvv ${TESTDB_DIR}/long.db-copy | tee ${TESTDB_DIR}/long-chk-copy.log) \
|| (echo "FAILED"; exit 1)
}
-###############################################################################
+#------------------------------------------------------------------------------
count=0
for nops in $(seq 2 6); do
diff --git a/libs/libmdbx/src/test/main.cc b/libs/libmdbx/src/test/main.cc
index 959359a515..c00837fa72 100644
--- a/libs/libmdbx/src/test/main.cc
+++ b/libs/libmdbx/src/test/main.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -14,9 +14,100 @@
#include "test.h"
+#if !(defined(_WIN32) || defined(_WIN64))
+#include <sys/resource.h>
+#include <sys/time.h>
+#endif /* !Windows */
+
void __noreturn usage(void) {
- printf("usage:\n"
- "\tFIXME\n");
+ puts(
+ "usage:\n"
+ " --help or -h Show this text\n"
+ "Common parameters:\n"
+ " --pathname=... Path and/or name of database files\n"
+ " --repeat=N Set repeat counter\n"
+ " --threads=N Number of thread (unsunpported for now)\n"
+ " --timeout=N[s|m|h|d] Set timeout in seconds/minutes/hours/days\n"
+ " --failfast[=YES/no] Lill all actors on first failure/error\n"
+ " --max-readers=N See mdbx_env_set_maxreaders() description\n"
+ " --max-tables=N Se mdbx_env_set_maxdbs() description\n"
+ " --dump-config[=YES/no] Dump entire test config before run\n"
+ " --progress[=YES/no] Enable/disable progress `canary`\n"
+ " --console[=yes/no] Enable/disable console-like output\n"
+ " --cleanup-before[=YES/no] Cleanup/remove and re-create database\n"
+ " --cleanup-after[=YES/no] Cleanup/remove database after completion\n"
+ "Database size control:\n"
+ " --pagesize=... Database page size: min, max, 256..65536\n"
+ " --size-lower=N[K|M|G|T] Lower-bound of size in Kb/Mb/Gb/Tb\n"
+ " --size-upper Upper-bound of size in Kb/Mb/Gb/Tb\n"
+ " --size Initial size in Kb/Mb/Gb/Tb\n"
+ " --shrink-threshold Shrink threshold in Kb/Mb/Gb/Tb\n"
+ " --growth-step Grow step in Kb/Mb/Gb/Tb\n"
+ "Predefined complext scenarios/cases:\n"
+ " --case=... Only `basic` scenario implemented for now\n"
+ " basic == Simultaneous multi-process execution\n"
+ " of test-actors: nested,hill,ttl,copy,append,jitter,try\n"
+ "Test actors:\n"
+ " --hill Fill-up and empty-down\n"
+ " by CRUD-operation quads\n"
+ " --ttl Stochastic time-to-live simulation\n"
+ " --nested Nested transactionы\n"
+ " with stochastic-size bellows\n"
+ " --jitter Jitter/delays simulation\n"
+ " --try Try write-transaction, no more\n"
+ " --copy Online copy/backup\n"
+ " --append Append-mode insertions\n"
+ " --dead.reader Dead-reader simulator\n"
+ " --dead.writer Dead-writer simulator\n"
+ "Actor options:\n"
+ " --batch.read=N Read-operations batch size\n"
+ " --batch.write=N Write-operations batch size\n"
+ " --delay=N | --no-delay (no)Delay test-actor before start\n"
+ " --wait4ops=N | --no-wait4ops (no)Wait for previous test-actor\n"
+ " completes # ops before start\n"
+ " --duration=N[s|m|h|d] Define running duration\n"
+ " --nops=N[K|M|G|T] Define number of operations/steps\n"
+ " --inject-writefault[=yes|NO] TBD (see the source code)\n"
+ " --drop[=yes|NO] Drop key-value space/table on "
+ "completion\n"
+ " --ignore-dbfull[=yes|NO] Ignore MDBX_MAP_FULL error\n"
+ " --speculum[=yes|NO] Use internal `speculum` to check "
+ "dataset\n"
+ "Keys and Value:\n"
+ " --keylen.min=N Minimal keys length\n"
+ " --keylen.max=N Miximal keys length\n"
+ " --datalen.min=N Minimal data length\n"
+ " --datalen.max=N Miximal data length\n"
+ " --keygen.width=N TBD (see the source code)\n"
+ " --keygen.mesh=N TBD (see the source code)\n"
+ " --keygen.seed=N TBD (see the source code)\n"
+ " --keygen.split=N TBD (see the source code)\n"
+ " --keygen.rotate=N TBD (see the source code)\n"
+ " --keygen.offset=N TBD (see the source code)\n"
+ " --keygen.case=random Generator case (only `random` for now)\n"
+ "Database operation mode:\n"
+ " --mode={[+-]FLAG}[,[+-]FLAG]...\n"
+ " nosubdir == MDBX_NOSUBDIR\n"
+ " rdonly == MDBX_RDONLY\n"
+ " nometasync == MDBX_NOMETASYNC\n"
+ " lifo == MDBX_LIFORECLAIM\n"
+ " coalesce == MDBX_COALESCE\n"
+ " nosync-safe == MDBX_SAFE_NOSYNC\n"
+ " writemap == MDBX_WRITEMAP\n"
+ " mapasync == MDBX_MAPASYNC\n"
+ " nosync-utterly == MDBX_UTTERLY_NOSYNC\n"
+ " perturb == MDBX_PAGEPERTURB\n"
+ " notls == MDBX_NOTLS\n"
+ " nordahead == MDBX_NORDAHEAD\n"
+ " nomeminit == MDBX_NOMEMINIT\n"
+ "Key-value space/table options:\n"
+ " --table={[+-]FLAG}[,[+-]FLAG]...\n"
+ " key.reverse == MDBX_REVERSEKEY\n"
+ " key.integer == MDBX_INTEGERKEY\n"
+ " data.dups == MDBX_DUPSORT\n"
+ " data.integer == MDBX_INTEGERDUP | MDBX_DUPFIXED | MDBX_DUPSORT\n"
+ " data.fixed == MDBX_DUPFIXED | MDBX_DUPSORT\n"
+ " data.reverse == MDBX_REVERSEDUP | MDBX_DUPSORT\n");
exit(EXIT_FAILURE);
}
@@ -25,17 +116,15 @@ void __noreturn usage(void) {
void actor_params::set_defaults(const std::string &tmpdir) {
pathname_log = "";
loglevel =
-#ifdef NDEBUG
- logging::info;
-#elif defined(_WIN32) || defined(_WIN64)
+#if defined(NDEBUG) || defined(_WIN32) || defined(_WIN64)
logging::verbose;
#else
logging::trace;
#endif
pathname_db = tmpdir + "mdbx-test.db";
- mode_flags = MDBX_NOSUBDIR | MDBX_WRITEMAP | MDBX_MAPASYNC | MDBX_NORDAHEAD |
- MDBX_NOMEMINIT | MDBX_COALESCE | MDBX_LIFORECLAIM;
+ mode_flags = MDBX_NOSUBDIR | MDBX_WRITEMAP | MDBX_MAPASYNC | MDBX_NOMEMINIT |
+ MDBX_COALESCE | MDBX_LIFORECLAIM | MDBX_ACCEDE;
table_flags = MDBX_DUPSORT;
size_lower = -1;
@@ -73,6 +162,7 @@ void actor_params::set_defaults(const std::string &tmpdir) {
drop_table = false;
ignore_dbfull = false;
+ speculum = false;
max_readers = 42;
max_tables = 42;
@@ -82,7 +172,8 @@ void actor_params::set_defaults(const std::string &tmpdir) {
global::config::cleanup_before = true;
global::config::cleanup_after = true;
global::config::failfast = true;
- global::config::progress_indicator = osal_istty(STDERR_FILENO);
+ global::config::progress_indicator = true;
+ global::config::console_mode = osal_istty(STDERR_FILENO);
}
namespace global {
@@ -103,6 +194,7 @@ bool cleanup_before;
bool cleanup_after;
bool failfast;
bool progress_indicator;
+bool console_mode;
} /* namespace config */
} /* namespace global */
@@ -130,7 +222,7 @@ int main(int argc, char *const argv[]) {
#endif /* _DEBUG */
if (argc < 2)
- failure("No parameters given\n");
+ failure("No parameters given. Try --help\n");
if (argc == 2 && strncmp(argv[1], global::thunk_param_prefix,
strlen(global::thunk_param_prefix)) == 0)
@@ -139,6 +231,10 @@ int main(int argc, char *const argv[]) {
? EXIT_SUCCESS
: EXIT_FAILURE;
+ if (argc == 2 &&
+ (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0))
+ usage();
+
actor_params params;
params.set_defaults(osal_tempdir());
global::config::dump_config = true;
@@ -168,8 +264,8 @@ int main(int argc, char *const argv[]) {
}
if (config::parse_option(argc, argv, narg, "pagesize", params.pagesize,
- mdbx_limits_pgsize_min(),
- mdbx_limits_pgsize_max())) {
+ int(mdbx_limits_pgsize_min()),
+ int(mdbx_limits_pgsize_max()))) {
const unsigned keylen_max = params.mdbx_keylen_max();
if (params.keylen_min > keylen_max)
params.keylen_min = keylen_max;
@@ -182,6 +278,17 @@ int main(int argc, char *const argv[]) {
params.datalen_max = datalen_max;
continue;
}
+ if (config::parse_option(argc, argv, narg, "repeat", params.nrepeat,
+ config::no_scale))
+ continue;
+ if (config::parse_option(argc, argv, narg, "threads", params.nthreads,
+ config::no_scale, 1, 64))
+ continue;
+ if (config::parse_option(argc, argv, narg, "timeout",
+ global::config::timeout_duration_seconds,
+ config::duration, 1))
+ continue;
+
if (config::parse_option_intptr(argc, argv, narg, "size-lower",
params.size_lower,
mdbx_limits_dbsize_min(params.pagesize),
@@ -231,17 +338,6 @@ int main(int argc, char *const argv[]) {
keycase_setup(value, params);
continue;
}
-
- if (config::parse_option(argc, argv, narg, "repeat", params.nrepeat,
- config::no_scale))
- continue;
- if (config::parse_option(argc, argv, narg, "threads", params.nthreads,
- config::no_scale, 1, 64))
- continue;
- if (config::parse_option(argc, argv, narg, "timeout",
- global::config::timeout_duration_seconds,
- config::duration, 1))
- continue;
if (config::parse_option(argc, argv, narg, "keylen.min", params.keylen_min,
config::no_scale, params.mdbx_keylen_min(),
params.mdbx_keylen_max())) {
@@ -296,6 +392,8 @@ int main(int argc, char *const argv[]) {
if (config::parse_option(argc, argv, narg, "ignore-dbfull",
params.ignore_dbfull))
continue;
+ if (config::parse_option(argc, argv, narg, "speculum", params.speculum))
+ continue;
if (config::parse_option(argc, argv, narg, "dump-config",
global::config::dump_config))
continue;
@@ -316,7 +414,7 @@ int main(int argc, char *const argv[]) {
params.delaystart = 0;
continue;
}
- if (config::parse_option(argc, argv, narg, "no-wait", nullptr)) {
+ if (config::parse_option(argc, argv, narg, "no-wait4ops", nullptr)) {
params.waitfor_nops = 0;
continue;
}
@@ -346,6 +444,10 @@ int main(int argc, char *const argv[]) {
configure_actor(last_space_id, ac_deadwrite, value, params);
continue;
}
+ if (config::parse_option(argc, argv, narg, "try", nullptr)) {
+ configure_actor(last_space_id, ac_try, value, params);
+ continue;
+ }
if (config::parse_option(argc, argv, narg, "copy", nullptr)) {
configure_actor(last_space_id, ac_copy, value, params);
continue;
@@ -358,17 +460,24 @@ int main(int argc, char *const argv[]) {
configure_actor(last_space_id, ac_ttl, value, params);
continue;
}
+ if (config::parse_option(argc, argv, narg, "nested", nullptr)) {
+ configure_actor(last_space_id, ac_nested, value, params);
+ continue;
+ }
if (config::parse_option(argc, argv, narg, "failfast",
global::config::failfast))
continue;
if (config::parse_option(argc, argv, narg, "progress",
global::config::progress_indicator))
continue;
+ if (config::parse_option(argc, argv, narg, "console",
+ global::config::console_mode))
+ continue;
if (*argv[narg] != '-')
testcase_setup(argv[narg], params, last_space_id);
else
- failure("Unknown option '%s'\n", argv[narg]);
+ failure("Unknown option '%s'. Try --help\n", argv[narg]);
}
if (global::config::dump_config)
@@ -456,8 +565,8 @@ int main(int argc, char *const argv[]) {
if (!actor)
continue;
- log_info("actor #%u, id %d, pid %u: %s\n", actor->actor_id,
- actor->space_id, pid, status2str(status));
+ log_verbose("actor #%u, id %d, pid %ld: %s\n", actor->actor_id,
+ actor->space_id, (long)pid, status2str(status));
if (status > as_running) {
left -= 1;
if (status != as_successful) {
@@ -480,9 +589,37 @@ int main(int argc, char *const argv[]) {
log_notice("RESULT: %s\n", failed ? "Failed" : "Successful");
if (global::config::cleanup_before) {
if (failed)
- log_info("skip cleanup");
+ log_verbose("skip cleanup");
else
cleanup();
}
+
+#if !(defined(_WIN32) || defined(_WIN64))
+ struct rusage spent;
+ if (!getrusage(global::singlemode ? RUSAGE_SELF : RUSAGE_CHILDREN, &spent)) {
+ log_notice("%6s: user %f, system %f", "CPU",
+ spent.ru_utime.tv_sec + spent.ru_utime.tv_usec * 1e-6,
+ spent.ru_stime.tv_sec + spent.ru_stime.tv_usec * 1e-6);
+#if defined(__linux__) || defined(__gnu_linux__) || defined(__FreeBSD__) || \
+ defined(__NetBSD__) || defined(__OpenBSD__) || defined(__BSD__) || \
+ defined(__bsdi__) || defined(__DragonFly__) || defined(__APPLE__) || \
+ defined(__MACH__) || defined(__sun)
+ log_notice("%6s: read %ld, write %ld", "IOPs", spent.ru_inblock,
+ spent.ru_oublock);
+ if (spent.ru_maxrss > 0)
+ log_notice("%6s: %ld Kb", "RAM",
+ spent.ru_maxrss
+#if defined(__sun)
+ * getpagesize() / 1024u
+#elif defined(__APPLE__)
+ / 1024u
+#endif
+ );
+ log_notice("%6s: reclaims %ld, faults %ld, swaps %ld", "Paging",
+ spent.ru_minflt, spent.ru_majflt, spent.ru_nswap);
+#endif /* Linux */
+ }
+#endif /* !Windows */
+
return failed ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/libs/libmdbx/src/test/osal-unix.cc b/libs/libmdbx/src/test/osal-unix.cc
index 0157bace23..4b8694c708 100644
--- a/libs/libmdbx/src/test/osal-unix.cc
+++ b/libs/libmdbx/src/test/osal-unix.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -21,112 +21,266 @@
#include <sys/wait.h>
#include <unistd.h>
-#ifdef __APPLE__
+#ifndef MDBX_LOCKING
+#error "Opps, MDBX_LOCKING is undefined!"
+#endif
+
+#if defined(__APPLE__) && (MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \
+ MDBX_LOCKING == MDBX_LOCKING_POSIX2008)
#include "darwin/pthread_barrier.c"
+#endif /* __APPLE__ && MDBX_LOCKING >= MDBX_LOCKING_POSIX2001 */
+
+#if MDBX_LOCKING == MDBX_LOCKING_SYSV
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#endif /* MDBX_LOCKING == MDBX_LOCKING_SYSV */
+
+#if MDBX_LOCKING == MDBX_LOCKING_POSIX1988
+#include <semaphore.h>
+
+#if __cplusplus >= 201103L
+#include <atomic>
+static __inline __maybe_unused int atomic_decrement(std::atomic_int *p) {
+ return std::atomic_fetch_sub(p, 1) - 1;
+}
+#else
+static __inline __maybe_unused int atomic_decrement(volatile int *p) {
+#if defined(__GNUC__) || defined(__clang__)
+ return __sync_sub_and_fetch(p, 1);
+#elif defined(_MSC_VER)
+ STATIC_ASSERT(sizeof(volatile long) == sizeof(volatile int));
+ return _InterlockedDecrement((volatile long *)p);
+#elif defined(__APPLE__)
+ return OSAtomicDecrement32Barrier((volatile int *)p);
+#else
+#error FIXME: Unsupported compiler
#endif
+}
+#endif /* C++11 */
+#endif /* MDBX_LOCKING == MDBX_LOCKING_POSIX1988 */
+
+#if MDBX_LOCKING == MDBX_LOCKING_SYSV
+static int ipc;
+static pid_t ipc_overlord_pid;
+static void ipc_remove(void) {
+ if (ipc_overlord_pid == getpid())
+ semctl(ipc, 0, IPC_RMID, nullptr);
+}
+#else
struct shared_t {
+#if MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \
+ MDBX_LOCKING == MDBX_LOCKING_POSIX2008
pthread_barrier_t barrier;
pthread_mutex_t mutex;
- size_t conds_size;
- pthread_cond_t conds[1];
+ size_t count;
+ pthread_cond_t events[1];
+#elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988
+ struct {
+#if __cplusplus >= 201103L
+ std::atomic_int countdown;
+#else
+ volatile int countdown;
+#endif /* C++11 */
+ sem_t sema;
+ } barrier;
+ size_t count;
+ sem_t events[1];
+#else
+#error "FIXME"
+#endif /* MDBX_LOCKING */
};
-
static shared_t *shared;
+#endif /* MDBX_LOCKING != MDBX_LOCKING_SYSV */
void osal_wait4barrier(void) {
+#if MDBX_LOCKING == MDBX_LOCKING_SYSV
+ struct sembuf op;
+ op.sem_num = 0;
+ op.sem_op = -1;
+ op.sem_flg = IPC_NOWAIT;
+ if (semop(ipc, &op, 1))
+ failure_perror("semop(dec)", errno);
+ op.sem_op = 0;
+ op.sem_flg = 0;
+ if (semop(ipc, &op, 1))
+ failure_perror("semop(wait)", errno);
+#elif MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \
+ MDBX_LOCKING == MDBX_LOCKING_POSIX2008
assert(shared != nullptr && shared != MAP_FAILED);
- int rc = pthread_barrier_wait(&shared->barrier);
- if (rc != 0 && rc != PTHREAD_BARRIER_SERIAL_THREAD) {
- failure_perror("pthread_barrier_wait(shared)", rc);
- }
+ int err = pthread_barrier_wait(&shared->barrier);
+ if (err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD)
+ failure_perror("pthread_barrier_wait(shared)", err);
+#elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988
+ assert(shared != nullptr && shared != MAP_FAILED);
+ int err = (atomic_decrement(&shared->barrier.countdown) > 0 &&
+ sem_wait(&shared->barrier.sema))
+ ? errno
+ : 0;
+ if (err != 0)
+ failure_perror("sem_wait(shared)", err);
+ if (sem_post(&shared->barrier.sema))
+ failure_perror("sem_post(shared)", errno);
+#else
+#error "FIXME"
+#endif /* MDBX_LOCKING */
}
void osal_setup(const std::vector<actor_config> &actors) {
+#if MDBX_LOCKING == MDBX_LOCKING_SYSV
+ if (ipc_overlord_pid)
+ failure("ipc already created by %ld pid", (long)ipc_overlord_pid);
+ ipc_overlord_pid = getpid();
+#ifndef SEM_A
+#define SEM_A S_IRUSR
+#endif
+#ifndef SEM_R
+#define SEM_R S_IWUSR
+#endif
+ ipc = semget(IPC_PRIVATE, actors.size() + 2, IPC_CREAT | SEM_A | SEM_R);
+ if (ipc < 0)
+ failure_perror("semget(IPC_PRIVATE, shared_sems)", errno);
+ if (atexit(ipc_remove))
+ failure_perror("atexit(ipc_remove)", errno);
+ if (semctl(ipc, 0, SETVAL, (int)(actors.size() + 1)))
+ failure_perror("semctl(SETVAL.0, shared_sems)", errno);
+ for (size_t i = 1; i < actors.size() + 2; ++i)
+ if (semctl(ipc, i, SETVAL, 1))
+ failure_perror("semctl(SETVAL.N, shared_sems)", errno);
+#else
assert(shared == nullptr);
-
- pthread_mutexattr_t mutexattr;
- int rc = pthread_mutexattr_init(&mutexattr);
- if (rc)
- failure_perror("pthread_mutexattr_init()", rc);
- rc = pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);
- if (rc)
- failure_perror("pthread_mutexattr_setpshared()", rc);
-
- pthread_barrierattr_t barrierattr;
- rc = pthread_barrierattr_init(&barrierattr);
- if (rc)
- failure_perror("pthread_barrierattr_init()", rc);
- rc = pthread_barrierattr_setpshared(&barrierattr, PTHREAD_PROCESS_SHARED);
- if (rc)
- failure_perror("pthread_barrierattr_setpshared()", rc);
-
- pthread_condattr_t condattr;
- rc = pthread_condattr_init(&condattr);
- if (rc)
- failure_perror("pthread_condattr_init()", rc);
- rc = pthread_condattr_setpshared(&condattr, PTHREAD_PROCESS_SHARED);
- if (rc)
- failure_perror("pthread_condattr_setpshared()", rc);
-
shared = (shared_t *)mmap(
- nullptr, sizeof(shared_t) + actors.size() * sizeof(pthread_cond_t),
- PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ nullptr, sizeof(shared_t) + actors.size() * sizeof(shared->events[0]),
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS
+#ifdef MAP_HASSEMAPHORE
+ | MAP_HASSEMAPHORE
+#endif
+ ,
+ -1, 0);
if (MAP_FAILED == (void *)shared)
failure_perror("mmap(shared_conds)", errno);
- rc = pthread_mutex_init(&shared->mutex, &mutexattr);
- if (rc)
- failure_perror("pthread_mutex_init(shared)", rc);
+ shared->count = actors.size() + 1;
- rc = pthread_barrier_init(&shared->barrier, &barrierattr, actors.size() + 1);
- if (rc)
- failure_perror("pthread_barrier_init(shared)", rc);
+#if MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \
+ MDBX_LOCKING == MDBX_LOCKING_POSIX2008
+ pthread_barrierattr_t barrierattr;
+ int err = pthread_barrierattr_init(&barrierattr);
+ if (err)
+ failure_perror("pthread_barrierattr_init()", err);
+ err = pthread_barrierattr_setpshared(&barrierattr, PTHREAD_PROCESS_SHARED);
+ if (err)
+ failure_perror("pthread_barrierattr_setpshared()", err);
+
+ err = pthread_barrier_init(&shared->barrier, &barrierattr, shared->count);
+ if (err)
+ failure_perror("pthread_barrier_init(shared)", err);
+ pthread_barrierattr_destroy(&barrierattr);
- const size_t n = actors.size() + 1;
- for (size_t i = 0; i < n; ++i) {
- pthread_cond_t *event = &shared->conds[i];
- rc = pthread_cond_init(event, &condattr);
- if (rc)
- failure_perror("pthread_cond_init(shared)", rc);
+ pthread_mutexattr_t mutexattr;
+ err = pthread_mutexattr_init(&mutexattr);
+ if (err)
+ failure_perror("pthread_mutexattr_init()", err);
+ err = pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);
+ if (err)
+ failure_perror("pthread_mutexattr_setpshared()", err);
+
+ pthread_condattr_t condattr;
+ err = pthread_condattr_init(&condattr);
+ if (err)
+ failure_perror("pthread_condattr_init()", err);
+ err = pthread_condattr_setpshared(&condattr, PTHREAD_PROCESS_SHARED);
+ if (err)
+ failure_perror("pthread_condattr_setpshared()", err);
+
+ err = pthread_mutex_init(&shared->mutex, &mutexattr);
+ if (err)
+ failure_perror("pthread_mutex_init(shared)", err);
+
+ for (size_t i = 0; i < shared->count; ++i) {
+ pthread_cond_t *event = &shared->events[i];
+ err = pthread_cond_init(event, &condattr);
+ if (err)
+ failure_perror("pthread_cond_init(shared)", err);
log_trace("osal_setup: event(shared pthread_cond) %" PRIuPTR " -> %p", i,
- event);
+ __Wpedantic_format_voidptr(event));
}
- shared->conds_size = actors.size() + 1;
-
- pthread_barrierattr_destroy(&barrierattr);
pthread_condattr_destroy(&condattr);
pthread_mutexattr_destroy(&mutexattr);
+#elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988
+ shared->barrier.countdown = shared->count;
+ if (sem_init(&shared->barrier.sema, true, 1))
+ failure_perror("sem_init(shared.barrier)", errno);
+ for (size_t i = 0; i < shared->count; ++i) {
+ sem_t *event = &shared->events[i];
+ if (sem_init(event, true, 0))
+ failure_perror("sem_init(shared.event)", errno);
+ log_trace("osal_setup: event(shared sem_init) %" PRIuPTR " -> %p", i,
+ __Wpedantic_format_voidptr(event));
+ }
+#else
+#error "FIXME"
+#endif /* MDBX_LOCKING */
+#endif /* MDBX_LOCKING != MDBX_LOCKING_SYSV */
}
void osal_broadcast(unsigned id) {
- assert(shared != nullptr && shared != MAP_FAILED);
log_trace("osal_broadcast: event %u", id);
- if (id >= shared->conds_size)
+#if MDBX_LOCKING == MDBX_LOCKING_SYSV
+ if (semctl(ipc, id + 1, SETVAL, 0))
+ failure_perror("semctl(SETVAL)", errno);
+#else
+ assert(shared != nullptr && shared != MAP_FAILED);
+ if (id >= shared->count)
failure("osal_broadcast: id > limit");
- int rc = pthread_cond_broadcast(shared->conds + id);
- if (rc)
- failure_perror("sem_post(shared)", rc);
+#if MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \
+ MDBX_LOCKING == MDBX_LOCKING_POSIX2008
+ int err = pthread_cond_broadcast(shared->events + id);
+ if (err)
+ failure_perror("pthread_cond_broadcast(shared)", err);
+#elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988
+ if (sem_post(shared->events + id))
+ failure_perror("sem_post(shared)", errno);
+#else
+#error "FIXME"
+#endif /* MDBX_LOCKING */
+#endif /* MDBX_LOCKING != MDBX_LOCKING_SYSV */
}
int osal_waitfor(unsigned id) {
- assert(shared != nullptr && shared != MAP_FAILED);
-
log_trace("osal_waitfor: event %u", id);
- if (id >= shared->conds_size)
+#if MDBX_LOCKING == MDBX_LOCKING_SYSV
+ struct sembuf op;
+ memset(&op, 0, sizeof(op));
+ op.sem_num = (short)(id + 1);
+ int rc = semop(ipc, &op, 1) ? errno : MDBX_SUCCESS;
+#else
+ assert(shared != nullptr && shared != MAP_FAILED);
+ if (id >= shared->count)
failure("osal_waitfor: id > limit");
+#if MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \
+ MDBX_LOCKING == MDBX_LOCKING_POSIX2008
int rc = pthread_mutex_lock(&shared->mutex);
if (rc != 0)
failure_perror("pthread_mutex_lock(shared)", rc);
- rc = pthread_cond_wait(shared->conds + id, &shared->mutex);
+ rc = pthread_cond_wait(shared->events + id, &shared->mutex);
if (rc && rc != EINTR)
failure_perror("pthread_cond_wait(shared)", rc);
rc = pthread_mutex_unlock(&shared->mutex);
if (rc != 0)
failure_perror("pthread_mutex_unlock(shared)", rc);
+#elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988
+ int rc = sem_wait(shared->events + id) ? errno : 0;
+ if (rc == 0 && sem_post(shared->events + id))
+ failure_perror("sem_post(shared)", errno);
+#else
+#error "FIXME"
+#endif /* MDBX_LOCKING */
+#endif /* MDBX_LOCKING != MDBX_LOCKING_SYSV */
return (rc == 0) ? true : false;
}
@@ -149,21 +303,69 @@ bool actor_config::osal_deserialize(const char *str, const char *end,
//-----------------------------------------------------------------------------
+static pid_t overlord_pid;
+
+static volatile sig_atomic_t sigusr1_head, sigusr2_head;
+static void handler_SIGUSR(int signum) {
+ switch (signum) {
+ case SIGUSR1:
+ sigusr1_head += 1;
+ return;
+ case SIGUSR2:
+ sigusr2_head += 1;
+ return;
+ default:
+ abort();
+ }
+}
+
+bool osal_progress_push(bool active) {
+ if (overlord_pid) {
+ if (kill(overlord_pid, active ? SIGUSR1 : SIGUSR2))
+ failure_perror("osal_progress_push: kill(overload)", errno);
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+
static std::unordered_map<pid_t, actor_status> childs;
-static void handler_SIGCHLD(int unused) { (void)unused; }
+static volatile sig_atomic_t sigalarm_head;
+static void handler_SIGCHLD(int signum) {
+ if (signum == SIGALRM)
+ sigalarm_head += 1;
+}
mdbx_pid_t osal_getpid(void) { return getpid(); }
int osal_delay(unsigned seconds) { return sleep(seconds) ? errno : 0; }
int osal_actor_start(const actor_config &config, mdbx_pid_t &pid) {
- if (childs.empty())
- signal(SIGCHLD, handler_SIGCHLD);
+ if (childs.empty()) {
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = handler_SIGCHLD;
+ sigaction(SIGCHLD, &act, nullptr);
+ sigaction(SIGALRM, &act, nullptr);
+ act.sa_handler = handler_SIGUSR;
+ sigaction(SIGUSR1, &act, nullptr);
+ sigaction(SIGUSR2, &act, nullptr);
+
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ sigaddset(&mask, SIGUSR1);
+ sigaddset(&mask, SIGUSR2);
+ sigprocmask(SIG_UNBLOCK, &mask, nullptr);
+ }
pid = fork();
if (pid == 0) {
+ overlord_pid = getppid();
const bool result = test_execute(config);
exit(result ? EXIT_SUCCESS : EXIT_FAILURE);
}
@@ -171,7 +373,8 @@ int osal_actor_start(const actor_config &config, mdbx_pid_t &pid) {
if (pid < 0)
return errno;
- log_trace("osal_actor_start: fork pid %i for %u", pid, config.actor_id);
+ log_trace("osal_actor_start: fork pid %ld for %u", (long)pid,
+ config.actor_id);
childs[pid] = as_running;
return 0;
}
@@ -186,60 +389,67 @@ void osal_killall_actors(void) {
}
int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) {
- struct timespec ts;
- ts.tv_nsec = 0;
- ts.tv_sec = (timeout > INT_MAX) ? INT_MAX : timeout;
-retry:
- int status, options = WNOHANG;
+ static sig_atomic_t sigalarm_tail;
+ alarm(0) /* cancel prev timeout */;
+ sigalarm_tail = sigalarm_head /* reset timeout flag */;
+
+ int options = WNOHANG;
+ if (timeout) {
+ alarm((timeout > INT_MAX) ? INT_MAX : timeout);
+ options = 0;
+ }
+
#ifdef WUNTRACED
options |= WUNTRACED;
#endif
#ifdef WCONTINUED
options |= WCONTINUED;
#endif
- pid = waitpid(0, &status, options);
-
- if (pid > 0) {
- if (WIFEXITED(status))
- childs[pid] =
- (WEXITSTATUS(status) == EXIT_SUCCESS) ? as_successful : as_failed;
- else if (WCOREDUMP(status))
- childs[pid] = as_coredump;
- else if (WIFSIGNALED(status))
- childs[pid] = as_killed;
- else if (WIFSTOPPED(status))
- childs[pid] = as_debuging;
- else if (WIFCONTINUED(status))
- childs[pid] = as_running;
- else {
- assert(false);
- }
- return 0;
- }
- if (pid == 0) {
- /* child still running */
- if (ts.tv_sec == 0 && ts.tv_nsec == 0)
- ts.tv_nsec = 1;
- if (nanosleep(&ts, &ts) == 0) {
- /* timeout and no signal from child */
- pid = 0;
+ while (sigalarm_tail == sigalarm_head) {
+ int status;
+ pid = waitpid(0, &status, options);
+
+ if (pid > 0) {
+ if (WIFEXITED(status))
+ childs[pid] =
+ (WEXITSTATUS(status) == EXIT_SUCCESS) ? as_successful : as_failed;
+ else if (WCOREDUMP(status))
+ childs[pid] = as_coredump;
+ else if (WIFSIGNALED(status))
+ childs[pid] = as_killed;
+ else if (WIFSTOPPED(status))
+ childs[pid] = as_debugging;
+ else if (WIFCONTINUED(status))
+ childs[pid] = as_running;
+ else {
+ assert(false);
+ }
return 0;
}
- if (errno == EINTR)
- goto retry;
- }
- switch (errno) {
- case EINTR:
- pid = 0;
- return 0;
+ static sig_atomic_t sigusr1_tail, sigusr2_tail;
+ if (sigusr1_tail != sigusr1_head) {
+ sigusr1_tail = sigusr1_head;
+ logging::progress_canary(true);
+ if (pid < 0 && errno == EINTR)
+ continue;
+ }
+ if (sigusr2_tail != sigusr2_head) {
+ sigusr2_tail = sigusr2_head;
+ logging::progress_canary(false);
+ if (pid < 0 && errno == EINTR)
+ continue;
+ }
- case ECHILD:
- default:
- pid = 0;
- return errno;
+ if (pid == 0)
+ break;
+
+ int err = errno;
+ if (err != EINTR)
+ return err;
}
+ return 0 /* timeout */;
}
void osal_yield(void) {
@@ -254,9 +464,17 @@ void osal_udelay(unsigned us) {
static unsigned threshold_us;
if (threshold_us == 0) {
+#if defined(_POSIX_CPUTIME) && _POSIX_CPUTIME > -1 && \
+ defined(CLOCK_PROCESS_CPUTIME_ID)
if (clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts)) {
int rc = errno;
- failure_perror("clock_getres(CLOCK_PROCESS_CPUTIME_ID)", rc);
+ log_warning("clock_getres(CLOCK_PROCESS_CPUTIME_ID), failed errno %d",
+ rc);
+ }
+#endif /* CLOCK_PROCESS_CPUTIME_ID */
+ if (threshold_us == 0 && clock_getres(CLOCK_MONOTONIC, &ts)) {
+ int rc = errno;
+ failure_perror("clock_getres(CLOCK_MONOTONIC)", rc);
}
chrono::time threshold = chrono::from_timespec(ts);
assert(threshold.seconds() == 0);
diff --git a/libs/libmdbx/src/test/osal-windows.cc b/libs/libmdbx/src/test/osal-windows.cc
index 975d8268f5..6de674651b 100644
--- a/libs/libmdbx/src/test/osal-windows.cc
+++ b/libs/libmdbx/src/test/osal-windows.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -16,6 +16,7 @@
static std::unordered_map<unsigned, HANDLE> events;
static HANDLE hBarrierSemaphore, hBarrierEvent;
+static HANDLE hProgressActiveEvent, hProgressPassiveEvent;
static int waitstatus2errcode(DWORD result) {
switch (result) {
@@ -85,6 +86,16 @@ void osal_setup(const std::vector<actor_config> &actors) {
if (!hBarrierEvent)
failure_perror("CreateEvent(BarrierEvent)", GetLastError());
hBarrierEvent = make_inheritable(hBarrierEvent);
+
+ hProgressActiveEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!hProgressActiveEvent)
+ failure_perror("CreateEvent(ProgressActiveEvent)", GetLastError());
+ hProgressActiveEvent = make_inheritable(hProgressActiveEvent);
+
+ hProgressPassiveEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!hProgressPassiveEvent)
+ failure_perror("CreateEvent(ProgressPassiveEvent)", GetLastError());
+ hProgressPassiveEvent = make_inheritable(hProgressPassiveEvent);
}
void osal_broadcast(unsigned id) {
@@ -112,6 +123,8 @@ const std::string
actor_config::osal_serialize(simple_checksum &checksum) const {
checksum.push(hBarrierSemaphore);
checksum.push(hBarrierEvent);
+ checksum.push(hProgressActiveEvent);
+ checksum.push(hProgressPassiveEvent);
HANDLE hWait = INVALID_HANDLE_VALUE;
if (wait4id) {
@@ -125,8 +138,8 @@ actor_config::osal_serialize(simple_checksum &checksum) const {
checksum.push(hSignal);
}
- return format("%p.%p.%p.%p", hBarrierSemaphore, hBarrierEvent, hWait,
- hSignal);
+ return format("%p.%p.%p.%p.%p.%p", hBarrierSemaphore, hBarrierEvent, hWait,
+ hSignal, hProgressActiveEvent, hProgressPassiveEvent);
}
bool actor_config::osal_deserialize(const char *str, const char *end,
@@ -137,17 +150,22 @@ bool actor_config::osal_deserialize(const char *str, const char *end,
assert(hBarrierSemaphore == 0);
assert(hBarrierEvent == 0);
+ assert(hProgressActiveEvent == 0);
+ assert(hProgressPassiveEvent == 0);
assert(events.empty());
HANDLE hWait, hSignal;
- if (sscanf_s(copy.c_str(), "%p.%p.%p.%p", &hBarrierSemaphore, &hBarrierEvent,
- &hWait, &hSignal) != 4) {
+ if (sscanf_s(copy.c_str(), "%p.%p.%p.%p.%p.%p", &hBarrierSemaphore,
+ &hBarrierEvent, &hWait, &hSignal, &hProgressActiveEvent,
+ &hProgressPassiveEvent) != 6) {
TRACE("<< osal_deserialize: failed\n");
return false;
}
checksum.push(hBarrierSemaphore);
checksum.push(hBarrierEvent);
+ checksum.push(hProgressActiveEvent);
+ checksum.push(hProgressPassiveEvent);
if (wait4id) {
checksum.push(hWait);
@@ -168,6 +186,17 @@ bool actor_config::osal_deserialize(const char *str, const char *end,
typedef std::pair<HANDLE, actor_status> child;
static std::unordered_map<mdbx_pid_t, child> childs;
+bool osal_progress_push(bool active) {
+ if (!childs.empty()) {
+ if (!SetEvent(active ? hProgressActiveEvent : hProgressPassiveEvent))
+ failure_perror("osal_progress_push: SetEvent(overlord.progress)",
+ GetLastError());
+ return true;
+ }
+
+ return false;
+}
+
static void ArgvQuote(std::string &CommandLine, const std::string &Argument,
bool Force = false)
@@ -314,7 +343,7 @@ actor_status osal_actor_info(const mdbx_pid_t pid) {
break;
case EXCEPTION_BREAKPOINT:
case EXCEPTION_SINGLE_STEP:
- status = as_debuging;
+ status = as_debugging;
break;
case STATUS_CONTROL_C_EXIT:
status = as_killed;
@@ -344,32 +373,45 @@ void osal_killall_actors(void) {
int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) {
std::vector<HANDLE> handles;
- handles.reserve(childs.size());
+ handles.reserve(childs.size() + 2);
+ handles.push_back(hProgressActiveEvent);
+ handles.push_back(hProgressPassiveEvent);
for (const auto &pair : childs)
if (pair.second.second <= as_running)
handles.push_back(pair.second.first);
- DWORD rc =
- MsgWaitForMultipleObjectsEx((DWORD)handles.size(), &handles[0],
- (timeout > 60) ? 60 * 1000 : timeout * 1000,
- QS_ALLINPUT | QS_ALLPOSTMESSAGE, 0);
+ while (true) {
+ DWORD rc =
+ MsgWaitForMultipleObjectsEx((DWORD)handles.size(), &handles[0],
+ (timeout > 60) ? 60 * 1000 : timeout * 1000,
+ QS_ALLINPUT | QS_ALLPOSTMESSAGE, 0);
- if (rc >= WAIT_OBJECT_0 && rc < WAIT_OBJECT_0 + handles.size()) {
- pid = 0;
- for (const auto &pair : childs)
- if (pair.second.first == handles[rc - WAIT_OBJECT_0]) {
- pid = pair.first;
- break;
- }
- return 0;
- }
+ if (rc == WAIT_OBJECT_0) {
+ logging::progress_canary(true);
+ continue;
+ }
+ if (rc == WAIT_OBJECT_0 + 1) {
+ logging::progress_canary(false);
+ continue;
+ }
- if (rc == WAIT_TIMEOUT) {
- pid = 0;
- return 0;
- }
+ if (rc >= WAIT_OBJECT_0 + 2 && rc < WAIT_OBJECT_0 + handles.size()) {
+ pid = 0;
+ for (const auto &pair : childs)
+ if (pair.second.first == handles[rc - WAIT_OBJECT_0]) {
+ pid = pair.first;
+ break;
+ }
+ return 0;
+ }
- return waitstatus2errcode(rc);
+ if (rc == WAIT_TIMEOUT) {
+ pid = 0;
+ return 0;
+ }
+
+ return waitstatus2errcode(rc);
+ }
}
void osal_yield(void) { SwitchToThread(); }
diff --git a/libs/libmdbx/src/test/osal.h b/libs/libmdbx/src/test/osal.h
index 5acf7ad094..6d0e1c4eb6 100644
--- a/libs/libmdbx/src/test/osal.h
+++ b/libs/libmdbx/src/test/osal.h
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -26,6 +26,8 @@ void osal_killall_actors(void);
int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout);
void osal_wait4barrier(void);
+bool osal_progress_push(bool active);
+
mdbx_pid_t osal_getpid(void);
int osal_delay(unsigned seconds);
void osal_udelay(unsigned us);
diff --git a/libs/libmdbx/src/test/pcrf/CMakeLists.txt b/libs/libmdbx/src/test/pcrf/CMakeLists.txt
index 399c33f88f..8bd3e3d859 100644
--- a/libs/libmdbx/src/test/pcrf/CMakeLists.txt
+++ b/libs/libmdbx/src/test/pcrf/CMakeLists.txt
@@ -1,7 +1,5 @@
set(TARGET pcrf_test)
-project(${TARGET})
-
add_executable(${TARGET} pcrf_test.c)
-
+target_include_directories(${TARGET} PRIVATE "${PROJECT_SOURCE_DIR}")
target_link_libraries(${TARGET} mdbx)
diff --git a/libs/libmdbx/src/test/pcrf/pcrf_test.c b/libs/libmdbx/src/test/pcrf/pcrf_test.c
index 213c8b1d80..206bd21d4e 100644
--- a/libs/libmdbx/src/test/pcrf/pcrf_test.c
+++ b/libs/libmdbx/src/test/pcrf/pcrf_test.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2017 Leonid Yuriev <leo@yuriev.ru>.
+ * Copyright 2016-2019 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015 Vladimir Romanov
* <https://www.linkedin.com/in/vladimirromanov>, Yota Lab.
*
@@ -36,7 +36,7 @@
(int)((addr) >> 24), (int)((addr) >> 16 & 0xff), (int)((addr) >> 8 & 0xff), \
(int)((addr)&0xff)
-char opt_db_path[PATH_MAX] = "/root/lmdbx_bench2";
+char opt_db_path[PATH_MAX] = "./mdbx_bench2";
static MDBX_env *env;
#define REC_COUNT 10240000
int64_t ids[REC_COUNT * 10];
@@ -105,12 +105,12 @@ static void db_connect() {
MDBX_dbi dbi_ip;
MDBX_CHECK(mdbx_env_create(&env));
- MDBX_CHECK(
- mdbx_env_set_mapsize(env, REC_COUNT * sizeof(session_data_t) * 10));
+ MDBX_CHECK(mdbx_env_set_geometry(
+ env, 0, 0, REC_COUNT * sizeof(session_data_t) * 10, -1, -1, -1));
MDBX_CHECK(mdbx_env_set_maxdbs(env, 30));
MDBX_CHECK(mdbx_env_open(env, opt_db_path,
MDBX_CREATE | MDBX_WRITEMAP | MDBX_MAPASYNC |
- MDBX_NOSYNC | MDBX_LIFORECLAIM,
+ MDBX_SAFE_NOSYNC | MDBX_LIFORECLAIM,
0664));
MDBX_txn *txn;
@@ -126,7 +126,7 @@ static void db_connect() {
printf("Connection open\n");
}
-static void create_record(int64_t record_id) {
+static void create_record(uint64_t record_id) {
MDBX_dbi dbi_session;
MDBX_dbi dbi_session_id;
MDBX_dbi dbi_event;
@@ -136,11 +136,13 @@ static void create_record(int64_t record_id) {
session_data_t data;
// transaction init
snprintf(data.session_id1, sizeof(data.session_id1),
- "prefix%02ld_%02ld.fill.fill.fill.fill.fill.fill;%ld",
- record_id % 3 + 1, record_id % 9 + 1, record_id);
+ "prefix%02u_%02u.fill.fill.fill.fill.fill.fill;%" PRIu64,
+ (unsigned)(record_id % 3) + 1, (unsigned)(record_id % 9) + 1,
+ record_id);
snprintf(data.session_id2, sizeof(data.session_id2),
- "dprefix%ld;%ld.fill.fill.;suffix", record_id,
- record_id % 1000000000 + 99999);
+ "dprefix%" PRIu64 ";%" PRIu64 ".fill.fill.;suffix", record_id,
+ (record_id + UINT64_C(1442695040888963407)) %
+ UINT64_C(6364136223846793005));
snprintf(data.ip, sizeof(data.ip), "%d.%d.%d.%d",
IP_PRINTF_ARG_HOST(record_id & 0xFFFFFFFF));
event.obj_id = record_id;
@@ -241,8 +243,9 @@ static void get_db_stat(const char *db, int64_t *ms_branch_pages,
MDBX_CHECK(mdbx_dbi_open(txn, db, MDBX_CREATE, &dbi));
MDBX_CHECK(mdbx_dbi_stat(txn, dbi, &stat, sizeof(stat)));
mdbx_txn_abort(txn);
- printf("%15s | %15ld | %5u | %10ld | %10ld | %11ld |\n", db,
- stat.ms_branch_pages, stat.ms_depth, stat.ms_entries,
+ printf("%15s | %15" PRIu64 " | %5u | %10" PRIu64 " | %10" PRIu64
+ " | %11" PRIu64 " |\n",
+ db, stat.ms_branch_pages, stat.ms_depth, stat.ms_entries,
stat.ms_leaf_pages, stat.ms_overflow_pages);
(*ms_branch_pages) += stat.ms_branch_pages;
(*ms_leaf_pages) += stat.ms_leaf_pages;
@@ -253,8 +256,8 @@ static void periodic_stat(void) {
int64_t ms_leaf_pages = 0;
MDBX_stat mst;
MDBX_envinfo mei;
- MDBX_CHECK(mdbx_env_stat(env, &mst, sizeof(mst)));
- MDBX_CHECK(mdbx_env_info(env, &mei, sizeof(mei)));
+ MDBX_CHECK(mdbx_env_stat_ex(env, NULL, &mst, sizeof(mst)));
+ MDBX_CHECK(mdbx_env_info_ex(env, NULL, &mei, sizeof(mei)));
printf("Environment Info\n");
printf(" Pagesize: %u\n", mst.ms_psize);
if (mei.mi_geo.lower != mei.mi_geo.upper) {
@@ -287,8 +290,8 @@ static void periodic_stat(void) {
get_db_stat("session_id", &ms_branch_pages, &ms_leaf_pages);
get_db_stat("event", &ms_branch_pages, &ms_leaf_pages);
get_db_stat("ip", &ms_branch_pages, &ms_leaf_pages);
- printf("%15s | %15ld | %5s | %10s | %10ld | %11s |\n", "", ms_branch_pages,
- "", "", ms_leaf_pages, "");
+ printf("%15s | %15" PRIu64 " | %5s | %10s | %10" PRIu64 " | %11s |\n", "",
+ ms_branch_pages, "", "", ms_leaf_pages, "");
static int64_t prev_add_count;
static int64_t prev_del_count;
@@ -297,26 +300,32 @@ static void periodic_stat(void) {
static int64_t t = -1;
if (t > 0) {
int64_t delta = (getClockUs() - t);
- printf(
- "CPS: add %ld, delete %ld, items processed - %ldK data=%ldK key=%ldK\n",
- (mdbx_add_count - prev_add_count) * 1000000 / delta,
- (mdbx_del_count - prev_del_count) * 1000000 / delta, obj_id / 1024,
- mdbx_data_size / 1024, mdbx_key_size / 1024);
- printf("usage data=%ld%%", ((mdbx_data_size + mdbx_key_size) * 100) /
- ((ms_leaf_pages + ms_branch_pages) * 4096));
+ printf("CPS: add %" PRIu64 ", delete %" PRIu64
+ ", items processed - %" PRIu64 "K data=%" PRIu64 "K key=%" PRIu64
+ "K\n",
+ (mdbx_add_count - prev_add_count) * 1000000 / delta,
+ (mdbx_del_count - prev_del_count) * 1000000 / delta, obj_id / 1024,
+ mdbx_data_size / 1024, mdbx_key_size / 1024);
+ printf("usage data=%" PRIu64 "%%",
+ ((mdbx_data_size + mdbx_key_size) * 100) /
+ ((ms_leaf_pages + ms_branch_pages) * 4096));
if (prev_add_time != mdbx_add_time) {
- printf(" Add : %ld c/s", (mdbx_add_count - prev_add_count) * 1000000 /
- (mdbx_add_time - prev_add_time));
+ printf(" Add : %" PRIu64 " c/s", (mdbx_add_count - prev_add_count) *
+ 1000000 /
+ (mdbx_add_time - prev_add_time));
}
if (prev_del_time != mdbx_del_time) {
- printf(" Del : %ld c/s", (mdbx_del_count - prev_del_count) * 1000000 /
- (mdbx_del_time - prev_del_time));
+ printf(" Del : %" PRIu64 " c/s", (mdbx_del_count - prev_del_count) *
+ 1000000 /
+ (mdbx_del_time - prev_del_time));
}
if (mdbx_add_time) {
- printf(" tAdd : %ld c/s", mdbx_add_count * 1000000 / mdbx_add_time);
+ printf(" tAdd : %" PRIu64 " c/s",
+ mdbx_add_count * 1000000 / mdbx_add_time);
}
if (mdbx_del_time) {
- printf(" tDel : %ld c/s", mdbx_del_count * 1000000 / mdbx_del_time);
+ printf(" tDel : %" PRIu64 " c/s",
+ mdbx_del_count * 1000000 / mdbx_del_time);
}
puts("");
}
@@ -385,14 +394,14 @@ int main(int argc, char **argv) {
id = get_id_from_pool();
delete_record(id);
}
- // for (i = 0; i < 50; i++) {
- // int64_t id = obj_id++;
- // create_record(id);
- // add_id_to_pool(id);
- // }
- // int64_t id = obj_id++;
- // create_record(id);
- // add_id_to_pool(id);
+ // for (i = 0; i < 50; i++) {
+ // int64_t id = obj_id++;
+ // create_record(id);
+ // add_id_to_pool(id);
+ // }
+ // int64_t id = obj_id++;
+ // create_record(id);
+ // add_id_to_pool(id);
int64_t now = getClockUs();
if ((now - t) > 10000000L) {
periodic_stat();
diff --git a/libs/libmdbx/src/test/test.cc b/libs/libmdbx/src/test/test.cc
index e9e925e9a9..ff1676274b 100644
--- a/libs/libmdbx/src/test/test.cc
+++ b/libs/libmdbx/src/test/test.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -37,6 +37,8 @@ const char *testcase2str(const actor_testcase testcase) {
return "append";
case ac_ttl:
return "ttl";
+ case ac_nested:
+ return "nested";
}
}
@@ -45,8 +47,8 @@ const char *status2str(actor_status status) {
default:
assert(false);
return "?!";
- case as_debuging:
- return "debuging";
+ case as_debugging:
+ return "debugging";
case as_running:
return "running";
case as_successful:
@@ -76,15 +78,16 @@ const char *keygencase2str(const keygen_case keycase) {
//-----------------------------------------------------------------------------
-int testcase::oom_callback(MDBX_env *env, int pid, mdbx_tid_t tid, uint64_t txn,
- unsigned gap, int retry) {
+int testcase::oom_callback(MDBX_env *env, mdbx_pid_t pid, mdbx_tid_t tid,
+ uint64_t txn, unsigned gap, size_t space,
+ int retry) {
testcase *self = (testcase *)mdbx_env_get_userctx(env);
if (retry == 0)
- log_notice("oom_callback: waitfor pid %u, thread %" PRIuPTR
- ", txn #%" PRIu64 ", gap %d",
- pid, (size_t)tid, txn, gap);
+ log_notice("oom_callback: waitfor pid %lu, thread %" PRIuPTR
+ ", txn #%" PRIu64 ", gap %d, scape %zu",
+ (long)pid, (size_t)tid, txn, gap, space);
if (self->should_continue(true)) {
osal_yield();
@@ -186,7 +189,8 @@ int testcase::breakable_commit() {
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
rc = err;
err = mdbx_txn_abort(txn);
- if (unlikely(err != MDBX_SUCCESS && err != MDBX_THREAD_MISMATCH))
+ if (unlikely(err != MDBX_SUCCESS && err != MDBX_THREAD_MISMATCH &&
+ err != MDBX_BAD_TXN))
failure_perror("mdbx_txn_abort()", err);
} else
failure_perror("mdbx_txn_commit()", err);
@@ -196,6 +200,20 @@ int testcase::breakable_commit() {
return rc;
}
+unsigned testcase::txn_underutilization_x256(MDBX_txn *txn) const {
+ if (txn) {
+ MDBX_txn_info info;
+ int err = mdbx_txn_info(txn, &info, false);
+ if (unlikely(err != MDBX_SUCCESS))
+ failure_perror("mdbx_txn_info()", err);
+ const size_t left = size_t(info.txn_space_leftover);
+ const size_t total =
+ size_t(info.txn_space_leftover) + size_t(info.txn_space_dirty);
+ return (unsigned)(left / (total >> 8));
+ }
+ return 0;
+}
+
void testcase::txn_end(bool abort) {
log_trace(">> txn_end(%s)", abort ? "abort" : "commit");
assert(txn_guard);
@@ -203,7 +221,8 @@ void testcase::txn_end(bool abort) {
MDBX_txn *txn = txn_guard.release();
if (abort) {
int err = mdbx_txn_abort(txn);
- if (unlikely(err != MDBX_SUCCESS && err != MDBX_THREAD_MISMATCH))
+ if (unlikely(err != MDBX_SUCCESS && err != MDBX_THREAD_MISMATCH &&
+ err != MDBX_BAD_TXN))
failure_perror("mdbx_txn_abort()", err);
} else {
txn_inject_writefault(txn);
@@ -215,18 +234,18 @@ void testcase::txn_end(bool abort) {
log_trace("<< txn_end(%s)", abort ? "abort" : "commit");
}
-void testcase::cursor_open(unsigned dbi) {
- log_trace(">> cursor_open(%u)", dbi);
+void testcase::cursor_open(MDBX_dbi handle) {
+ log_trace(">> cursor_open(%u)", handle);
assert(!cursor_guard);
assert(txn_guard);
MDBX_cursor *cursor = nullptr;
- int rc = mdbx_cursor_open(txn_guard.get(), dbi, &cursor);
+ int rc = mdbx_cursor_open(txn_guard.get(), handle, &cursor);
if (unlikely(rc != MDBX_SUCCESS))
failure_perror("mdbx_cursor_open()", rc);
cursor_guard.reset(cursor);
- log_trace("<< cursor_open(%u)", dbi);
+ log_trace("<< cursor_open(%u)", handle);
}
void testcase::cursor_close() {
@@ -264,8 +283,9 @@ void testcase::txn_inject_writefault(MDBX_txn *txn) {
if (config.params.inject_writefaultn && txn) {
if (config.params.inject_writefaultn <= nops_completed &&
(mdbx_txn_flags(txn) & MDBX_RDONLY) == 0) {
- log_info("== txn_inject_writefault(): got %u nops or more, inject FAULT",
- config.params.inject_writefaultn);
+ log_verbose(
+ "== txn_inject_writefault(): got %u nops or more, inject FAULT",
+ config.params.inject_writefaultn);
log_flush();
#if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS)
TerminateProcess(GetCurrentProcess(), 42);
@@ -305,21 +325,9 @@ bool testcase::wait4start() {
}
void testcase::kick_progress(bool active) const {
- chrono::time now = chrono::now_motonic();
- if (active) {
- static int last_point = -1;
- int point = (now.fixedpoint >> 29) & 3;
- if (point != last_point) {
- last.progress_timestamp = now;
- fprintf(stderr, "%c\b", "-\\|/"[last_point = point]);
- fflush(stderr);
- }
- } else if (now.fixedpoint - last.progress_timestamp.fixedpoint >
- chrono::from_seconds(2).fixedpoint) {
- last.progress_timestamp = now;
- fprintf(stderr, "%c\b", "@*"[now.utc & 1]);
- fflush(stderr);
- }
+ if (!global::config::progress_indicator)
+ return;
+ logging::progress_canary(active);
}
void testcase::report(size_t nops_done) {
@@ -328,11 +336,10 @@ void testcase::report(size_t nops_done) {
return;
nops_completed += nops_done;
- log_verbose("== complete +%" PRIuPTR " iteration, total %" PRIuPTR " done",
- nops_done, nops_completed);
+ log_debug("== complete +%" PRIuPTR " iteration, total %" PRIuPTR " done",
+ nops_done, nops_completed);
- if (global::config::progress_indicator)
- kick_progress(true);
+ kick_progress(true);
if (config.signal_nops && !signalled &&
config.signal_nops <= nops_completed) {
@@ -387,7 +394,7 @@ bool testcase::should_continue(bool check_timeout_only) const {
nops_completed >= config.params.test_nops)
result = false;
- if (result && global::config::progress_indicator)
+ if (result)
kick_progress(false);
return result;
@@ -430,14 +437,14 @@ void testcase::update_canary(uint64_t increment) {
log_trace("<< update_canary: sequence = %" PRIu64, canary_now.y);
}
-int testcase::db_open__begin__table_create_open_clean(MDBX_dbi &dbi) {
+int testcase::db_open__begin__table_create_open_clean(MDBX_dbi &handle) {
db_open();
int err, retry_left = 42;
for (;;) {
txn_begin(false);
- dbi = db_table_open(true);
- db_table_clear(dbi);
+ handle = db_table_open(true);
+ db_table_clear(handle);
err = breakable_commit();
if (likely(err == MDBX_SUCCESS)) {
txn_begin(false);
@@ -464,7 +471,7 @@ MDBX_dbi testcase::db_table_open(bool create) {
failure("snprintf(tablename): %d", rc);
tablename = tablename_buf;
}
- log_verbose("use %s table", tablename ? tablename : "MAINDB");
+ log_debug("use %s table", tablename ? tablename : "MAINDB");
MDBX_dbi handle = 0;
int rc = mdbx_dbi_open(txn_guard.get(), tablename,
@@ -490,9 +497,9 @@ void testcase::db_table_drop(MDBX_dbi handle) {
}
}
-void testcase::db_table_clear(MDBX_dbi handle) {
+void testcase::db_table_clear(MDBX_dbi handle, MDBX_txn *txn) {
log_trace(">> testcase::db_table_clear, handle %u", handle);
- int rc = mdbx_drop(txn_guard.get(), handle, false);
+ int rc = mdbx_drop(txn ? txn : txn_guard.get(), handle, false);
if (unlikely(rc != MDBX_SUCCESS))
failure_perror("mdbx_drop(delete=false)", rc);
log_trace("<< testcase::db_table_clear");
@@ -510,7 +517,7 @@ void testcase::db_table_close(MDBX_dbi handle) {
void testcase::checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check,
MDBX_val expected_valued) {
MDBX_val actual_value = expected_valued;
- int rc = mdbx_get2(txn_guard.get(), handle, &key2check, &actual_value);
+ int rc = mdbx_get_nearest(txn_guard.get(), handle, &key2check, &actual_value);
if (unlikely(rc != MDBX_SUCCESS))
failure_perror(step, rc);
if (!is_samedata(&actual_value, &expected_valued))
@@ -560,6 +567,9 @@ bool test_execute(const actor_config &config_const) {
case ac_ttl:
test.reset(new testcase_ttl(config, pid));
break;
+ case ac_nested:
+ test.reset(new testcase_nested(config, pid));
+ break;
default:
test.reset(new testcase(config, pid));
break;
@@ -582,13 +592,13 @@ bool test_execute(const actor_config &config_const) {
}
if (config.params.nrepeat == 1)
- log_info("test successed");
+ log_verbose("test successed");
else {
if (config.params.nrepeat)
- log_info("test successed (iteration %zi of %zi)", iter,
- size_t(config.params.nrepeat));
+ log_verbose("test successed (iteration %zi of %zi)", iter,
+ size_t(config.params.nrepeat));
else
- log_info("test successed (iteration %zi)", iter);
+ log_verbose("test successed (iteration %zi)", iter);
config.params.keygen.seed += INT32_C(0xA4F4D37B);
}
@@ -599,3 +609,135 @@ bool test_execute(const actor_config &config_const) {
return false;
}
}
+
+//-----------------------------------------------------------------------------
+
+int testcase::insert(const keygen::buffer &akey, const keygen::buffer &adata,
+ unsigned flags) {
+ int err = mdbx_put(txn_guard.get(), dbi, &akey->value, &adata->value, flags);
+ if (err == MDBX_SUCCESS && config.params.speculum) {
+ const auto S_key = S(akey);
+ const auto S_data = S(adata);
+ const bool inserted = speculum.emplace(S_key, S_data).second;
+ assert(inserted);
+ (void)inserted;
+ }
+ return err;
+}
+
+int testcase::replace(const keygen::buffer &akey,
+ const keygen::buffer &new_data,
+ const keygen::buffer &old_data, unsigned flags) {
+ if (config.params.speculum) {
+ const auto S_key = S(akey);
+ const auto S_old = S(old_data);
+ const auto S_new = S(new_data);
+ const auto removed = speculum.erase(SET::key_type(S_key, S_old));
+ assert(removed == 1);
+ (void)removed;
+ const bool inserted = speculum.emplace(S_key, S_new).second;
+ assert(inserted);
+ (void)inserted;
+ }
+ return mdbx_replace(txn_guard.get(), dbi, &akey->value, &new_data->value,
+ &old_data->value, flags);
+}
+
+int testcase::remove(const keygen::buffer &akey, const keygen::buffer &adata) {
+ if (config.params.speculum) {
+ const auto S_key = S(akey);
+ const auto S_data = S(adata);
+ const auto removed = speculum.erase(SET::key_type(S_key, S_data));
+ assert(removed == 1);
+ (void)removed;
+ }
+ return mdbx_del(txn_guard.get(), dbi, &akey->value, &adata->value);
+}
+
+bool testcase::speculum_verify() {
+ if (!config.params.speculum)
+ return true;
+
+ if (!txn_guard)
+ txn_begin(true);
+
+ char dump_key[128], dump_value[128];
+ char dump_mkey[128], dump_mvalue[128];
+
+ MDBX_cursor *cursor;
+ int err = mdbx_cursor_open(txn_guard.get(), dbi, &cursor);
+ if (err != MDBX_SUCCESS)
+ failure_perror("mdbx_cursor_open()", err);
+
+ bool rc = true;
+ MDBX_val akey, avalue;
+ MDBX_val mkey, mvalue;
+ err = mdbx_cursor_get(cursor, &akey, &avalue, MDBX_FIRST);
+
+ unsigned extra = 0, lost = 0, n = 0;
+ assert(std::is_sorted(speculum.cbegin(), speculum.cend(), ItemCompare(this)));
+ auto it = speculum.cbegin();
+ while (true) {
+ if (err != MDBX_SUCCESS) {
+ akey.iov_len = avalue.iov_len = 0;
+ akey.iov_base = avalue.iov_base = nullptr;
+ }
+ const auto S_key = S(akey);
+ const auto S_data = S(avalue);
+ if (it != speculum.cend()) {
+ mkey.iov_base = (void *)it->first.c_str();
+ mkey.iov_len = it->first.size();
+ mvalue.iov_base = (void *)it->second.c_str();
+ mvalue.iov_len = it->second.size();
+ }
+ if (err == MDBX_SUCCESS && it != speculum.cend() && S_key == it->first &&
+ S_data == it->second) {
+ ++it;
+ err = mdbx_cursor_get(cursor, &akey, &avalue, MDBX_NEXT);
+ } else if (err == MDBX_SUCCESS &&
+ (it == speculum.cend() || S_key < it->first ||
+ (S_key == it->first && S_data < it->second))) {
+ extra += 1;
+ if (it != speculum.cend()) {
+ log_error("extra pair %u/%u: db{%s, %s} < mi{%s, %s}", n, extra,
+ mdbx_dump_val(&akey, dump_key, sizeof(dump_key)),
+ mdbx_dump_val(&avalue, dump_value, sizeof(dump_value)),
+ mdbx_dump_val(&mkey, dump_mkey, sizeof(dump_mkey)),
+ mdbx_dump_val(&mvalue, dump_mvalue, sizeof(dump_mvalue)));
+ } else {
+ log_error("extra pair %u/%u: db{%s, %s} < mi.END", n, extra,
+ mdbx_dump_val(&akey, dump_key, sizeof(dump_key)),
+ mdbx_dump_val(&avalue, dump_value, sizeof(dump_value)));
+ }
+ err = mdbx_cursor_get(cursor, &akey, &avalue, MDBX_NEXT);
+ rc = false;
+ } else if (it != speculum.cend() &&
+ (err == MDBX_NOTFOUND || S_key > it->first ||
+ (S_key == it->first && S_data > it->second))) {
+ lost += 1;
+ if (err == MDBX_NOTFOUND) {
+ log_error("lost pair %u/%u: db.END > mi{%s, %s}", n, lost,
+ mdbx_dump_val(&mkey, dump_mkey, sizeof(dump_mkey)),
+ mdbx_dump_val(&mvalue, dump_mvalue, sizeof(dump_mvalue)));
+ } else {
+ log_error("lost pair %u/%u: db{%s, %s} > mi{%s, %s}", n, lost,
+ mdbx_dump_val(&akey, dump_key, sizeof(dump_key)),
+ mdbx_dump_val(&avalue, dump_value, sizeof(dump_value)),
+ mdbx_dump_val(&mkey, dump_mkey, sizeof(dump_mkey)),
+ mdbx_dump_val(&mvalue, dump_mvalue, sizeof(dump_mvalue)));
+ }
+ ++it;
+ rc = false;
+ } else if (err == MDBX_NOTFOUND && it == speculum.cend()) {
+ break;
+ } else if (err != MDBX_SUCCESS) {
+ failure_perror("mdbx_cursor_get()", err);
+ } else {
+ assert(!"WTF?");
+ }
+ n += 1;
+ }
+
+ mdbx_cursor_close(cursor);
+ return rc;
+}
diff --git a/libs/libmdbx/src/test/test.h b/libs/libmdbx/src/test/test.h
index fb5ad4ee83..630059ac3f 100644
--- a/libs/libmdbx/src/test/test.h
+++ b/libs/libmdbx/src/test/test.h
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -22,6 +22,24 @@
#include "osal.h"
#include "utils.h"
+#include <deque>
+#include <set>
+#include <stack>
+#include <tuple>
+
+#ifndef HAVE_cxx17_std_string_view
+#if __cplusplus >= 201703L && __has_include(<string_view>)
+#include <string_view>
+#define HAVE_cxx17_std_string_view 1
+#else
+#define HAVE_cxx17_std_string_view 0
+#endif
+#endif /* HAVE_cxx17_std_string_view */
+
+#if HAVE_cxx17_std_string_view
+#include <string_view>
+#endif
+
bool test_execute(const actor_config &config);
std::string thunk_param(const actor_config &config);
void testcase_setup(const char *casename, actor_params &params,
@@ -49,25 +67,26 @@ extern bool cleanup_before;
extern bool cleanup_after;
extern bool failfast;
extern bool progress_indicator;
+extern bool console_mode;
} /* namespace config */
} /* namespace global */
//-----------------------------------------------------------------------------
-struct db_deleter : public std::unary_function<void, MDBX_env *> {
+struct db_deleter /* : public std::unary_function<void, MDBX_env *> */ {
void operator()(MDBX_env *env) const { mdbx_env_close(env); }
};
-struct txn_deleter : public std::unary_function<void, MDBX_txn *> {
+struct txn_deleter /* : public std::unary_function<void, MDBX_txn *> */ {
void operator()(MDBX_txn *txn) const {
int rc = mdbx_txn_abort(txn);
if (rc)
- log_trouble(mdbx_func_, "mdbx_txn_abort()", rc);
+ log_trouble(__func__, "mdbx_txn_abort()", rc);
}
};
-struct cursor_deleter : public std::unary_function<void, MDBX_cursor *> {
+struct cursor_deleter /* : public std::unary_function<void, MDBX_cursor *> */ {
void operator()(MDBX_cursor *cursor) const { mdbx_cursor_close(cursor); }
};
@@ -79,9 +98,45 @@ typedef std::unique_ptr<MDBX_cursor, cursor_deleter> scoped_cursor_guard;
class testcase {
protected:
+#if HAVE_cxx17_std_string_view
+ using data_view = std::string_view;
+#else
+ using data_view = std::string;
+#endif
+ static inline data_view S(const MDBX_val &v) {
+ return data_view(static_cast<const char *>(v.iov_base), v.iov_len);
+ }
+ static inline data_view S(const keygen::buffer &b) { return S(b->value); }
+
+ using Item = std::pair<std::string, std::string>;
+ struct ItemCompare {
+ const testcase *context;
+ ItemCompare(const testcase *owner) : context(owner) {}
+
+ bool operator()(const Item &a, const Item &b) const {
+ MDBX_val va, vb;
+ va.iov_base = (void *)a.first.data();
+ va.iov_len = a.first.size();
+ vb.iov_base = (void *)b.first.data();
+ vb.iov_len = b.first.size();
+ int cmp = mdbx_cmp(context->txn_guard.get(), context->dbi, &va, &vb);
+ if (cmp == 0 &&
+ (context->config.params.table_flags & MDBX_DUPSORT) != 0) {
+ va.iov_base = (void *)a.second.data();
+ va.iov_len = a.second.size();
+ vb.iov_base = (void *)b.second.data();
+ vb.iov_len = b.second.size();
+ cmp = mdbx_dcmp(context->txn_guard.get(), context->dbi, &va, &vb);
+ }
+ return cmp < 0;
+ }
+ };
+ using SET = std::set<Item, ItemCompare>;
+
const actor_config &config;
const mdbx_pid_t pid;
+ MDBX_dbi dbi;
scoped_db_guard db_guard;
scoped_txn_guard txn_guard;
scoped_cursor_guard cursor_guard;
@@ -95,12 +150,23 @@ protected:
struct {
mdbx_canary canary;
- mutable chrono::time progress_timestamp;
} last;
- static int oom_callback(MDBX_env *env, int pid, mdbx_tid_t tid, uint64_t txn,
- unsigned gap, int retry);
+ SET speculum;
+ bool speculum_verify();
+ int insert(const keygen::buffer &akey, const keygen::buffer &adata,
+ unsigned flags);
+ int replace(const keygen::buffer &akey, const keygen::buffer &new_value,
+ const keygen::buffer &old_value, unsigned flags);
+ int remove(const keygen::buffer &akey, const keygen::buffer &adata);
+ static int oom_callback(MDBX_env *env, mdbx_pid_t pid, mdbx_tid_t tid,
+ uint64_t txn, unsigned gap, size_t space, int retry);
+
+ bool is_nested_txn_available() const {
+ return (config.params.mode_flags & MDBX_WRITEMAP) == 0;
+ }
+ void kick_progress(bool active) const;
void db_prepare();
void db_open();
void db_close();
@@ -109,21 +175,21 @@ protected:
void txn_end(bool abort);
int breakable_restart();
void txn_restart(bool abort, bool readonly, unsigned flags = 0);
- void cursor_open(unsigned dbi);
+ void cursor_open(MDBX_dbi handle);
void cursor_close();
void txn_inject_writefault(void);
void txn_inject_writefault(MDBX_txn *txn);
void fetch_canary();
void update_canary(uint64_t increment);
- void kick_progress(bool active) const;
void checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check,
MDBX_val expected_valued);
+ unsigned txn_underutilization_x256(MDBX_txn *txn) const;
MDBX_dbi db_table_open(bool create);
void db_table_drop(MDBX_dbi handle);
- void db_table_clear(MDBX_dbi handle);
+ void db_table_clear(MDBX_dbi handle, MDBX_txn *txn = nullptr);
void db_table_close(MDBX_dbi handle);
- int db_open__begin__table_create_open_clean(MDBX_dbi &dbi);
+ int db_open__begin__table_create_open_clean(MDBX_dbi &handle);
bool wait4start();
void report(size_t nops_done);
@@ -131,13 +197,12 @@ protected:
bool should_continue(bool check_timeout_only = false) const;
void generate_pair(const keygen::serial_t serial, keygen::buffer &out_key,
- keygen::buffer &out_value, keygen::serial_t data_age = 0) {
- keyvalue_maker.pair(serial, out_key, out_value, data_age);
+ keygen::buffer &out_value, keygen::serial_t data_age) {
+ keyvalue_maker.pair(serial, out_key, out_value, data_age, false);
}
- void generate_pair(const keygen::serial_t serial,
- keygen::serial_t data_age = 0) {
- generate_pair(serial, key, data, data_age);
+ void generate_pair(const keygen::serial_t serial) {
+ keyvalue_maker.pair(serial, key, data, 0, true);
}
bool mode_readonly() const {
@@ -146,7 +211,8 @@ protected:
public:
testcase(const actor_config &config, const mdbx_pid_t pid)
- : config(config), pid(pid), signalled(false), nops_completed(0) {
+ : config(config), pid(pid), signalled(false), nops_completed(0),
+ speculum(ItemCompare(this)) {
start_timestamp.reset();
memset(&last, 0, sizeof(last));
}
@@ -161,49 +227,52 @@ class testcase_ttl : public testcase {
public:
testcase_ttl(const actor_config &config, const mdbx_pid_t pid)
: testcase(config, pid) {}
- bool run();
+ bool run() override;
};
class testcase_hill : public testcase {
+ using inherited = testcase;
+ SET speculum_commited;
+
public:
testcase_hill(const actor_config &config, const mdbx_pid_t pid)
- : testcase(config, pid) {}
- bool run();
+ : testcase(config, pid), speculum_commited(ItemCompare(this)) {}
+ bool run() override;
};
class testcase_append : public testcase {
public:
testcase_append(const actor_config &config, const mdbx_pid_t pid)
: testcase(config, pid) {}
- bool run();
+ bool run() override;
};
class testcase_deadread : public testcase {
public:
testcase_deadread(const actor_config &config, const mdbx_pid_t pid)
: testcase(config, pid) {}
- bool run();
+ bool run() override;
};
class testcase_deadwrite : public testcase {
public:
testcase_deadwrite(const actor_config &config, const mdbx_pid_t pid)
: testcase(config, pid) {}
- bool run();
+ bool run() override;
};
class testcase_jitter : public testcase {
public:
testcase_jitter(const actor_config &config, const mdbx_pid_t pid)
: testcase(config, pid) {}
- bool run();
+ bool run() override;
};
class testcase_try : public testcase {
public:
testcase_try(const actor_config &config, const mdbx_pid_t pid)
: testcase(config, pid) {}
- bool run();
+ bool run() override;
};
class testcase_copy : public testcase {
@@ -214,5 +283,31 @@ public:
testcase_copy(const actor_config &config, const mdbx_pid_t pid)
: testcase(config, pid),
copy_pathname(config.params.pathname_db + "-copy") {}
- bool run();
+ bool run() override;
+};
+
+class testcase_nested : public testcase {
+ using inherited = testcase;
+ using FIFO = std::deque<std::pair<uint64_t, unsigned>>;
+
+ uint64_t serial;
+ FIFO fifo;
+ std::stack<std::tuple<scoped_txn_guard, uint64_t, FIFO, SET>> stack;
+
+ bool trim_tail(unsigned window_width);
+ bool grow_head(unsigned head_count);
+ bool pop_txn(bool abort);
+ bool pop_txn() {
+ return pop_txn(inherited::is_nested_txn_available() ? flipcoin_x3()
+ : flipcoin_x2());
+ }
+ void push_txn();
+ bool stochastic_breakable_restart_with_nested(bool force_restart = false);
+
+public:
+ testcase_nested(const actor_config &config, const mdbx_pid_t pid)
+ : testcase(config, pid) {}
+ bool setup() override;
+ bool run() override;
+ bool teardown() override;
};
diff --git a/libs/libmdbx/src/test/test.vcxproj b/libs/libmdbx/src/test/test.vcxproj
deleted file mode 100644
index 93de00e3a0..0000000000
--- a/libs/libmdbx/src/test/test.vcxproj
+++ /dev/null
@@ -1,209 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|Win32">
- <Configuration>Debug</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|Win32">
- <Configuration>Release</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Debug|x64">
- <Configuration>Debug</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|x64">
- <Configuration>Release</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\dll.vcxproj">
- <Project>{6d19209b-ece7-4b9c-941c-0aa2b484f199}</Project>
- </ProjectReference>
- </ItemGroup>
- <PropertyGroup Label="Globals">
- <ProjectGuid>{30E29CE6-E6FC-4D32-AA07-46A55FAF3A31}</ProjectGuid>
- <Keyword>Win32Proj</Keyword>
- <RootNamespace>mdbxtest</RootNamespace>
- <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Label="ExtensionSettings">
- </ImportGroup>
- <ImportGroup Label="Shared">
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <LinkIncremental>true</LinkIncremental>
- <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
- <IntDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
- <TargetName>mdbx_test</TargetName>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <LinkIncremental>false</LinkIncremental>
- <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
- <IntDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
- <TargetName>mdbx_test</TargetName>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <LinkIncremental>true</LinkIncremental>
- <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
- <IntDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
- <TargetName>mdbx_test</TargetName>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <LinkIncremental>false</LinkIncremental>
- <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
- <IntDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
- <TargetName>mdbx_test</TargetName>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <ClCompile>
- <PrecompiledHeader>Use</PrecompiledHeader>
- <WarningLevel>Level4</WarningLevel>
- <Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;MDBX_DEBUG=1;%(PreprocessorDefinitions);LIBMDBX_IMPORTS=1</PreprocessorDefinitions>
- <SDLCheck>true</SDLCheck>
- <PrecompiledHeaderFile>test.h</PrecompiledHeaderFile>
- <TreatWarningAsError>true</TreatWarningAsError>
- </ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <ClCompile>
- <PrecompiledHeader>Use</PrecompiledHeader>
- <WarningLevel>Level4</WarningLevel>
- <Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>_DEBUG;_CONSOLE;MDBX_DEBUG=1;%(PreprocessorDefinitions);LIBMDBX_IMPORTS=1</PreprocessorDefinitions>
- <SDLCheck>true</SDLCheck>
- <PrecompiledHeaderFile>test.h</PrecompiledHeaderFile>
- <TreatWarningAsError>true</TreatWarningAsError>
- </ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <ClCompile>
- <WarningLevel>Level4</WarningLevel>
- <PrecompiledHeader>Use</PrecompiledHeader>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);LIBMDBX_IMPORTS=1</PreprocessorDefinitions>
- <SDLCheck>true</SDLCheck>
- <PrecompiledHeaderFile>test.h</PrecompiledHeaderFile>
- <OmitFramePointers>true</OmitFramePointers>
- </ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <ClCompile>
- <WarningLevel>Level4</WarningLevel>
- <PrecompiledHeader>Use</PrecompiledHeader>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions);LIBMDBX_IMPORTS=1</PreprocessorDefinitions>
- <SDLCheck>true</SDLCheck>
- <PrecompiledHeaderFile>test.h</PrecompiledHeaderFile>
- <OmitFramePointers>true</OmitFramePointers>
- </ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
- </Link>
- </ItemDefinitionGroup>
- <ItemGroup>
- <ClInclude Include="base.h" />
- <ClInclude Include="chrono.h" />
- <ClInclude Include="config.h" />
- <ClInclude Include="keygen.h" />
- <ClInclude Include="log.h" />
- <ClInclude Include="osal.h" />
- <ClInclude Include="test.h" />
- <ClInclude Include="utils.h" />
- </ItemGroup>
- <ItemGroup>
- <ClCompile Include="ttl.cc" />
- <ClCompile Include="append.cc" />
- <ClCompile Include="cases.cc" />
- <ClCompile Include="chrono.cc" />
- <ClCompile Include="config.cc" />
- <ClCompile Include="copy.cc" />
- <ClCompile Include="dead.cc" />
- <ClCompile Include="hill.cc" />
- <ClCompile Include="try.cc" />
- <ClCompile Include="jitter.cc" />
- <ClCompile Include="keygen.cc" />
- <ClCompile Include="log.cc" />
- <ClCompile Include="main.cc" />
- <ClCompile Include="osal-windows.cc">
- <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
- <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
- <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
- <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
- </ClCompile>
- <ClCompile Include="test.cc" />
- <ClCompile Include="utils.cc" />
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
-</Project>
diff --git a/libs/libmdbx/src/test/try.cc b/libs/libmdbx/src/test/try.cc
index 150abd36de..adb0113096 100644
--- a/libs/libmdbx/src/test/try.cc
+++ b/libs/libmdbx/src/test/try.cc
@@ -1,4 +1,4 @@
-#include "test.h"
+#include "test.h"
bool testcase_try::run() {
db_open();
diff --git a/libs/libmdbx/src/test/ttl.cc b/libs/libmdbx/src/test/ttl.cc
index b2650f0c13..782a8b4d56 100644
--- a/libs/libmdbx/src/test/ttl.cc
+++ b/libs/libmdbx/src/test/ttl.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -29,11 +29,10 @@ static unsigned edge2count(uint64_t edge, unsigned count_max) {
}
bool testcase_ttl::run() {
- MDBX_dbi dbi;
int err = db_open__begin__table_create_open_clean(dbi);
if (unlikely(err != MDBX_SUCCESS)) {
log_notice("ttl: bailout-prepare due '%s'", mdbx_strerror(err));
- return true;
+ return false;
}
/* LY: тест "эмуляцией time-to-live":
@@ -54,18 +53,8 @@ bool testcase_ttl::run() {
/* LY: для параметризации используем подходящие параметры, которые не имеют
* здесь смысла в первоначальном значении. */
- const unsigned window_max_lower =
-#ifdef __APPLE__
- 333;
-#else
- 999;
-#endif
- const unsigned count_max_lower =
-#ifdef __APPLE__
- 333;
-#else
- 999;
-#endif
+ const unsigned window_max_lower = 333;
+ const unsigned count_max_lower = 333;
const unsigned window_max = (config.params.batch_read > window_max_lower)
? config.params.batch_read
@@ -73,8 +62,8 @@ bool testcase_ttl::run() {
const unsigned count_max = (config.params.batch_write > count_max_lower)
? config.params.batch_write
: count_max_lower;
- log_info("ttl: using `batch_read` value %u for window_max", window_max);
- log_info("ttl: using `batch_write` value %u for count_max", count_max);
+ log_verbose("ttl: using `batch_read` value %u for window_max", window_max);
+ log_verbose("ttl: using `batch_write` value %u for count_max", count_max);
uint64_t seed =
prng64_map2_white(config.params.keygen.seed) + config.actor_id;
@@ -87,14 +76,16 @@ bool testcase_ttl::run() {
std::deque<std::pair<uint64_t, unsigned>> fifo;
uint64_t serial = 0;
+ bool rc = false;
while (should_continue()) {
const uint64_t salt = prng64_white(seed) /* mdbx_txn_id(txn_guard.get()) */;
- const unsigned window_width = edge2window(salt, window_max);
+ const unsigned window_width =
+ flipcoin_x4() ? 0 : edge2window(salt, window_max);
unsigned head_count = edge2count(salt, count_max);
- log_verbose("ttl: step #%zu (serial %" PRIu64
- ", window %u, count %u) salt %" PRIu64,
- nops_completed, serial, window_width, head_count, salt);
+ log_debug("ttl: step #%zu (serial %" PRIu64
+ ", window %u, count %u) salt %" PRIu64,
+ nops_completed, serial, window_width, head_count, salt);
if (window_width) {
while (fifo.size() > window_width) {
@@ -104,7 +95,7 @@ bool testcase_ttl::run() {
tail_count);
fifo.pop_back();
for (unsigned n = 0; n < tail_count; ++n) {
- log_trace("ttl: remove-tail %" PRIu64, serial);
+ log_trace("ttl: remove-tail %" PRIu64, tail_serial);
generate_pair(tail_serial);
err = mdbx_del(txn_guard.get(), dbi, &key->value, &data->value);
if (unlikely(err != MDBX_SUCCESS)) {
@@ -158,7 +149,9 @@ bool testcase_ttl::run() {
serial = fifo.front().first;
fifo.pop_front();
}
+
report(1);
+ rc = true;
}
bailout:
@@ -170,10 +163,10 @@ bailout:
err = breakable_commit();
if (unlikely(err != MDBX_SUCCESS)) {
log_notice("ttl: bailout-clean due '%s'", mdbx_strerror(err));
- return true;
+ return false;
}
} else
db_table_close(dbi);
}
- return true;
+ return rc;
}
diff --git a/libs/libmdbx/src/test/utils.cc b/libs/libmdbx/src/test/utils.cc
index d9b3538b99..311cf544ef 100644
--- a/libs/libmdbx/src/test/utils.cc
+++ b/libs/libmdbx/src/test/utils.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -331,6 +331,9 @@ double double_from_upper(uint64_t salt) {
}
bool flipcoin() { return bleach32((uint32_t)entropy_ticks()) & 1; }
+bool flipcoin_x2() { return (bleach32((uint32_t)entropy_ticks()) & 3) == 0; }
+bool flipcoin_x3() { return (bleach32((uint32_t)entropy_ticks()) & 7) == 0; }
+bool flipcoin_x4() { return (bleach32((uint32_t)entropy_ticks()) & 15) == 0; }
bool jitter(unsigned probability_percent) {
const uint32_t top = UINT32_MAX - UINT32_MAX % 100;
diff --git a/libs/libmdbx/src/test/utils.h b/libs/libmdbx/src/test/utils.h
index d1b859acd4..2a5a54de6d 100644
--- a/libs/libmdbx/src/test/utils.h
+++ b/libs/libmdbx/src/test/utils.h
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -151,7 +151,7 @@ template <typename T> static __inline T load(const void *ptr) {
#if defined(_MSC_VER) && \
(defined(_M_ARM64) || defined(_M_X64) || defined(_M_IA64))
return *(const T __unaligned *)ptr;
-#elif UNALIGNED_OK
+#elif MDBX_UNALIGNED_OK
return *(const T *)ptr;
#else
T local;
@@ -161,14 +161,14 @@ template <typename T> static __inline T load(const void *ptr) {
memcpy(&local, (const T *)ptr, sizeof(T));
#endif /* __GNUC__ || __clang__ */
return local;
-#endif /* UNALIGNED_OK */
+#endif /* MDBX_UNALIGNED_OK */
}
template <typename T> static __inline void store(void *ptr, const T &value) {
#if defined(_MSC_VER) && \
(defined(_M_ARM64) || defined(_M_X64) || defined(_M_IA64))
*((T __unaligned *)ptr) = value;
-#elif UNALIGNED_OK
+#elif MDBX_UNALIGNED_OK
*(volatile T *)ptr = value;
#else
#if defined(__GNUC__) || defined(__clang__)
@@ -176,7 +176,7 @@ template <typename T> static __inline void store(void *ptr, const T &value) {
#else
memcpy(ptr, &value, sizeof(T));
#endif /* __GNUC__ || __clang__ */
-#endif /* UNALIGNED_OK */
+#endif /* MDBX_UNALIGNED_OK */
}
} /* namespace unaligned */
@@ -355,5 +355,8 @@ uint64_t prng64(void);
void prng_fill(void *ptr, size_t bytes);
bool flipcoin();
+bool flipcoin_x2();
+bool flipcoin_x3();
+bool flipcoin_x4();
bool jitter(unsigned probability_percent);
void jitter_delay(bool extra = false);