diff options
Diffstat (limited to 'src/test/system/systest_runnable.cc')
-rw-r--r-- | src/test/system/systest_runnable.cc | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/src/test/system/systest_runnable.cc b/src/test/system/systest_runnable.cc new file mode 100644 index 000000000..40f94c403 --- /dev/null +++ b/src/test/system/systest_runnable.cc @@ -0,0 +1,233 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2011 New Dream Network + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "include/compat.h" +#include "common/errno.h" +#include "systest_runnable.h" +#include "systest_settings.h" + +#include <errno.h> +#include <pthread.h> +#include <sstream> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <string> +#ifndef _WIN32 +#include <sys/syscall.h> +#include <sys/wait.h> +#endif +#include <sys/types.h> +#include <unistd.h> +#include <atomic> +#include <limits> +#include <vector> + +using std::ostringstream; +using std::string; + +static pid_t do_gettid(void) +{ +#if defined(__linux__) + return static_cast < pid_t >(syscall(SYS_gettid)); +#elif defined(_WIN32) + return static_cast < pid_t >(GetCurrentThreadId()); +#else + return static_cast < pid_t >(pthread_getthreadid_np()); +#endif +} + +std::atomic<unsigned> m_highest_id = { 0 }; + +SysTestRunnable:: +SysTestRunnable(int argc, const char **argv) + : m_argc(0), + m_argv(NULL), + m_argv_orig(NULL) +{ + m_started = false; + m_id = ++m_highest_id; + memset(&m_pthread, 0, sizeof(m_pthread)); + update_id_str(false); + set_argv(argc, argv); +} + +SysTestRunnable:: +~SysTestRunnable() +{ + set_argv(0, NULL); +} + +const char* SysTestRunnable:: +get_id_str(void) const +{ + return m_id_str; +} + +int SysTestRunnable:: +start() +{ + if (m_started) { + return -EDOM; + } + int ret; + bool use_threads = SysTestSettings::inst().use_threads(); + if (use_threads) { + ret = pthread_create(&m_pthread, NULL, systest_runnable_pthread_helper, + static_cast<void*>(this)); + if (ret) + return ret; + m_started = true; + } else { + #ifdef _WIN32 + printf("Using separate processes is not supported on Windows.\n"); + return -1; + #else + std::string err_msg; + ret = preforker.prefork(err_msg); + if (ret < 0) { + printf("prefork failed: %s\n", err_msg.c_str()); + return ret; + } + + if (preforker.is_child()) { + m_started = true; + void *retptr = systest_runnable_pthread_helper(static_cast<void*>(this)); + preforker.exit((int)(uintptr_t)retptr); + } else { + m_started = true; + } + #endif + } + return 0; +} + +std::string SysTestRunnable:: +join() +{ + if (!m_started) { + return "SysTestRunnable was never started."; + } + int ret; + bool use_threads = SysTestSettings::inst().use_threads(); + if (use_threads) { + void *ptrretval; + ret = pthread_join(m_pthread, &ptrretval); + if (ret) { + ostringstream oss; + oss << "pthread_join failed with error " << ret; + return oss.str(); + } + int retval = (int)(uintptr_t)ptrretval; + if (retval != 0) { + ostringstream oss; + oss << "ERROR " << retval; + return oss.str(); + } + return ""; + } else { + #ifdef _WIN32 + return "Using separate processes is not supported on Windows.\n"; + #else + std::string err_msg; + ret = preforker.parent_wait(err_msg); + return err_msg; + #endif + } +} + +std::string SysTestRunnable:: +run_until_finished(std::vector < SysTestRunnable * > &runnables) +{ + int index = 0; + for (std::vector < SysTestRunnable * >::const_iterator r = runnables.begin(); + r != runnables.end(); ++r) { + int ret = (*r)->start(); + if (ret) { + ostringstream oss; + oss << "run_until_finished: got error " << ret + << " when starting runnable " << index; + return oss.str(); + } + ++index; + } + + for (std::vector < SysTestRunnable * >::const_iterator r = runnables.begin(); + r != runnables.end(); ++r) { + std::string rstr = (*r)->join(); + if (!rstr.empty()) { + ostringstream oss; + oss << "run_until_finished: runnable " << (*r)->get_id_str() + << ": got error: " << rstr; + return oss.str(); + } + } + printf("*******************************\n"); + return ""; +} + +void *systest_runnable_pthread_helper(void *arg) +{ + SysTestRunnable *st = static_cast < SysTestRunnable * >(arg); + st->update_id_str(true); + printf("%s: starting.\n", st->get_id_str()); + int ret = st->run(); + printf("%s: shutting down.\n", st->get_id_str()); + return (void*)(uintptr_t)ret; +} + +void SysTestRunnable:: +update_id_str(bool started) +{ + bool use_threads = SysTestSettings::inst().use_threads(); + char extra[std::numeric_limits<int>::digits10 + 1]; + extra[0] = '\0'; + + if (started) { + if (use_threads) + snprintf(extra, sizeof(extra), "_[%d]", do_gettid()); + else + snprintf(extra, sizeof(extra), "_[%d]", getpid()); + } + if (use_threads) + snprintf(m_id_str, SysTestRunnable::ID_STR_SZ, "thread_%d%s", m_id, extra); + else + snprintf(m_id_str, SysTestRunnable::ID_STR_SZ, "process_%d%s", m_id, extra); +} + +// Copy argv so that if some fiend decides to modify it, it's ok. +void SysTestRunnable:: +set_argv(int argc, const char **argv) +{ + if (m_argv_orig != NULL) { + for (int i = 0; i < m_argc; ++i) + free((void*)(m_argv_orig[i])); + delete[] m_argv_orig; + m_argv_orig = NULL; + delete[] m_argv; + m_argv = NULL; + m_argc = 0; + } + if (argv == NULL) + return; + m_argc = argc; + m_argv_orig = new const char*[m_argc+1]; + for (int i = 0; i < m_argc; ++i) + m_argv_orig[i] = strdup(argv[i]); + m_argv_orig[argc] = NULL; + m_argv = new const char*[m_argc+1]; + for (int i = 0; i <= m_argc; ++i) + m_argv[i] = m_argv_orig[i]; +} |