diff options
Diffstat (limited to 'plugins/Dbx_mdbx/src/libmdbx/test/osal-unix.cc')
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/test/osal-unix.cc | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/osal-unix.cc b/plugins/Dbx_mdbx/src/libmdbx/test/osal-unix.cc new file mode 100644 index 0000000000..88a10f1175 --- /dev/null +++ b/plugins/Dbx_mdbx/src/libmdbx/test/osal-unix.cc @@ -0,0 +1,274 @@ +/* + * Copyright 2017 Leonid Yuriev <leo@yuriev.ru> + * and other libmdbx authors: please see AUTHORS file. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "test.h" + +#include <pthread.h> +#include <signal.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +struct shared_t { + pthread_barrier_t barrier; + pthread_mutex_t mutex; + size_t conds_size; + pthread_cond_t conds[1]; +}; + +static shared_t *shared; + +void osal_wait4barrier(void) { + 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); + } +} + +void osal_setup(const std::vector<actor_config> &actors) { + 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); + 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); + + rc = pthread_barrier_init(&shared->barrier, &barrierattr, actors.size() + 1); + if (rc) + failure_perror("pthread_barrier_init(shared)", rc); + + 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); + log_trace("osal_setup: event(shared pthread_cond) %" PRIuPTR " -> %p", i, + event); + } + shared->conds_size = actors.size() + 1; + + pthread_barrierattr_destroy(&barrierattr); + pthread_condattr_destroy(&condattr); + pthread_mutexattr_destroy(&mutexattr); +} + +void osal_broadcast(unsigned id) { + assert(shared != nullptr && shared != MAP_FAILED); + 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); +} + +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(shared->conds + 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); + + return (rc == 0) ? true : false; +} + +//----------------------------------------------------------------------------- + +const std::string +actor_config::osal_serialize(simple_checksum &checksum) const { + (void)checksum; + /* not used in workload, but just for testing */ + return "unix.fork"; +} + +bool actor_config::osal_deserialize(const char *str, const char *end, + simple_checksum &checksum) { + (void)checksum; + /* not used in workload, but just for testing */ + return strncmp(str, "unix.fork", 9) == 0 && str + 9 == end; +} + +//----------------------------------------------------------------------------- + +static std::unordered_map<pid_t, actor_status> childs; + +static void handler_SIGCHLD(int unused) { (void)unused; } + +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); + + pid = fork(); + + if (pid == 0) { + const bool result = test_execute(config); + exit(result ? EXIT_SUCCESS : EXIT_FAILURE); + } + + 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; +} + +actor_status osal_actor_info(const mdbx_pid_t pid) { return childs.at(pid); } + +void osal_killall_actors(void) { + for (auto &pair : childs) { + kill(pair.first, SIGKILL); + pair.second = as_killed; + } +} + +int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) { +retry: + int status, options = WNOHANG; +#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 (WIFSIGNALED(status) || WCOREDUMP(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) { + if (timeout && sleep(timeout)) + goto retry; + return 0; + } + + switch (errno) { + case EINTR: + pid = 0; + return 0; + + case ECHILD: + default: + pid = 0; + return errno; + } +} + +void osal_yield(void) { + if (sched_yield()) + failure_perror("sched_yield()", errno); +} + +void osal_udelay(unsigned us) { + chrono::time until, now = chrono::now_motonic(); + until.fixedpoint = now.fixedpoint + chrono::from_us(us).fixedpoint; + struct timespec ts; + + static unsigned threshold_us; + if (threshold_us == 0) { + if (clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts)) { + int rc = errno; + failure_perror("clock_getres(CLOCK_PROCESS_CPUTIME_ID)", rc); + } + chrono::time threshold = chrono::from_timespec(ts); + assert(threshold.seconds() == 0); + + threshold_us = chrono::fractional2us(threshold.fractional); + if (threshold_us < 1000) + threshold_us = 1000; + } + + ts.tv_sec = ts.tv_nsec = 0; + if (us > threshold_us) { + ts.tv_sec = us / 1000000u; + ts.tv_nsec = (us % 1000000u) * 1000u; + } + + do { + if (us > threshold_us) { + if (nanosleep(&ts, &ts)) { + int rc = errno; + /* if (rc == EINTR) { ... } ? */ + failure_perror("usleep()", rc); + } + us = ts.tv_sec * 1000000u + ts.tv_nsec / 1000u; + } + cpu_relax(); + + now = chrono::now_motonic(); + } while (until.fixedpoint > now.fixedpoint); +} + +bool osal_istty(int fd) { return isatty(fd) == 1; } |