test: jitter testcase (squashed major refine).

This commit is contained in:
Leo Yuriev 2017-04-21 18:41:11 +03:00
parent 101e015d2c
commit f3e31a74ee
12 changed files with 454 additions and 268 deletions

View File

@ -66,7 +66,7 @@ clean:
rm -rf $(TOOLS) test/test @* *.[ao] *.[ls]o *~ tmp.db/* *.gcov *.log *.err rm -rf $(TOOLS) test/test @* *.[ao] *.[ls]o *~ tmp.db/* *.gcov *.log *.err
check: test/test check: test/test
test/test --pathname=tmp.db --basic --dont-cleanup-after && ./mdbx_chk -vn tmp.db test/test --pathname=tmp.db --dont-cleanup-after basic && ./mdbx_chk -vn tmp.db
mdbx.o: $(MDBX_SRC) Makefile mdbx.o: $(MDBX_SRC) Makefile
$(CC) $(CFLAGS) -c src/mdbx.c -o $@ $(CC) $(CFLAGS) -c src/mdbx.c -o $@

View File

@ -23,8 +23,8 @@ void configure_actor(unsigned &lastid, const actor_testcase testcase,
if (i->is_waitable(params.waitfor_nops)) { if (i->is_waitable(params.waitfor_nops)) {
if (i->signal_nops && i->signal_nops != params.waitfor_nops) if (i->signal_nops && i->signal_nops != params.waitfor_nops)
failure("Previous waitable actor (id=%u) already linked on %u-ops\n", failure("Previous waitable actor (id=%u) already linked on %u-ops\n",
i->id, i->signal_nops); i->actor_id, i->signal_nops);
wait4id = i->id; wait4id = i->actor_id;
i->signal_nops = params.waitfor_nops; i->signal_nops = params.waitfor_nops;
break; break;
} }
@ -33,7 +33,7 @@ void configure_actor(unsigned &lastid, const actor_testcase testcase,
failure("No previous waitable actor for %u-ops\n", params.waitfor_nops); failure("No previous waitable actor for %u-ops\n", params.waitfor_nops);
} }
unsigned long id = 0; unsigned id = 0;
if (!id_cstr || strcmp(id_cstr, "auto") == 0) if (!id_cstr || strcmp(id_cstr, "auto") == 0)
id = lastid + 1; id = lastid + 1;
else { else {
@ -47,23 +47,26 @@ void configure_actor(unsigned &lastid, const actor_testcase testcase,
} }
if (id < 1 || id > ACTOR_ID_MAX) if (id < 1 || id > ACTOR_ID_MAX)
failure("Invalid actor-id %lu\n", id); failure("Invalid actor-id %u\n", id);
lastid = id; lastid = id;
log_trace("configure_actor: %u for %s", id, testcase2str(testcase));
global::actors.emplace_back(actor_config(testcase, params, id, wait4id)); global::actors.emplace_back(actor_config(testcase, params, id, wait4id));
global::databases.insert(params.pathname_db); global::databases.insert(params.pathname_db);
} }
bool testcase_setup(const char *casename, const actor_params &params, void testcase_setup(const char *casename, actor_params &params,
unsigned &lastid) { unsigned &lastid) {
log_notice("testcase_setup(%s): TODO", casename);
if (strcmp(casename, "basic") == 0) { if (strcmp(casename, "basic") == 0) {
log_notice(">>> testcase_setup(%s)", casename);
configure_actor(lastid, ac_hill, nullptr, params); configure_actor(lastid, ac_hill, nullptr, params);
return true; configure_actor(lastid, ac_jitter, nullptr, params);
configure_actor(lastid, ac_jitter, nullptr, params);
configure_actor(lastid, ac_jitter, nullptr, params);
log_notice("<<< testcase_setup(%s): done", casename);
} else {
failure("unknown testcase `%s`", casename);
} }
return false;
} }
/* TODO */ /* TODO */

View File

