summaryrefslogtreecommitdiffstats
path: root/src/test/system/systest_runnable.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/system/systest_runnable.cc')
-rw-r--r--src/test/system/systest_runnable.cc231
1 files changed, 231 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..f7342aa7d
--- /dev/null
+++ b/src/test/system/systest_runnable.cc
@@ -0,0 +1,231 @@
+// -*- 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)
+ preforker.exit(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];
+}