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
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
$(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->signal_nops && i->signal_nops != params.waitfor_nops)
failure("Previous waitable actor (id=%u) already linked on %u-ops\n",
i->id, i->signal_nops);
wait4id = i->id;
i->actor_id, i->signal_nops);
wait4id = i->actor_id;
i->signal_nops = params.waitfor_nops;
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);
}
unsigned long id = 0;
unsigned id = 0;
if (!id_cstr || strcmp(id_cstr, "auto") == 0)
id = lastid + 1;
else {
@ -47,23 +47,26 @@ void configure_actor(unsigned &lastid, const actor_testcase testcase,
}
if (id < 1 || id > ACTOR_ID_MAX)
failure("Invalid actor-id %lu\n", id);
failure("Invalid actor-id %u\n", id);
lastid = id;
log_trace("configure_actor: %u for %s", id, testcase2str(testcase));
global::actors.emplace_back(actor_config(testcase, params, id, wait4id));
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) {
log_notice("testcase_setup(%s): TODO", casename);
if (strcmp(casename, "basic") == 0) {
log_notice(">>> testcase_setup(%s)", casename);
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 */

View File

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

View File

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

View File

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

View File

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

View File

@ -19,13 +19,47 @@ bool testcase_jitter::setup() {
if (!inherited::setup())
return false;
/* TODO */
log_trace("<< setup");
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() {
log_trace(">> teardown");

View File

@ -44,7 +44,7 @@ void actor_params::set_defaults(void) {
seed = 1;
test_duration = 0;
test_nrecords = 1000;
test_nops = 1000;
nrepeat = 1;
nthreads = 1;
@ -63,6 +63,11 @@ void actor_params::set_defaults(void) {
max_readers = 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 {
@ -72,12 +77,15 @@ std::unordered_map<unsigned, actor_config *> events;
std::unordered_map<mdbx_pid_t, actor_config *> pid2actor;
std::set<std::string> databases;
unsigned nactors;
chrono::time start_motonic;
chrono::time deadline_motonic;
bool singlemode;
namespace config {
unsigned timeout;
unsigned timeout_duration_seconds;
bool dump_config;
bool dont_cleanup_before;
bool dont_cleanup_after;
bool cleanup_before;
bool cleanup_after;
} /* namespace config */
} /* namespace global */
@ -121,122 +129,154 @@ int main(int argc, char *const argv[]) {
logging::setup((logging::loglevel)params.loglevel, "main");
unsigned lastid = 0;
if (argc == 2 && strncmp(argv[1], "--case=", 7) == 0) {
const char *casename = argv[1] + 7;
if (!testcase_setup(casename, params, lastid))
failure("unknown testcase `%s`", casename);
} else {
for (int i = 1; i < argc;) {
for (int narg = 1; narg < argc; ++narg) {
const char *value = nullptr;
if (config::parse_option(argc, argv, i, "basic", nullptr)) {
bool ok = testcase_setup("basic", params, lastid);
assert(ok);
(void)ok;
} else if (config::parse_option(argc, argv, i, "race", nullptr)) {
bool ok = testcase_setup("race", params, lastid);
assert(ok);
(void)ok;
} else if (config::parse_option(argc, argv, i, "bench", nullptr)) {
bool ok = testcase_setup("bench", params, lastid);
assert(ok);
(void)ok;
} else if (config::parse_option(argc, argv, i, "pathname",
params.pathname_db) ||
config::parse_option(argc, argv, i, "mode", params.mode_flags,
config::mode_bits) ||
config::parse_option(argc, argv, i, "table",
params.table_flags, config::table_bits) ||
config::parse_option(argc, argv, i, "size", params.size,
config::binary, 4096 * 4) ||
config::parse_option(argc, argv, i, "seed", params.seed,
config::no_scale) ||
config::parse_option(argc, argv, i, "repeat", params.nrepeat,
config::no_scale) ||
config::parse_option(argc, argv, i, "threads", params.nthreads,
config::no_scale, 1, 64) ||
config::parse_option(argc, argv, i, "timeout",
global::config::timeout, config::duration,
1) ||
config::parse_option(argc, argv, i, "keylen.min",
params.keylen_min, config::no_scale, 0,
params.keylen_max) ||
config::parse_option(argc, argv, i, "keylen.max",
params.keylen_max, config::no_scale,
params.keylen_min,
mdbx_get_maxkeysize(0)) ||
config::parse_option(argc, argv, i, "datalen.min",
if (config::parse_option(argc, argv, narg, "case", &value)) {
testcase_setup(value, params, lastid);
continue;
}
if (config::parse_option(argc, argv, narg, "pathname", params.pathname_db))
continue;
if (config::parse_option(argc, argv, narg, "mode", params.mode_flags,
config::mode_bits))
continue;
if (config::parse_option(argc, argv, narg, "table", params.table_flags,
config::table_bits))
continue;
if (config::parse_option(argc, argv, narg, "size", params.size,
config::binary, 4096 * 4))
continue;
if (config::parse_option(argc, argv, narg, "seed", params.seed,
config::no_scale))
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, 0, params.keylen_max))
continue;
if (config::parse_option(argc, argv, narg, "keylen.max", params.keylen_max,
config::no_scale, params.keylen_min,
mdbx_get_maxkeysize(0)))
continue;
if (config::parse_option(argc, argv, narg, "datalen.min",
params.datalen_min, config::no_scale, 0,
params.datalen_max) ||
config::parse_option(argc, argv, i, "datalen.max",
params.datalen_max))
continue;
if (config::parse_option(argc, argv, narg, "datalen.max",
params.datalen_max, config::no_scale,
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) {
params.datalen_min, MDBX_MAXDATASIZE))
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;
} 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;
} else if (config::parse_option(argc, argv, i, "records",
params.test_nrecords, config::decimal,
1)) {
}
if (config::parse_option(argc, argv, narg, "no-wait", nullptr)) {
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;
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);
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);
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);
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);
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)
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();
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");
osal_setup(global::actors);
log_trace("<< osal_setup");
@ -250,33 +290,34 @@ int main(int argc, char *const argv[]) {
log_trace(">> killall_actors");
osal_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;
}
log_trace("=== ready to start...");
atexit(osal_killall_actors);
log_trace(">> wait4barrier");
osal_wait4barrier();
log_trace("<< wait4barrier");
}
time_t timestamp_start = time(nullptr);
size_t left = global::actors.size();
log_trace("=== polling...");
while (left > 0) {
unsigned timeout = INT_MAX;
if (global::config::timeout) {
time_t timestamp_now = time(nullptr);
if (timestamp_now - timestamp_start > global::config::timeout)
timeout = 0;
else
timeout = global::config::timeout -
(unsigned)(timestamp_now - timestamp_start);
unsigned timeout_seconds_left = INT_MAX;
chrono::time now_motonic = chrono::now_motonic();
if (now_motonic.fixedpoint >= global::deadline_motonic.fixedpoint)
timeout_seconds_left = 0;
else {
chrono::time left_motonic;
left_motonic.fixedpoint =
global::deadline_motonic.fixedpoint - now_motonic.fixedpoint;
timeout_seconds_left = left_motonic.seconds();
}
mdbx_pid_t pid;
int rc = osal_actor_poll(pid, timeout);
int rc = osal_actor_poll(pid, timeout_seconds_left);
if (rc)
failure("Poll error: %s (%d)\n", test_strerror(rc), rc);
@ -286,22 +327,23 @@ int main(int argc, char *const argv[]) {
if (!actor)
continue;
log_info("actor #%u, id %d, pid %u: %s\n", actor->order, actor->id, pid,
status2str(status));
log_info("actor #%u, id %d, pid %u: %s\n", actor->actor_id,
actor->space_id, pid, status2str(status));
if (status > as_running) {
left -= 1;
if (status != as_successful)
failed = true;
}
} else {
if (global::config::timeout &&
time(nullptr) - timestamp_start > global::config::timeout)
if (timeout_seconds_left == 0)
failure("Timeout\n");
}
}
log_trace("=== done...");
}
log_notice("OVERALL: %s\n", failed ? "Failed" : "Successful");
if (!global::config::dont_cleanup_before) {
log_notice("RESULT: %s\n", failed ? "Failed" : "Successful");
if (global::config::cleanup_before) {
if (failed)
log_info("skip cleanup");
else

View File

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

View File

@ -14,7 +14,7 @@
#include "test.h"
static std::unordered_map<unsigned, HANDLE> events;
static std::vector<HANDLE> events;
static HANDLE hBarrierSemaphore, hBarrierEvent;
static int waitstatus2errcode(DWORD result) {
@ -63,15 +63,17 @@ static HANDLE make_inharitable(HANDLE hHandle) {
}
void osal_setup(const std::vector<actor_config> &actors) {
size_t n = 0;
for (const auto &a : actors) {
if (a.wanna_event4signalling()) {
assert(events.empty());
const size_t n = actors.size() + 1;
events.reserve(n);
for (size_t i = 0; i < n; ++i) {
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!hEvent)
failure_perror("CreateEvent()", GetLastError());
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);
@ -86,11 +88,13 @@ void osal_setup(const std::vector<actor_config> &actors) {
}
void osal_broadcast(unsigned id) {
log_trace("osal_broadcast: event %u", id);
if (!SetEvent(events.at(id)))
failure_perror("SetEvent()", GetLastError());
}
int osal_waitfor(unsigned id) {
log_trace("osal_waitfor: event %u", id);
DWORD rc = WaitForSingleObject(events.at(id), INFINITE);
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,
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)
level = logging::info;
level = logging::verbose;
if (type & MDBX_DBG_ASSERT) {
log_error("libmdbx assertion failure: %s, %d",
log_error("mdbx: assertion failure: %s, %d",
function ? function : "unknown", line);
level = logging::failure;
}
output(level, msg, args);
if (logging::output(level, "mdbx: "))
logging::feed(msg, args);
if (type & MDBX_DBG_ASSERT)
abort();
}
void testcase::mdbx_prepare() {
log_trace(">> mdbx_prepare");
void testcase::db_prepare() {
log_trace(">> db_prepare");
assert(!db_guard);
int rc = mdbx_setup_debug(MDBX_DBG_DNT, mdbx_debug_logger, MDBX_DBG_DNT);
log_info("libmdbx debug-flags: 0x%02x", rc);
int mdbx_dbg_opts = MDBX_DBG_ASSERT;
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;
rc = mdbx_env_create(&env);
@ -97,83 +109,163 @@ void testcase::mdbx_prepare() {
if (rc != MDB_SUCCESS)
failure_perror("mdbx_env_set_mapsize()", rc);
log_trace("<< mdbx_prepare");
log_trace("<< db_prepare");
}
void testcase::mdbx_open() {
log_trace(">> mdbx_open");
void testcase::db_open() {
log_trace(">> db_open");
if (!db_guard)
db_prepare();
int rc = mdbx_env_open(db_guard.get(), config.params.pathname_db.c_str(),
(unsigned)config.params.mode_flags, 0640);
if (rc != MDB_SUCCESS)
failure_perror("mdbx_env_open()", rc);
log_trace("<< mdbx_open");
log_trace("<< db_open");
}
void testcase::mdbx_close() {
log_trace(">> mdbx_close");
void testcase::db_close() {
log_trace(">> db_close");
cursor_guard.reset();
txn_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() {
if (config.wait4id) {
log_trace(">> wait4start(%u)", config.wait4id);
assert(!global::singlemode);
int rc = osal_waitfor(config.wait4id);
if (rc) {
log_trace("<< wait4start(%u), failed %s", config.wait4id,
test_strerror(rc));
return false;
}
return true;
} else {
log_trace("== wait4start(not needed)");
return true;
log_trace("== skip wait4start: not needed");
}
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) {
if (config.signal_nops && !signalled && config.signal_nops <= nops_done) {
log_trace(">> signal(n-ops %zu)", nops_done);
osal_broadcast(config.id);
nops_completed += nops_done;
log_verbose("== complete +%zu iteration, total %zu done", nops_done,
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;
log_trace("<< signal(n-ops %zu)", nops_done);
log_trace("<< signal(n-ops %zu)", nops_completed);
}
}
void testcase::signal() {
if (!signalled) {
log_trace(">> signal(forced)");
osal_broadcast(config.id);
if (!global::singlemode)
osal_broadcast(config.actor_id);
signalled = true;
log_trace("<< signal(forced)");
}
}
bool testcase::setup() {
mdbx_prepare();
return wait4start();
db_prepare();
if (!wait4start())
return false;
start_timestamp = chrono::now_motonic();
return true;
}
bool testcase::teardown() {
log_trace(">> testcase::teardown");
signal();
mdbx_close();
db_close();
log_trace("<< testcase::teardown");
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) {
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");
osal_wait4barrier();
log_trace("<< wait4barrier");
}
try {
std::unique_ptr<testcase> test;
@ -206,7 +298,7 @@ bool test_execute(const actor_config &config) {
return true;
}
} catch (const std::exception &pipets) {
failure("Exception: %s", pipets.what());
failure("***** Exception: %s *****", pipets.what());
}
return false;
}

View File

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