@ -32,26 +32,23 @@ bool parse_option(int argc, char *const argv[], int &narg, const char *option,
if (!value) { if (!value) {
if (current[optlen + 2] == '=') if (current[optlen + 2] == '=')
failure("Option '--%s' doen't accept any value\n", option); failure("Option '--%s' doen't accept any value\n", option);
narg += 1;
return true; return true;
} }
*value = nullptr; *value = nullptr;
if (current[optlen + 2] == '=') { if (current[optlen + 2] == '=') {
*value = &current[optlen + 3]; *value = &current[optlen + 3];
narg += 1;
return true; return true;
} }
if (narg + 1 < argc && strncmp("--", argv[narg + 1], 2)) { if (narg + 1 < argc && strncmp("--", argv[narg + 1], 2) != 0) {
*value = argv[narg + 1]; *value = argv[narg + 1];
narg += 2; ++narg;
return true; return true;
} }
if (default_value) { if (default_value) {
*value = default_value; *value = default_value;
narg += 1;
return true; return true;
} }
@ -184,12 +181,17 @@ bool parse_option(int argc, char *const argv[], int &narg, const char *option,
const char *value_cstr = NULL; const char *value_cstr = NULL;
if (!parse_option(argc, argv, narg, option, &value_cstr, "yes")) { if (!parse_option(argc, argv, narg, option, &value_cstr, "yes")) {
const char *current = argv[narg]; const char *current = argv[narg];
if (strncmp(current, "--no-", 5) || strcmp(current + 5, option)) if (strncmp(current, "--no-", 5) == 0 && strcmp(current + 5, option) == 0) {
return false;
value = false; value = false;
narg += 1;
return true; return true;
} }
if (strncmp(current, "--dont-", 7) == 0 &&
strcmp(current + 7, option) == 0) {
value = false;
return true;
}
return false;
}
if (!value_cstr) { if (!value_cstr) {
value = true; value = true;
@ -266,8 +268,8 @@ void dump(const char *title) {
logging::local_suffix indent(title); logging::local_suffix indent(title);
for (auto i = global::actors.begin(); i != global::actors.end(); ++i) { for (auto i = global::actors.begin(); i != global::actors.end(); ++i) {
log_info("#%u, testcase %s, id/table %u\n", i->order, log_info("#%u, testcase %s, space_id/table %u\n", i->actor_id,
testcase2str(i->testcase), i->id); testcase2str(i->testcase), i->space_id);
indent.push(); indent.push();
if (i->params.loglevel) { if (i->params.loglevel) {
@ -284,8 +286,8 @@ void dump(const char *title) {
log_info("seed %u\n", i->params.seed); log_info("seed %u\n", i->params.seed);
if (i->params.test_nrecords) if (i->params.test_nops)
log_info("records %u\n", i->params.test_nrecords); log_info("iterations/records %u\n", i->params.test_nops);
else else
dump_duration("duration", i->params.test_duration); dump_duration("duration", i->params.test_duration);
@ -319,10 +321,10 @@ void dump(const char *title) {
indent.pop(); indent.pop();
} }
dump_duration("timeout", global::config::timeout); dump_duration("timeout", global::config::timeout_duration_seconds);
log_info("cleanup: before %s, after %s\n", log_info("cleanup: before %s, after %s\n",
global::config::dont_cleanup_before ? "No" : "Yes", global::config::cleanup_before ? "Yes" : "No",
global::config::dont_cleanup_after ? "No" : "Yes"); global::config::cleanup_after ? "Yes" : "No");
} }
} /* namespace config */ } /* namespace config */
@ -332,10 +334,10 @@ void dump(const char *title) {
using namespace config; using namespace config;
actor_config::actor_config(actor_testcase testcase, const actor_params &params, actor_config::actor_config(actor_testcase testcase, const actor_params &params,
unsigned id, unsigned wait4id) unsigned space_id, unsigned wait4id)
: params(params) { : params(params) {
this->id = id; this->space_id = space_id;
this->order = (unsigned)global::actors.size(); this->actor_id = 1 + (unsigned)global::actors.size();
this->testcase = testcase; this->testcase = testcase;
this->wait4id = wait4id; this->wait4id = wait4id;
signal_nops = 0; signal_nops = 0;

View File

@ -78,7 +78,7 @@ struct actor_params_pod {
unsigned seed; unsigned seed;
unsigned test_duration; unsigned test_duration;
unsigned test_nrecords; unsigned test_nops;
unsigned nrepeat; unsigned nrepeat;
unsigned nthreads; unsigned nthreads;
@ -98,7 +98,7 @@ struct actor_params_pod {
}; };
struct actor_config_pod { struct actor_config_pod {
unsigned id, order; unsigned actor_id, space_id;
actor_testcase testcase; actor_testcase testcase;
unsigned wait4id; unsigned wait4id;
unsigned signal_nops; unsigned signal_nops;
@ -123,8 +123,8 @@ struct actor_config : public config::actor_config_pod {
bool wanna_event4signalling() const { return true /* TODO ? */; } bool wanna_event4signalling() const { return true /* TODO ? */; }
actor_config(actor_testcase testcase, const actor_params &params, unsigned id, actor_config(actor_testcase testcase, const actor_params &params,
unsigned wait4id); unsigned space_id, unsigned wait4id);
actor_config(const char *str) { actor_config(const char *str) {
if (!deserialize(str, *this)) if (!deserialize(str, *this))
@ -140,7 +140,7 @@ struct actor_config : public config::actor_config_pod {
bool is_waitable(size_t nops) const { bool is_waitable(size_t nops) const {
switch (testcase) { switch (testcase) {
case ac_hill: case ac_hill:
if (!params.test_nrecords || params.test_nrecords >= nops) if (!params.test_nops || params.test_nops >= nops)
return true; return true;
default: default:
return false; return false;

View File

@ -24,7 +24,8 @@ bool testcase_deadread::setup() {
} }
bool testcase_deadread::run() { bool testcase_deadread::run() {
/* TODO */ db_open();
txn_begin(true);
return true; return true;
} }
@ -33,7 +34,7 @@ bool testcase_deadread::teardown() {
cursor_guard.release(); cursor_guard.release();
txn_guard.release(); txn_guard.release();
db_guard.release(); db_guard.release();
return true; return inherited::teardown();
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -48,7 +49,8 @@ bool testcase_deadwrite::setup() {
} }
bool testcase_deadwrite::run() { bool testcase_deadwrite::run() {
/* TODO */ db_open();
txn_begin(false);
return true; return true;
} }
@ -57,5 +59,5 @@ bool testcase_deadwrite::teardown() {
cursor_guard.release(); cursor_guard.release();
txn_guard.release(); txn_guard.release();
db_guard.release(); db_guard.release();
return true; return inherited::teardown();
} }

View File

@ -26,7 +26,7 @@ bool testcase_hill::setup() {
} }
bool testcase_hill::run() { bool testcase_hill::run() {
mdbx_open(); db_open();
/* TODO */ /* TODO */
return true; return true;
} }

View File

@ -19,13 +19,47 @@ bool testcase_jitter::setup() {
if (!inherited::setup()) if (!inherited::setup())
return false; return false;
/* TODO */
log_trace("<< setup"); log_trace("<< setup");
return true; return true;
} }
bool testcase_jitter::run() { return true; } bool testcase_jitter::run() {
while (should_continue()) {
jitter_delay();
db_open();
if (flipcoin()) {
jitter_delay();
txn_begin(true);
jitter_delay();
txn_end(false);
}
jitter_delay();
txn_begin(mode_readonly());
jitter_delay();
if (!mode_readonly()) {
/* TODO:
* - db_sequence()
* - db_setsize()
* ...
*/
}
txn_end(false);
if (flipcoin()) {
jitter_delay();
txn_begin(true);
jitter_delay();
txn_end(false);
}
jitter_delay();
db_close();
report(1);
}
return true;
}
bool testcase_jitter::teardown() { bool testcase_jitter::teardown() {
log_trace(">> teardown"); log_trace(">> teardown");

View File

@ -44,7 +44,7 @@ void actor_params::set_defaults(void) {
seed = 1; seed = 1;
test_duration = 0; test_duration = 0;
test_nrecords = 1000; test_nops = 1000;
nrepeat = 1; nrepeat = 1;
nthreads = 1; nthreads = 1;
@ -63,6 +63,11 @@ void actor_params::set_defaults(void) {
max_readers = 42; max_readers = 42;
max_tables = 42; max_tables = 42;
global::config::timeout_duration_seconds = 0 /* infinite */;
global::config::dump_config = true;
global::config::cleanup_before = true;
global::config::cleanup_after = true;
} }
namespace global { namespace global {
@ -72,12 +77,15 @@ std::unordered_map<unsigned, actor_config *> events;
std::unordered_map<mdbx_pid_t, actor_config *> pid2actor; std::unordered_map<mdbx_pid_t, actor_config *> pid2actor;
std::set<std::string> databases; std::set<std::string> databases;
unsigned nactors; unsigned nactors;
chrono::time start_motonic;
chrono::time deadline_motonic;
bool singlemode;
namespace config { namespace config {
unsigned timeout; unsigned timeout_duration_seconds;
bool dump_config; bool dump_config;
bool dont_cleanup_before; bool cleanup_before;
bool dont_cleanup_after; bool cleanup_after;
} /* namespace config */ } /* namespace config */
} /* namespace global */ } /* namespace global */
@ -121,122 +129,154 @@ int main(int argc, char *const argv[]) {
logging::setup((logging::loglevel)params.loglevel, "main"); logging::setup((logging::loglevel)params.loglevel, "main");
unsigned lastid = 0; unsigned lastid = 0;
if (argc == 2 && strncmp(argv[1], "--case=", 7) == 0) { for (int narg = 1; narg < argc; ++narg) {
const char *casename = argv[1] + 7;
if (!testcase_setup(casename, params, lastid))
failure("unknown testcase `%s`", casename);
} else {
for (int i = 1; i < argc;) {
const char *value = nullptr; const char *value = nullptr;
if (config::parse_option(argc, argv, i, "basic", nullptr)) {
bool ok = testcase_setup("basic", params, lastid); if (config::parse_option(argc, argv, narg, "case", &value)) {
assert(ok); testcase_setup(value, params, lastid);
(void)ok; continue;
} else if (config::parse_option(argc, argv, i, "race", nullptr)) { }
bool ok = testcase_setup("race", params, lastid); if (config::parse_option(argc, argv, narg, "pathname", params.pathname_db))
assert(ok); continue;
(void)ok; if (config::parse_option(argc, argv, narg, "mode", params.mode_flags,
} else if (config::parse_option(argc, argv, i, "bench", nullptr)) { config::mode_bits))
bool ok = testcase_setup("bench", params, lastid); continue;
assert(ok); if (config::parse_option(argc, argv, narg, "table", params.table_flags,
(void)ok; config::table_bits))
} else if (config::parse_option(argc, argv, i, "pathname", continue;
params.pathname_db) || if (config::parse_option(argc, argv, narg, "size", params.size,
config::parse_option(argc, argv, i, "mode", params.mode_flags, config::binary, 4096 * 4))
config::mode_bits) || continue;
config::parse_option(argc, argv, i, "table", if (config::parse_option(argc, argv, narg, "seed", params.seed,
params.table_flags, config::table_bits) || config::no_scale))
config::parse_option(argc, argv, i, "size", params.size, continue;
config::binary, 4096 * 4) || if (config::parse_option(argc, argv, narg, "repeat", params.nrepeat,
config::parse_option(argc, argv, i, "seed", params.seed, config::no_scale))
config::no_scale) || continue;
config::parse_option(argc, argv, i, "repeat", params.nrepeat, if (config::parse_option(argc, argv, narg, "threads", params.nthreads,
config::no_scale) || config::no_scale, 1, 64))
config::parse_option(argc, argv, i, "threads", params.nthreads, continue;
config::no_scale, 1, 64) || if (config::parse_option(argc, argv, narg, "timeout",
config::parse_option(argc, argv, i, "timeout", global::config::timeout_duration_seconds,
global::config::timeout, config::duration, config::duration, 1))
1) || continue;
config::parse_option(argc, argv, i, "keylen.min", if (config::parse_option(argc, argv, narg, "keylen.min", params.keylen_min,
params.keylen_min, config::no_scale, 0, config::no_scale, 0, params.keylen_max))
params.keylen_max) || continue;
config::parse_option(argc, argv, i, "keylen.max", if (config::parse_option(argc, argv, narg, "keylen.max", params.keylen_max,
params.keylen_max, config::no_scale, config::no_scale, params.keylen_min,
params.keylen_min, mdbx_get_maxkeysize(0)))
mdbx_get_maxkeysize(0)) || continue;
config::parse_option(argc, argv, i, "datalen.min", if (config::parse_option(argc, argv, narg, "datalen.min",
params.datalen_min, config::no_scale, 0, params.datalen_min, config::no_scale, 0,
params.datalen_max) || params.datalen_max))
config::parse_option(argc, argv, i, "datalen.max", continue;
if (config::parse_option(argc, argv, narg, "datalen.max",
params.datalen_max, config::no_scale, params.datalen_max, config::no_scale,
params.datalen_min, MDBX_MAXDATASIZE) || params.datalen_min, MDBX_MAXDATASIZE))
config::parse_option(argc, argv, i, "batch.read",
params.batch_read, config::no_scale, 1) ||
config::parse_option(argc, argv, i, "batch.write",
params.batch_write, config::no_scale,
1) ||
config::parse_option(argc, argv, i, "delay", params.delaystart,
config::duration) ||
config::parse_option(argc, argv, i, "wait4ops",
params.waitfor_nops, config::decimal) ||
config::parse_option(argc, argv, i, "drop",
params.drop_table) ||
config::parse_option(argc, argv, i, "dump-config",
global::config::dump_config) ||
config::parse_option(argc, argv, i, "dont-cleanup-before",
global::config::dont_cleanup_before) ||
config::parse_option(argc, argv, i, "dont-cleanup-after",
global::config::dont_cleanup_after) ||
config::parse_option(argc, argv, i, "max-readers",
params.max_readers, config::no_scale, 1,
255) ||
config::parse_option(argc, argv, i, "max-tables",
params.max_tables, config::no_scale, 1,
INT16_MAX) ||
false) {
continue; continue;
} else if (config::parse_option(argc, argv, i, "no-delay", nullptr)) { if (config::parse_option(argc, argv, narg, "batch.read", params.batch_read,
config::no_scale, 1))
continue;
if (config::parse_option(argc, argv, narg, "batch.write",
params.batch_write, config::no_scale, 1))
continue;
if (config::parse_option(argc, argv, narg, "delay", params.delaystart,
config::duration))
continue;
if (config::parse_option(argc, argv, narg, "wait4ops", params.waitfor_nops,
config::decimal))
continue;
if (config::parse_option(argc, argv, narg, "drop", params.drop_table))
continue;
if (config::parse_option(argc, argv, narg, "dump-config",
global::config::dump_config))
continue;
if (config::parse_option(argc, argv, narg, "cleanup-before",
global::config::cleanup_before))
continue;
if (config::parse_option(argc, argv, narg, "cleanup-after",
global::config::cleanup_after))
continue;
if (config::parse_option(argc, argv, narg, "max-readers",
params.max_readers, config::no_scale, 1, 255))
continue;
if (config::parse_option(argc, argv, narg, "max-tables", params.max_tables,
config::no_scale, 1, INT16_MAX))
continue;
if (config::parse_option(argc, argv, narg, "no-delay", nullptr)) {
params.delaystart = 0; params.delaystart = 0;
} else if (config::parse_option(argc, argv, i, "no-wait", nullptr)) {
params.waitfor_nops = 0;
} else if (config::parse_option(argc, argv, i, "duration",
params.test_duration, config::duration,
1)) {
params.test_nrecords = 0;
continue; continue;
} else if (config::parse_option(argc, argv, i, "records", }
params.test_nrecords, config::decimal, if (config::parse_option(argc, argv, narg, "no-wait", nullptr)) {
1)) { params.waitfor_nops = 0;
continue;
}
if (config::parse_option(argc, argv, narg, "duration", params.test_duration,
config::duration, 1)) {
params.test_nops = 0;
continue;
}
if (config::parse_option(argc, argv, narg, "nops", params.test_nops,
config::decimal, 1)) {
params.test_duration = 0; params.test_duration = 0;
continue; continue;
} else if (config::parse_option(argc, argv, i, "hill", &value)) { }
if (config::parse_option(argc, argv, narg, "hill", &value)) {
configure_actor(lastid, ac_hill, value, params); configure_actor(lastid, ac_hill, value, params);
continue; continue;
} else if (config::parse_option(argc, argv, i, "jitter", nullptr)) { }
if (config::parse_option(argc, argv, narg, "jitter", nullptr)) {
configure_actor(lastid, ac_jitter, value, params); configure_actor(lastid, ac_jitter, value, params);
continue; continue;
} else if (config::parse_option(argc, argv, i, "dead.reader", nullptr)) { }
if (config::parse_option(argc, argv, narg, "dead.reader", nullptr)) {
configure_actor(lastid, ac_deadread, value, params); configure_actor(lastid, ac_deadread, value, params);
continue; continue;
} else if (config::parse_option(argc, argv, i, "dead.writer", nullptr)) { }
if (config::parse_option(argc, argv, narg, "dead.writer", nullptr)) {
configure_actor(lastid, ac_deadwrite, value, params); configure_actor(lastid, ac_deadwrite, value, params);
continue; continue;
} else {
failure("Unknown option '%s'\n", argv[i]);
}
} }
if (*argv[narg] != '-')
testcase_setup(argv[narg], params, lastid);
else
failure("Unknown option '%s'\n", argv[narg]);
} }
if (global::config::dump_config) if (global::config::dump_config)
config::dump(); config::dump();
bool failed = false; //--------------------------------------------------------------------------
if (global::actors.size()) {
logging::setup("overlord");
if (!global::config::dont_cleanup_before) if (global::actors.empty()) {
log_notice("no testcase(s) configured, exiting");
return EXIT_SUCCESS;
}
bool failed = false;
global::start_motonic = chrono::now_motonic();
global::deadline_motonic.fixedpoint =
(global::config::timeout_duration_seconds == 0)
? chrono::infinite().fixedpoint
: global::start_motonic.fixedpoint +
chrono::from_seconds(global::config::timeout_duration_seconds)
.fixedpoint;
if (global::config::cleanup_before)
cleanup(); cleanup();
if (global::actors.size() == 1) {
logging::setup("main");
global::singlemode = true;
if (!test_execute(global::actors.front()))
failed = true;
} else {
logging::setup("overlord");
log_trace("=== preparing...");
log_trace(">> osal_setup"); log_trace(">> osal_setup");
osal_setup(global::actors); osal_setup(global::actors);
log_trace("<< osal_setup"); log_trace("<< osal_setup");
@ -250,33 +290,34 @@ int main(int argc, char *const argv[]) {
log_trace(">> killall_actors"); log_trace(">> killall_actors");
osal_killall_actors(); osal_killall_actors();
log_trace("<< killall_actors"); log_trace("<< killall_actors");
failure("Failed to start actor #%u (%s)\n", a.order, test_strerror(rc)); failure("Failed to start actor #%u (%s)\n", a.actor_id,
test_strerror(rc));
} }
global::pid2actor[pid] = &a; global::pid2actor[pid] = &a;
} }
log_trace("=== ready to start...");
atexit(osal_killall_actors); atexit(osal_killall_actors);
log_trace(">> wait4barrier"); log_trace(">> wait4barrier");
osal_wait4barrier(); osal_wait4barrier();
log_trace("<< wait4barrier"); log_trace("<< wait4barrier");
}
time_t timestamp_start = time(nullptr);
size_t left = global::actors.size(); size_t left = global::actors.size();
log_trace("=== polling...");
while (left > 0) { while (left > 0) {
unsigned timeout = INT_MAX; unsigned timeout_seconds_left = INT_MAX;
if (global::config::timeout) { chrono::time now_motonic = chrono::now_motonic();
time_t timestamp_now = time(nullptr); if (now_motonic.fixedpoint >= global::deadline_motonic.fixedpoint)
if (timestamp_now - timestamp_start > global::config::timeout) timeout_seconds_left = 0;
timeout = 0; else {
else chrono::time left_motonic;
timeout = global::config::timeout - left_motonic.fixedpoint =
(unsigned)(timestamp_now - timestamp_start); global::deadline_motonic.fixedpoint - now_motonic.fixedpoint;
timeout_seconds_left = left_motonic.seconds();
} }
mdbx_pid_t pid; mdbx_pid_t pid;
int rc = osal_actor_poll(pid, timeout); int rc = osal_actor_poll(pid, timeout_seconds_left);
if (rc) if (rc)
failure("Poll error: %s (%d)\n", test_strerror(rc), rc); failure("Poll error: %s (%d)\n", test_strerror(rc), rc);
@ -286,22 +327,23 @@ int main(int argc, char *const argv[]) {
if (!actor) if (!actor)
continue; continue;
log_info("actor #%u, id %d, pid %u: %s\n", actor->order, actor->id, pid, log_info("actor #%u, id %d, pid %u: %s\n", actor->actor_id,
status2str(status)); actor->space_id, pid, status2str(status));
if (status > as_running) { if (status > as_running) {
left -= 1; left -= 1;
if (status != as_successful) if (status != as_successful)
failed = true; failed = true;
} }
} else { } else {
if (global::config::timeout && if (timeout_seconds_left == 0)
time(nullptr) - timestamp_start > global::config::timeout)
failure("Timeout\n"); failure("Timeout\n");
} }
} }
log_trace("=== done...");
}
log_notice("OVERALL: %s\n", failed ? "Failed" : "Successful"); log_notice("RESULT: %s\n", failed ? "Failed" : "Successful");
if (!global::config::dont_cleanup_before) { if (global::config::cleanup_before) {
if (failed) if (failed)
log_info("skip cleanup"); log_info("skip cleanup");
else else

View File

@ -24,11 +24,11 @@
struct shared_t { struct shared_t {
pthread_barrier_t barrier; pthread_barrier_t barrier;
pthread_mutex_t mutex; pthread_mutex_t mutex;
pthread_cond_t conds[0]; size_t conds_size;
pthread_cond_t conds[1];
}; };
static shared_t *shared; static shared_t *shared;
static std::unordered_map<unsigned, pthread_cond_t *> events;
void osal_wait4barrier(void) { void osal_wait4barrier(void) {
assert(shared != nullptr && shared != MAP_FAILED); assert(shared != nullptr && shared != MAP_FAILED);
@ -65,13 +65,8 @@ void osal_setup(const std::vector<actor_config> &actors) {
if (rc) if (rc)
failure_perror("pthread_condattr_setpshared()", rc); failure_perror("pthread_condattr_setpshared()", rc);
size_t n = 0;
for (const auto &a : actors)
if (a.wanna_event4signalling())
++n;
shared = (shared_t *)mmap( shared = (shared_t *)mmap(
nullptr, sizeof(shared_t) + n * sizeof(pthread_cond_t), nullptr, sizeof(shared_t) + actors.size() * sizeof(pthread_cond_t),
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (MAP_FAILED == (void *)shared) if (MAP_FAILED == (void *)shared)
failure_perror("mmap(shared_conds)", errno); failure_perror("mmap(shared_conds)", errno);
@ -84,24 +79,15 @@ void osal_setup(const std::vector<actor_config> &actors) {
if (rc) if (rc)
failure_perror("pthread_barrier_init(shared)", rc); failure_perror("pthread_barrier_init(shared)", rc);
auto a = actors.begin(); const size_t n = actors.size() + 1;
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
pthread_cond_t *event = &shared->conds[i]; pthread_cond_t *event = &shared->conds[i];
rc = pthread_cond_init(event, &condattr); rc = pthread_cond_init(event, &condattr);
if (rc) if (rc)
failure_perror("pthread_cond_init(shared)", rc); failure_perror("pthread_cond_init(shared)", rc);
log_trace("osal_setup: event(shared pthread_cond) %zu -> %p", i, event);
unsigned id = 0;
while (a != actors.end()) {
if (a->wanna_event4signalling()) {
id = a->id;
break;
}
++a;
}
assert(id != 0);
events[id] = event;
} }
shared->conds_size = actors.size() + 1;
pthread_barrierattr_destroy(&barrierattr); pthread_barrierattr_destroy(&barrierattr);
pthread_condattr_destroy(&condattr); pthread_condattr_destroy(&condattr);
@ -110,7 +96,10 @@ void osal_setup(const std::vector<actor_config> &actors) {
void osal_broadcast(unsigned id) { void osal_broadcast(unsigned id) {
assert(shared != nullptr && shared != MAP_FAILED); assert(shared != nullptr && shared != MAP_FAILED);
int rc = pthread_cond_broadcast(events.at(id)); log_trace("osal_broadcast: event %u", id);
if (id >= shared->conds_size)
failure("osal_broadcast: id > limit");
int rc = pthread_cond_broadcast(shared->conds + id);
if (rc) if (rc)
failure_perror("sem_post(shared)", rc); failure_perror("sem_post(shared)", rc);
} }
@ -118,11 +107,15 @@ void osal_broadcast(unsigned id) {
int osal_waitfor(unsigned id) { int osal_waitfor(unsigned id) {
assert(shared != nullptr && shared != MAP_FAILED); assert(shared != nullptr && shared != MAP_FAILED);
log_trace("osal_waitfor: event %u", id);
if (id >= shared->conds_size)
failure("osal_waitfor: id > limit");
int rc = pthread_mutex_lock(&shared->mutex); int rc = pthread_mutex_lock(&shared->mutex);
if (rc != 0) if (rc != 0)
failure_perror("pthread_mutex_lock(shared)", rc); failure_perror("pthread_mutex_lock(shared)", rc);
rc = pthread_cond_wait(events.at(id), &shared->mutex); rc = pthread_cond_wait(shared->conds + id, &shared->mutex);
if (rc && rc != EINTR) if (rc && rc != EINTR)
failure_perror("pthread_cond_wait(shared)", rc); failure_perror("pthread_cond_wait(shared)", rc);
@ -173,6 +166,7 @@ int osal_actor_start(const actor_config &config, mdbx_pid_t &pid) {
if (pid < 0) if (pid < 0)
return errno; return errno;
log_trace("osal_actor_start: fork pid %i for %u", pid, config.actor_id);
childs[pid] = as_running; childs[pid] = as_running;
return 0; return 0;
} }

View File

@ -14,7 +14,7 @@
#include "test.h" #include "test.h"
static std::unordered_map<unsigned, HANDLE> events; static std::vector<HANDLE> events;
static HANDLE hBarrierSemaphore, hBarrierEvent; static HANDLE hBarrierSemaphore, hBarrierEvent;
static int waitstatus2errcode(DWORD result) { static int waitstatus2errcode(DWORD result) {
@ -63,15 +63,17 @@ static HANDLE make_inharitable(HANDLE hHandle) {
} }
void osal_setup(const std::vector<actor_config> &actors) { void osal_setup(const std::vector<actor_config> &actors) {
size_t n = 0; assert(events.empty());
for (const auto &a : actors) { const size_t n = actors.size() + 1;
if (a.wanna_event4signalling()) { events.reserve(n);
for (size_t i = 0; i < n; ++i) {
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!hEvent) if (!hEvent)
failure_perror("CreateEvent()", GetLastError()); failure_perror("CreateEvent()", GetLastError());
hEvent = make_inharitable(hEvent); hEvent = make_inharitable(hEvent);
events[a.id] = hEvent; log_trace("osal_setup: event %zu -> %p", i, hEvent);
} events.push_back(hEvent);
} }
hBarrierSemaphore = CreateSemaphore(NULL, 0, (LONG)actors.size(), NULL); hBarrierSemaphore = CreateSemaphore(NULL, 0, (LONG)actors.size(), NULL);
@ -86,11 +88,13 @@ void osal_setup(const std::vector<actor_config> &actors) {
} }
void osal_broadcast(unsigned id) { void osal_broadcast(unsigned id) {
log_trace("osal_broadcast: event %u", id);
if (!SetEvent(events.at(id))) if (!SetEvent(events.at(id)))
failure_perror("SetEvent()", GetLastError()); failure_perror("SetEvent()", GetLastError());
} }
int osal_waitfor(unsigned id) { int osal_waitfor(unsigned id) {
log_trace("osal_waitfor: event %u", id);
DWORD rc = WaitForSingleObject(events.at(id), INFINITE); DWORD rc = WaitForSingleObject(events.at(id), INFINITE);
return waitstatus2errcode(rc); return waitstatus2errcode(rc);
} }

View File

@ -53,25 +53,37 @@ const char *status2str(actor_status status) {
static void mdbx_debug_logger(int type, const char *function, int line, static void mdbx_debug_logger(int type, const char *function, int line,
const char *msg, va_list args) { const char *msg, va_list args) {
logging::loglevel level = logging::trace; 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) if (type & MDBX_DBG_PRINT)
level = logging::info; level = logging::verbose;
if (type & MDBX_DBG_ASSERT) { if (type & MDBX_DBG_ASSERT) {
log_error("libmdbx assertion failure: %s, %d", log_error("mdbx: assertion failure: %s, %d",
function ? function : "unknown", line); function ? function : "unknown", line);
level = logging::failure; level = logging::failure;
} }
output(level, msg, args); if (logging::output(level, "mdbx: "))
logging::feed(msg, args);
if (type & MDBX_DBG_ASSERT) if (type & MDBX_DBG_ASSERT)
abort(); abort();
} }
void testcase::mdbx_prepare() { void testcase::db_prepare() {
log_trace(">> mdbx_prepare"); log_trace(">> db_prepare");
assert(!db_guard);
int rc = mdbx_setup_debug(MDBX_DBG_DNT, mdbx_debug_logger, MDBX_DBG_DNT); int mdbx_dbg_opts = MDBX_DBG_ASSERT;
log_info("libmdbx debug-flags: 0x%02x", rc); if (config.params.loglevel <= logging::trace)
mdbx_dbg_opts |= MDBX_DBG_TRACE;
if (config.params.loglevel <= logging::verbose)
mdbx_dbg_opts |= MDBX_DBG_PRINT;
int rc = mdbx_setup_debug(mdbx_dbg_opts, mdbx_debug_logger, MDBX_DBG_DNT);
log_info("set mdbx debug-opts: 0x%02x", rc);
MDB_env *env = nullptr; MDB_env *env = nullptr;
rc = mdbx_env_create(&env); rc = mdbx_env_create(&env);
@ -97,83 +109,163 @@ void testcase::mdbx_prepare() {
if (rc != MDB_SUCCESS) if (rc != MDB_SUCCESS)
failure_perror("mdbx_env_set_mapsize()", rc); failure_perror("mdbx_env_set_mapsize()", rc);
log_trace("<< mdbx_prepare"); log_trace("<< db_prepare");
} }
void testcase::mdbx_open() { void testcase::db_open() {
log_trace(">> mdbx_open"); log_trace(">> db_open");
if (!db_guard)
db_prepare();
int rc = mdbx_env_open(db_guard.get(), config.params.pathname_db.c_str(), int rc = mdbx_env_open(db_guard.get(), config.params.pathname_db.c_str(),
(unsigned)config.params.mode_flags, 0640); (unsigned)config.params.mode_flags, 0640);
if (rc != MDB_SUCCESS) if (rc != MDB_SUCCESS)
failure_perror("mdbx_env_open()", rc); failure_perror("mdbx_env_open()", rc);
log_trace("<< mdbx_open");
log_trace("<< db_open");
} }
void testcase::mdbx_close() { void testcase::db_close() {
log_trace(">> mdbx_close"); log_trace(">> db_close");
cursor_guard.reset(); cursor_guard.reset();
txn_guard.reset(); txn_guard.reset();
db_guard.reset(); db_guard.reset();
log_trace("<< mdbx_close"); log_trace("<< db_close");
}
void testcase::txn_begin(bool readonly) {
log_trace(">> txn_begin(%s)", readonly ? "read-only" : "read-write");
assert(!txn_guard);
MDB_txn *txn = nullptr;
int rc =
mdbx_txn_begin(db_guard.get(), nullptr, readonly ? MDB_RDONLY : 0, &txn);
if (rc != MDB_SUCCESS)
failure_perror("mdbx_txn_begin()", rc);
txn_guard.reset(txn);
log_trace("<< txn_begin(%s)", readonly ? "read-only" : "read-write");
}
void testcase::txn_end(bool abort) {
log_trace(">> txn_end(%s)", abort ? "abort" : "commit");
assert(txn_guard);
MDB_txn *txn = txn_guard.release();
if (abort) {
int rc = mdbx_txn_abort(txn);
if (rc != MDB_SUCCESS)
failure_perror("mdbx_txn_abort()", rc);
} else {
int rc = mdbx_txn_commit(txn);
if (rc != MDB_SUCCESS)
failure_perror("mdbx_txn_commit()", rc);
}
log_trace("<< txn_end(%s)", abort ? "abort" : "commit");
} }
bool testcase::wait4start() { bool testcase::wait4start() {
if (config.wait4id) { if (config.wait4id) {
log_trace(">> wait4start(%u)", config.wait4id); log_trace(">> wait4start(%u)", config.wait4id);
assert(!global::singlemode);
int rc = osal_waitfor(config.wait4id); int rc = osal_waitfor(config.wait4id);
if (rc) { if (rc) {
log_trace("<< wait4start(%u), failed %s", config.wait4id, log_trace("<< wait4start(%u), failed %s", config.wait4id,
test_strerror(rc)); test_strerror(rc));
return false; return false;
} }
return true;
} else { } else {
log_trace("== wait4start(not needed)"); log_trace("== skip wait4start: not needed");
return true;
} }
if (config.params.delaystart) {
int rc = osal_delay(config.params.delaystart);
if (rc) {
log_trace("<< delay(%u), failed %s", config.params.delaystart,
test_strerror(rc));
return false;
}
} else {
log_trace("== skip delay: not needed");
}
return true;
} }
void testcase::report(size_t nops_done) { void testcase::report(size_t nops_done) {
if (config.signal_nops && !signalled && config.signal_nops <= nops_done) { nops_completed += nops_done;
log_trace(">> signal(n-ops %zu)", nops_done); log_verbose("== complete +%zu iteration, total %zu done", nops_done,
osal_broadcast(config.id); nops_completed);
if (config.signal_nops && !signalled &&
config.signal_nops <= nops_completed) {
log_trace(">> signal(n-ops %zu)", nops_completed);
if (!global::singlemode)
osal_broadcast(config.actor_id);
signalled = true; signalled = true;
log_trace("<< signal(n-ops %zu)", nops_done); log_trace("<< signal(n-ops %zu)", nops_completed);
} }
} }
void testcase::signal() { void testcase::signal() {
if (!signalled) { if (!signalled) {
log_trace(">> signal(forced)"); log_trace(">> signal(forced)");
osal_broadcast(config.id); if (!global::singlemode)
osal_broadcast(config.actor_id);
signalled = true; signalled = true;
log_trace("<< signal(forced)"); log_trace("<< signal(forced)");
} }
} }
bool testcase::setup() { bool testcase::setup() {
mdbx_prepare(); db_prepare();
return wait4start(); if (!wait4start())
return false;
start_timestamp = chrono::now_motonic();
return true;
} }
bool testcase::teardown() { bool testcase::teardown() {
log_trace(">> testcase::teardown"); log_trace(">> testcase::teardown");
signal(); signal();
mdbx_close(); db_close();
log_trace("<< testcase::teardown"); log_trace("<< testcase::teardown");
return true; return true;
} }
bool testcase::should_continue() const {
bool result = true;
if (config.params.test_duration) {
chrono::time since;
since.fixedpoint =
chrono::now_motonic().fixedpoint - start_timestamp.fixedpoint;
if (since.seconds() >= config.params.test_duration)
result = false;
}
if (config.params.test_nops && nops_completed >= config.params.test_nops)
result = false;
return result;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
bool test_execute(const actor_config &config) { bool test_execute(const actor_config &config) {
const mdbx_pid_t pid = osal_getpid(); const mdbx_pid_t pid = osal_getpid();
logging::setup((logging::loglevel)config.params.loglevel,
format("child_%u.%u", config.order, config.id));
if (global::singlemode) {
logging::setup(format("single_%s", testcase2str(config.testcase)));
} else {
logging::setup((logging::loglevel)config.params.loglevel,
format("child_%u.%u", config.actor_id, config.space_id));
log_trace(">> wait4barrier"); log_trace(">> wait4barrier");
osal_wait4barrier(); osal_wait4barrier();
log_trace("<< wait4barrier"); log_trace("<< wait4barrier");
}
try { try {
std::unique_ptr<testcase> test; std::unique_ptr<testcase> test;
@ -206,7 +298,7 @@ bool test_execute(const actor_config &config) {
return true; return true;
} }
} catch (const std::exception &pipets) { } catch (const std::exception &pipets) {
failure("Exception: %s", pipets.what()); failure("***** Exception: %s *****", pipets.what());
} }
return false; return false;
} }

View File

@ -24,7 +24,7 @@
bool test_execute(const actor_config &config); bool test_execute(const actor_config &config);
std::string thunk_param(const actor_config &config); std::string thunk_param(const actor_config &config);
bool testcase_setup(const char *casename, const actor_params &params, void testcase_setup(const char *casename, actor_params &params,
unsigned &lastid); unsigned &lastid);
void configure_actor(unsigned &lastid, const actor_testcase testcase, void configure_actor(unsigned &lastid, const actor_testcase testcase,
const char *id_cstr, const actor_params &params); const char *id_cstr, const actor_params &params);
@ -36,12 +36,16 @@ extern std::vector<actor_config> actors;
extern std::unordered_map<unsigned, actor_config *> events; extern std::unordered_map<unsigned, actor_config *> events;
extern std::unordered_map<mdbx_pid_t, actor_config *> pid2actor; extern std::unordered_map<mdbx_pid_t, actor_config *> pid2actor;
extern std::set<std::string> databases; extern std::set<std::string> databases;
extern unsigned nactors;
extern chrono::time start_motonic;
extern chrono::time deadline_motonic;
extern bool singlemode;
namespace config { namespace config {
extern unsigned timeout; extern unsigned timeout_duration_seconds;
extern bool dump_config; extern bool dump_config;
extern bool dont_cleanup_before; extern bool cleanup_before;
extern bool dont_cleanup_after; extern bool cleanup_after;
} /* namespace config */ } /* namespace config */
} /* namespace global */ } /* namespace global */
@ -80,19 +84,28 @@ protected:
scoped_cursor_guard cursor_guard; scoped_cursor_guard cursor_guard;
bool signalled; bool signalled;
void mdbx_prepare(); size_t nops_completed;
void mdbx_open(); chrono::time start_timestamp;
void mdbx_close();
void db_prepare();
void db_open();
void db_close();
void txn_begin(bool readonly);
void txn_end(bool abort);
bool wait4start(); bool wait4start();
void report(size_t nops_done); void report(size_t nops_done);
void signal(); void signal();
bool should_continue() const;
bool mode_readonly() const {
return (config.params.mode_flags & MDB_RDONLY) ? true : false;
}
public: public:
testcase(const actor_config &config, const mdbx_pid_t pid) testcase(const actor_config &config, const mdbx_pid_t pid)
: config(config), pid(pid) { : config(config), pid(pid), nops_completed(0) {
logging::setup(format("%s_%u.%u", testcase2str(config.testcase), start_timestamp.reset();
config.order, config.id));
} }
virtual bool setup(); virtual bool setup();