Hi Anders, Minor comments below. /Thanks HansN
-----Original Message----- From: Anders Widell [mailto:[email protected]] Sent: den 29 februari 2016 12:42 To: [email protected] Cc: [email protected] Subject: [devel] [PATCH 1 of 1] base: Add C++ utility functions [#836] Makefile.common | 3 +- configure.ac | 3 + osaf/libs/core/Makefile.am | 7 +- osaf/libs/core/cplusplus/Makefile.am | 21 + osaf/libs/core/cplusplus/base/Makefile.am | 42 ++ osaf/libs/core/cplusplus/base/getenv.cc | 160 ++++++++ osaf/libs/core/cplusplus/base/getenv.h | 40 ++ osaf/libs/core/cplusplus/base/macros.h | 33 + osaf/libs/core/cplusplus/base/process.cc | 204 ++++++++++ osaf/libs/core/cplusplus/base/process.h | 113 +++++ osaf/libs/core/cplusplus/base/tests/Makefile.am | 49 ++ osaf/libs/core/cplusplus/base/tests/getenv_test.cc | 105 +++++ osaf/libs/core/cplusplus/base/tests/mock_logtrace.cc | 32 + osaf/libs/core/cplusplus/base/tests/mock_logtrace.h | 23 + osaf/libs/core/cplusplus/base/tests/mock_osaf_abort.cc | 25 + osaf/libs/core/cplusplus/base/tests/mock_osaf_abort.h | 23 + osaf/libs/core/cplusplus/base/tests/mock_osafassert.cc | 27 + osaf/libs/core/cplusplus/base/tests/mock_osafassert.h | 23 + osaf/libs/core/cplusplus/base/tests/time_add_test.cc | 54 ++ osaf/libs/core/cplusplus/base/tests/time_compare_test.cc | 195 +++++++++ osaf/libs/core/cplusplus/base/tests/time_convert_test.cc | 89 ++++ osaf/libs/core/cplusplus/base/tests/time_subtract_test.cc | 52 ++ osaf/libs/core/cplusplus/base/time.h | 275 ++++++++++++++ 23 files changed, 1594 insertions(+), 4 deletions(-) Add the following C++ utility functions to the base component: * getenv.h - parsing of environment variables with default values * macros.h - selecting behaviour of copy constructors and assignment operators * process.h - time supervised execution of programs * time.h - conversion to/from POSIX timespec structures diff --git a/Makefile.common b/Makefile.common --- a/Makefile.common +++ b/Makefile.common @@ -6,7 +6,8 @@ CORE_INCLUDES = \ -I$(top_srcdir)/osaf/libs/core/include \ -I$(top_srcdir)/osaf/libs/core/leap/include \ -I$(top_srcdir)/osaf/libs/core/mds/include \ - -I$(top_srcdir)/osaf/libs/core/common/include + -I$(top_srcdir)/osaf/libs/core/common/include \ + -I$(top_srcdir)/osaf/libs/core/cplusplus AM_CPPFLAGS = \ $(CORE_INCLUDES) \ diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -774,6 +774,9 @@ AC_CONFIG_FILES([ osaf/libs/saf/libSaSmf/Makefile osaf/libs/saf/libSaSmf/pkgconfig/Makefile osaf/libs/saf/libSaSmf/pkgconfig/opensaf-smf.pc + osaf/libs/core/cplusplus/Makefile + osaf/libs/core/cplusplus/base/Makefile + osaf/libs/core/cplusplus/base/tests/Makefile osaf/services/Makefile osaf/services/infrastructure/Makefile osaf/services/infrastructure/dtms/Makefile diff --git a/osaf/libs/core/Makefile.am b/osaf/libs/core/Makefile.am --- a/osaf/libs/core/Makefile.am +++ b/osaf/libs/core/Makefile.am @@ -18,7 +18,7 @@ include $(top_srcdir)/Makefile.common MAINTAINERCLEANFILES = Makefile.in -SUBDIRS = include common leap mbcsv mds +SUBDIRS = include common leap mbcsv mds cplusplus LIB_VERSION = 0:2:0 @@ -30,11 +30,12 @@ libopensaf_core_la_CPPFLAGS = \ $(AM_CPPFLAGS) libopensaf_core_la_LDFLAGS = \ - -lpthread -ldl -lrt \ + -pthread -ldl -lrt -lstdc++ \ -version-number $(LIB_VERSION) libopensaf_core_la_LIBADD = \ $(top_builddir)/osaf/libs/core/common/libopensaf_common.la \ $(top_builddir)/osaf/libs/core/leap/libleap.la \ $(top_builddir)/osaf/libs/core/mbcsv/libmbca.la \ - $(top_builddir)/osaf/libs/core/mds/libmds.la + $(top_builddir)/osaf/libs/core/mds/libmds.la \ + $(top_builddir)/osaf/libs/core/cplusplus/base/libbase.la diff --git a/osaf/libs/core/cplusplus/Makefile.am b/osaf/libs/core/cplusplus/Makefile.am new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/Makefile.am @@ -0,0 +1,21 @@ +# -*- OpenSAF -*- +# +# (C) Copyright 2016 The OpenSAF Foundation +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed +# under the GNU Lesser General Public License Version 2.1, February 1999. +# The complete license can be accessed from the following location: +# http://opensource.org/licenses/lgpl-license.php +# See the Copying file included with the OpenSAF distribution for full +# licensing terms. +# +# Author(s): Ericsson AB +# + +include $(top_srcdir)/Makefile.common + +MAINTAINERCLEANFILES = Makefile.in + +SUBDIRS = base diff --git a/osaf/libs/core/cplusplus/base/Makefile.am b/osaf/libs/core/cplusplus/base/Makefile.am new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/Makefile.am @@ -0,0 +1,42 @@ +# -*- OpenSAF -*- +# +# (C) Copyright 2016 The OpenSAF Foundation +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed +# under the GNU Lesser General Public License Version 2.1, February 1999. +# The complete license can be accessed from the following location: +# http://opensource.org/licenses/lgpl-license.php +# See the Copying file included with the OpenSAF distribution for full +# licensing terms. +# +# Author(s): Ericsson AB +# + +include $(top_srcdir)/Makefile.common + +MAINTAINERCLEANFILES = Makefile.in + +DEFAULT_INCLUDES = + +SUBDIRS = tests + +noinst_HEADERS = \ + getenv.h \ + macros.h \ + process.h \ + time.h + +noinst_LTLIBRARIES = libbase.la + +libbase_CXXFLAGS =$(AM_CXXFLAGS) + +libbase_la_CPPFLAGS = \ + $(AM_CPPFLAGS) + +libbase_la_LDFLAGS = -static + +libbase_la_SOURCES = \ + getenv.cc \ + process.cc diff --git a/osaf/libs/core/cplusplus/base/getenv.cc b/osaf/libs/core/cplusplus/base/getenv.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/getenv.cc @@ -0,0 +1,160 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include "base/getenv.h" [HansN] from c++11 you can used #include <cstdint> +#include <stdint.h> +#include <cerrno> +#include <cstdlib> +#include <cinttypes> +#include <climits> +#include <string> +#include "logtrace.h" + +namespace { + +uint64_t GetEnvUnsigned(const char* environment_variable, + uint64_t default_value, uint64_t max_value); +int64_t GetEnvSigned(const char* environment_variable, + int64_t default_value, int64_t max_value, + int64_t min_value); +char* RemoveLeadingWhitespace(char* str); + +} // namespace + +namespace base { + +template<> +const char* GetEnv<const char*>(const char* environment_variable, + const char* default_value) { + const char* str = getenv(environment_variable); + if (str == nullptr) { + TRACE("%s is not set; using default value '%s'", environment_variable, + default_value); + str = default_value; + } else { + TRACE("%s = '%s'", environment_variable, str); + } + return str; +} + +template<> +std::string GetEnv<std::string>(const char* environment_variable, + std::string default_value) { + return GetEnv(environment_variable, default_value.c_str()); +} + +template<> +uint64_t GetEnv<uint64_t>(const char* environment_variable, + uint64_t default_value) { + return GetEnvUnsigned(environment_variable, default_value, UINT64_MAX); +} + +template<> +uint32_t GetEnv<uint32_t>(const char* environment_variable, + uint32_t default_value) { + return GetEnvUnsigned(environment_variable, default_value, UINT32_MAX); +} + +template<> +uint16_t GetEnv<uint16_t>(const char* environment_variable, + uint16_t default_value) { + return GetEnvUnsigned(environment_variable, default_value, UINT16_MAX); +} + +template<> +int64_t GetEnv<int64_t>(const char* environment_variable, + int64_t default_value) { + return GetEnvSigned(environment_variable, default_value, INT64_MAX, + INT64_MIN); +} + +template<> +int32_t GetEnv<int32_t>(const char* environment_variable, + int32_t default_value) { + return GetEnvSigned(environment_variable, default_value, INT32_MAX, + INT32_MIN); +} + +template<> +int16_t GetEnv<int16_t>(const char* environment_variable, + int16_t default_value) { + return GetEnvSigned(environment_variable, default_value, INT16_MAX, + INT16_MIN); +} + +} // namespace base + +namespace { + +uint64_t GetEnvUnsigned(const char* environment_variable, + uint64_t default_value, uint64_t max_value) { + char* str = RemoveLeadingWhitespace(getenv(environment_variable)); + uint64_t retval = default_value; + if (str != nullptr && *str != '\0') { + errno = 0; + char* endptr; + unsigned long long val = strtoull(str, &endptr, 0); + endptr = RemoveLeadingWhitespace(endptr); + if (errno == 0 && val <= max_value && *endptr == '\0') { + retval = val; + TRACE("%s = %" PRIu64, environment_variable, retval); + } else { + LOG_ER("%s has invalid value '%s'; using default value %" PRIu64, + environment_variable, str, default_value); + } + } else { + TRACE("%s is %s; using default value %" PRIu64, environment_variable, + str == nullptr ? "not set" : "empty", default_value); + + } + return retval; +} + +int64_t GetEnvSigned(const char* environment_variable, int64_t default_value, + int64_t max_value, int64_t min_value) { + char* str = RemoveLeadingWhitespace(getenv(environment_variable)); + int64_t retval = default_value; + if (str != nullptr && *str != '\0') { + errno = 0; + char* endptr; + long long val = strtoll(str, &endptr, 0); + endptr = RemoveLeadingWhitespace(endptr); + if (errno == 0 && val >= min_value && val <= max_value && *endptr == '\0') { + retval = val; + TRACE("%s = %" PRId64, environment_variable, retval); + } else { + LOG_ER("%s has invalid value '%s'; using default value %" PRId64, + environment_variable, str, default_value); + } + } else { + TRACE("%s is %s; using default value %" PRId64, environment_variable, + str == nullptr ? "not set" : "empty", default_value); + } + return retval; +} + +char* RemoveLeadingWhitespace(char* str) { + if (str != nullptr) { + while (*str == ' ' || *str == '\t' || *str == '\r' || *str == '\n') ++str; + } + return str; +} + +} // namespace diff --git a/osaf/libs/core/cplusplus/base/getenv.h b/osaf/libs/core/cplusplus/base/getenv.h new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/getenv.h @@ -0,0 +1,40 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#ifndef OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_GETENV_H_ +#define OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_GETENV_H_ + +namespace base { + +/** + * @brief Parse an environment variable with a default value. + * + * Returns the contents of an environment variable. If the variable is not set + * or not possible to parse, the @a default_value will be returned. This is a + * template functions, and the return type will be the same as the type of the + * @a default_value. Supported types are strings (const char*, std::string), as + * well as signed and unsigned integer types. When parsing integers, the + * prefixes 0x and 0 are supported for indicating that the number is hexadecimal + * or octal, respectively. The parsed value is always logged using the OpenSAF + * logtrace functionality, and parse errors are logged to syslog. + */ +template<typename T> +T GetEnv(const char* environment_variable, T default_value); + +} // namespace base + +#endif /* OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_GETENV_H_ */ diff --git a/osaf/libs/core/cplusplus/base/macros.h b/osaf/libs/core/cplusplus/base/macros.h new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/macros.h @@ -0,0 +1,33 @@ +/* + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#ifndef OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_MACROS_H_ +#define OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_MACROS_H_ + +#define USE_DEFAULT_COPY_AND_MOVE_OPERATORS(className) \ + className(className&&) = default; \ + className(const className&) = default; \ + className& operator=(className&&) = default; \ + className& operator=(const className&) = default + +#define DELETE_COPY_AND_MOVE_OPERATORS(className) \ + className(className&&) = delete; \ + className(const className&) = delete; \ + className& operator=(className&&) = delete; \ + className& operator=(const className&) = delete + +#endif /* OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_MACROS_H_ */ diff --git a/osaf/libs/core/cplusplus/base/process.cc b/osaf/libs/core/cplusplus/base/process.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/process.cc @@ -0,0 +1,204 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include "base/process.h" +#include <sys/resource.h> +#include <sys/wait.h> +#include <unistd.h> +#include <cerrno> +#include <cinttypes> +#include <climits> +#include <cstdlib> +#include <cstring> +#include <thread> +#include "logtrace.h" +#include "base/time.h" + +namespace { [HansN] the static in unnamed namespaces is not necessary +static char path_environment_variable[] = "PATH=/usr/local/sbin:" \ + "/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"; +} + +namespace base { + +Process::Process() : + mutex_{}, + timer_id_{}, + is_timer_created_{false}, + process_group_id_{0} { +} + +Process::~Process() { + StopTimer(); +} + +void Process::Kill(int sig_no, const Duration& wait_time) { + std::unique_lock<std::mutex> lock(mutex_); [HansN] could be made lock free if using std::atomic instead, e.g. std::atomic<pid_t> process_group_id_ {1234}; pid_t pgid {0}; pgid = process_group_id_.exchange(pgid); + pid_t pgid = process_group_id_; + process_group_id_ = 0; + lock.unlock(); + if (pgid > 1) KillProc(-pgid, sig_no, wait_time); +} + +void Process::KillProc(pid_t pid, int sig_no, const Duration& wait_time) { + LOG_NO("Sending signal %d to PID %d", sig_no, (int) pid); + if (kill(pid, sig_no) == 0) { + TimePoint timeout = Clock::now() + wait_time; + for (;;) { + int status; + pid_t wait_pid = waitpid(pid, &status, WNOHANG); + if (wait_pid == (pid_t) -1) { + if (errno == ECHILD) break; + if (errno != EINTR) osaf_abort(pid); + } + if (Clock::now() >= timeout) { + if (sig_no == SIGTERM) { + KillProc(pid, SIGKILL, wait_time); + } else { + LOG_WA("Failed to kill %d", (int) pid); + } + break; + } + if (wait_pid == 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + } else if (errno == EPERM) { + LOG_WA("kill(%d, %d) failed due to EPERM", (int) pid, sig_no); + } else if (errno != ESRCH) { + osaf_abort(pid); + } +} + +void Process::Execute(int argc, char *argv[], const Duration& timeout) { + struct rlimit rlim; + int nofile = 1024; + char *const env[] = { path_environment_variable, nullptr }; + + TRACE_ENTER(); + osafassert(argc >= 1 && argv[argc] == nullptr); + LOG_NO("Running '%s' with %d argument(s)", argv[0], argc - 1); + + if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { + if (rlim.rlim_cur != RLIM_INFINITY && + rlim.rlim_cur <= INT_MAX && (int) rlim.rlim_cur >= 0) { + nofile = rlim.rlim_cur; + } else { + LOG_ER("RLIMIT_NOFILE is out of bounds: %llu", + (unsigned long long) rlim.rlim_cur); + } + } else { + LOG_ER("getrlimit(RLIMIT_NOFILE) failed: %s", strerror(errno)); + } + + pid_t child_pid = fork(); + if (child_pid == 0) { + setpgid(0, 0); + for (int fd = 3; fd < nofile; ++fd) close(fd); + execve(argv[0], argv, env); + _Exit(123); + } else if (child_pid != (pid_t) -1 && child_pid != 1) { + if (setpgid(child_pid, 0) != 0) { + LOG_WA("setpgid(%u, 0) failed: %s", + (unsigned) child_pid, strerror(errno)); + } + std::unique_lock<std::mutex> lock(mutex_); [HansN] see comment above, std::atomic + process_group_id_ = child_pid; + StartTimer(timeout); + lock.unlock(); + int status; + pid_t wait_pid; + do { + wait_pid = waitpid(child_pid, &status, 0); + } while (wait_pid == (pid_t) -1 && errno == EINTR); + lock.lock(); + StopTimer(); + bool killed = process_group_id_ == 0; + process_group_id_ = 0; + lock.unlock(); + bool successful = false; + if (!killed && wait_pid != (pid_t) -1) { + if (!WIFEXITED(status)) { + LOG_ER("'%s' terminated abnormally", argv[0]); + } else if (WEXITSTATUS(status) != 0) { + if (WEXITSTATUS(status) == 123) { + LOG_ER("'%s' could not be executed", argv[0]); + } else { + LOG_ER("'%s' failed with exit code %d", argv[0], + WEXITSTATUS(status)); + } + } else { + LOG_IN("'%s' exited successfully", argv[0]); + successful = true; + } + } else if (!killed) { + LOG_ER("'%s' failed in waitpid(%u): %s", + argv[0], (unsigned) child_pid, strerror(errno)); + } + if (!successful) KillProc(-child_pid, SIGTERM, std::chrono::seconds(2)); + } else { + LOG_ER("'%s' failed in fork(): %s", argv[0], + strerror(errno)); + } + TRACE_LEAVE(); +} + +void Process::StartTimer(const Duration& timeout) { + if (timeout != std::chrono::seconds(0)) { + if (!is_timer_created_) { + sigevent event; + event.sigev_notify = SIGEV_THREAD; + event.sigev_value.sival_ptr = this; + event.sigev_notify_function = TimerExpirationEvent; + event.sigev_notify_attributes = nullptr; + int result; + do { + result = timer_create(CLOCK_MONOTONIC, &event, &timer_id_); + if (result == -1 && errno == EAGAIN) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } while (result == -1 && errno == EAGAIN); + if (result != 0) osaf_abort(result); + is_timer_created_ = true; + } + itimerspec tmrspec; + tmrspec.it_interval = kZeroSeconds; + tmrspec.it_value = DurationToTimespec(timeout); + if (timer_settime(timer_id_, 0, &tmrspec, nullptr) != 0) osaf_abort(0); + } else { + StopTimer(); + } +} + +void Process::StopTimer() { + if (is_timer_created_) { + if (timer_delete(timer_id_) != 0) osaf_abort(0); + is_timer_created_ = false; + } +} + +void Process::TimerExpirationEvent(sigval notification_data) { + LOG_ER("Process execution timed out; sending SIGTERM to the process group"); + Process* instance = static_cast<Process*>(notification_data.sival_ptr); + instance->Kill(SIGTERM, std::chrono::seconds(2)); +} + +} // namespace base + diff --git a/osaf/libs/core/cplusplus/base/process.h b/osaf/libs/core/cplusplus/base/process.h new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/process.h @@ -0,0 +1,113 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#ifndef OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_PROCESS_H_ +#define OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_PROCESS_H_ + +#include <csignal> [HansN] use #include <cstdint> +#include <stdint.h> +#include <ctime> +#include <chrono> +#include <mutex> +#include "base/macros.h" + +namespace base { + +class Process { + DELETE_COPY_AND_MOVE_OPERATORS(Process); + public: + using Duration = std::chrono::steady_clock::duration; + Process(); + virtual ~Process(); + /** + * @brief Execute a program with a timeout. + * + * Execute a program in a new process group, with the parameters specified in + * @a argc and @a argv (analogous to the parameters taken by the main() + * function). The first index in argv must be the full path to the executable + * file. The rest of the parameters shall specify the arguments to the + * program. In addition, argv[argc] must be a NULL pointer. This function + * blocks until the called program has exited or until the @a timeout expires, + * whichever happens first. If the @a timeout expires, all the processes in + * the new process group of the started program will be killed by calling the + * Kill() method of this class with the SIGTERM signal. A @a timeout of zero + * will give an infinite timeout. + * + * The program will be executed with an empty environment containing just a + * PATH variable with the fixed value set to the following string: + * /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + * + * You should not try to execute several programs concurrently using the same + * instance of this class, since the timeout handling only supports waiting + * for one program at a time. + */ + void Execute(int argc, char *argv[], const Duration& timeout); + /** + * @brief Check if there is currently an executing process. + * + * Returns true if there is currently an executing process that was started + * using this instance of the Process class. Note that if the process(es) are + * currently being killed (i.e. because of a call to the Kill() method or + * because the timeout has expired), this method may return false even though + * not all processes have yet been killed. + */ + bool is_executing() const { + return process_group_id_ > 1; + } + /** + * @brief Kill the currently executing program. + * + * This function kills the currently executing program by calling the + * KillProc() method on the new process group that was created when starting + * the program. + */ + void Kill(int sig_no, const Duration& wait_time); + /** + * @brief Send a signal to a process (group) and wait for it to terminate. + * + * This is a utility function that works in a similar way as the killproc(8) + * command. It sends the signal @a sig_no to the process @a pid, and waits for + * @a wait_time or until the process has terminated, whichever happens + * first. If @a sig_no is SIGTERM and the process does not terminate before + * the @a wait_time have passed, this function will retry by sending SIGKILL + * to @a pid and again waiting @a wait_time for it to terminate. In the worst + * case, the total wait time can thus be two times @a wait_time when @a sig_no + * is SIGTERM; however, in practice the process will terminate very quickly + * after receiving SIGKILL since this signal cannot be caught, and the the + * total wait time should thus not be much longer than @a wait_time. + * + * It is also possible to use this function to send a signal to a process + * group, by passing a negative value as @a pid (in a similar way as with the + * kill(2) function). In that case, this function will wait until all + * processes in the process group have terminated. + */ + static void KillProc(pid_t pid, int sig_no, const Duration& wait_time); + private: + using Clock = std::chrono::steady_clock; + using TimePoint = std::chrono::steady_clock::time_point; + void StartTimer(const Duration& timeout); + void StopTimer(); + static void TimerExpirationEvent(sigval notification_data); + std::mutex mutex_; + timer_t timer_id_; + bool is_timer_created_; + pid_t process_group_id_; +}; + +} // namespace base + +#endif /* OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_PROCESS_H_ */ diff --git a/osaf/libs/core/cplusplus/base/tests/Makefile.am b/osaf/libs/core/cplusplus/base/tests/Makefile.am new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/tests/Makefile.am @@ -0,0 +1,49 @@ +# -*- OpenSAF -*- +# +# (C) Copyright 2016 The OpenSAF Foundation +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed +# under the GNU Lesser General Public License Version 2.1, February 1999. +# The complete license can be accessed from the following location: +# http://opensource.org/licenses/lgpl-license.php +# See the Copying file included with the OpenSAF distribution for full +# licensing terms. +# +# Author(s): Ericsson AB +# + +include $(top_srcdir)/Makefile.common + +MAINTAINERCLEANFILES = Makefile.in + +TESTS = libbase_test + +check_PROGRAMS = $(TESTS) + +libbase_test_CXXFLAGS =$(AM_CXXFLAGS) + +libbase_test_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir)/osaf/libs/core/cplusplus \ + -I$(GTEST_DIR)/include + +libbase_test_LDFLAGS = \ + -pthread -lrt \ + $(top_builddir)/osaf/libs/core/cplusplus/base/libbase_la-getenv.o \ + $(top_builddir)/osaf/libs/core/cplusplus/base/libbase_la-process.o + +libbase_test_SOURCES = \ + time_add_test.cc \ + time_subtract_test.cc \ + time_compare_test.cc \ + time_convert_test.cc \ + getenv_test.cc \ + mock_logtrace.cc \ + mock_osafassert.cc \ + mock_osaf_abort.cc + +libbase_test_LDADD = \ + $(GTEST_DIR)/lib/libgtest.la \ + $(GTEST_DIR)/lib/libgtest_main.la diff --git a/osaf/libs/core/cplusplus/base/tests/getenv_test.cc b/osaf/libs/core/cplusplus/base/tests/getenv_test.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/tests/getenv_test.cc @@ -0,0 +1,105 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#include <cstdlib> +#include <cstring> +#include "base/getenv.h" +#include "gtest/gtest.h" + +TEST(BaseGetEnv, StringDefault) { + unsetenv("HELLO"); + std::string default_value{"DEFAULT_VALUE"}; + std::string value = base::GetEnv("HELLO", default_value); + EXPECT_EQ(value, default_value); +} + +TEST(BaseGetEnv, StringValue) { + setenv("HELLO", "THERE", 1); + std::string default_value{"DEFAULT_VALUE"}; + std::string value = base::GetEnv("HELLO", default_value); + EXPECT_EQ(value, std::string{"THERE"}); +} + +TEST(BaseGetEnv, CharDefault) { + unsetenv("HELLO"); + const char* default_value{"DEFAULT_VALUE"}; + const char* value = base::GetEnv("HELLO", default_value); + EXPECT_EQ(strcmp(value, default_value), 0); +} + +TEST(BaseGetEnv, CharValue) { + setenv("HELLO", "THERE", 1); + const char* default_value{"DEFAULT_VALUE"}; + const char* value = base::GetEnv("HELLO", default_value); + EXPECT_EQ(strcmp(value, "THERE"), 0); +} + +TEST(BaseGetEnv, NegativeSignedIntegerDefault) { + unsetenv("HELLO"); + int64_t default_value{-1234567890123456789ll}; + int64_t value = base::GetEnv("HELLO", default_value); + EXPECT_EQ(value, default_value); +} + +TEST(BaseGetEnv, NegativeSignedIntegerValue) { + setenv("HELLO", "-2345678901234567890", 1); + int64_t default_value{-1234567890123456789ll}; + int64_t value = base::GetEnv("HELLO", default_value); + EXPECT_EQ(value, -2345678901234567890ll); +} + +TEST(BaseGetEnv, PositiveSignedIntegerDefault) { + unsetenv("HELLO"); + int64_t default_value{1234567890123456789ll}; + int64_t value = base::GetEnv("HELLO", default_value); + EXPECT_EQ(value, default_value); +} + +TEST(BaseGetEnv, PositiveSignedIntegerValue) { + setenv("HELLO", "2345678901234567890", 1); + int64_t default_value{1234567890123456789ll}; + int64_t value = base::GetEnv("HELLO", default_value); + EXPECT_EQ(value, 2345678901234567890ll); +} + +TEST(BaseGetEnv, HexadecimalUnsignedIntegerDefault) { + unsetenv("HELLO"); + uint64_t default_value{0xfefefefefefefefeull}; + uint64_t value = base::GetEnv("HELLO", default_value); + EXPECT_EQ(value, default_value); +} + +TEST(BaseGetEnv, HexadecimalUnsignedIntegerValue) { + setenv("HELLO", "0xabababababababab", 1); + uint64_t default_value{0xfefefefefefefefeull}; + uint64_t value = base::GetEnv("HELLO", default_value); + EXPECT_EQ(value, 0xababababababababull); +} + +TEST(BaseGetEnv, UnparsableInteger) { + setenv("HELLO", "THERE", 1); + uint32_t default_value{0xfefefefeul}; + uint32_t value = base::GetEnv("HELLO", default_value); + EXPECT_EQ(value, default_value); +} + +TEST(BaseGetEnv, IntegerWithTrailingGarbage) { + setenv("HELLO", "12ab", 1); + uint32_t default_value{0xcdcdcdcdul}; + uint32_t value = base::GetEnv("HELLO", default_value); + EXPECT_EQ(value, default_value); +} diff --git a/osaf/libs/core/cplusplus/base/tests/mock_logtrace.cc b/osaf/libs/core/cplusplus/base/tests/mock_logtrace.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/tests/mock_logtrace.cc @@ -0,0 +1,32 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#include "mock_logtrace.h" + +void _logtrace_log(const char *file, unsigned int line, int priority, const char *format, ...) { + (void) file; + (void) line; + (void) priority; + (void) format; +} + +void _logtrace_trace(const char *file, unsigned int line, unsigned int category, const char *format, ...) { + (void) file; + (void) line; + (void) category; + (void) format; +} diff --git a/osaf/libs/core/cplusplus/base/tests/mock_logtrace.h b/osaf/libs/core/cplusplus/base/tests/mock_logtrace.h new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/tests/mock_logtrace.h @@ -0,0 +1,23 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#ifndef OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_TESTS_MOCK_LOGTRACE_H_ +#define OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_TESTS_MOCK_LOGTRACE_H_ + +#include "logtrace.h" + +#endif diff --git a/osaf/libs/core/cplusplus/base/tests/mock_osaf_abort.cc b/osaf/libs/core/cplusplus/base/tests/mock_osaf_abort.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/tests/mock_osaf_abort.cc @@ -0,0 +1,25 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#include "mock_osaf_abort.h" +#include <cstdlib> + +void osaf_abort(long i_cause) +{ + (void) i_cause; + abort(); +} diff --git a/osaf/libs/core/cplusplus/base/tests/mock_osaf_abort.h b/osaf/libs/core/cplusplus/base/tests/mock_osaf_abort.h new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/tests/mock_osaf_abort.h @@ -0,0 +1,23 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#ifndef OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_TESTS_MOCK_OSAF_ABORT_H_ +#define OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_TESTS_MOCK_OSAF_ABORT_H_ + +#include "osaf_utility.h" + +#endif diff --git a/osaf/libs/core/cplusplus/base/tests/mock_osafassert.cc b/osaf/libs/core/cplusplus/base/tests/mock_osafassert.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/tests/mock_osafassert.cc @@ -0,0 +1,27 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#include "mock_osafassert.h" +#include <cstdlib> + +void __osafassert_fail(const char *file, int line, const char* func, const char *assertion) { + (void) file; + (void) line; + (void) func; + (void) assertion; + abort(); +} diff --git a/osaf/libs/core/cplusplus/base/tests/mock_osafassert.h b/osaf/libs/core/cplusplus/base/tests/mock_osafassert.h new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/tests/mock_osafassert.h @@ -0,0 +1,23 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#ifndef OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_TESTS_MOCK_OSAFASSERT_H_ +#define OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_TESTS_MOCK_OSAFASSERT_H_ + +#include "ncsgl_defs.h" + +#endif diff --git a/osaf/libs/core/cplusplus/base/tests/time_add_test.cc b/osaf/libs/core/cplusplus/base/tests/time_add_test.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/tests/time_add_test.cc @@ -0,0 +1,54 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#include "base/time.h" +#include "gtest/gtest.h" + +static const timespec kTwoDotFourSeconds = { 2, 400000000 }; +static const timespec kFourDotEightSeconds = { 4, 800000000 }; +static const timespec kNineDotSixSeconds = { 9, 600000000 }; +static const timespec number1 = { 576315623, 358743382 }; +static const timespec number2 = { 1279394477, 719128783 }; +static const timespec sum = { 1855710101, 77872165 }; + +TEST(BaseTimeAdd, ZeroPlusZero) { + timespec result = base::kZeroSeconds + base::kZeroSeconds; + EXPECT_TRUE(result == base::kZeroSeconds); +} + +TEST(BaseTimeAdd, TwoDotFourAddedWithItself) { + timespec result = kTwoDotFourSeconds; + result += result; + EXPECT_TRUE(result == kFourDotEightSeconds); +} + +TEST(BaseTimeAdd, FourDotEightAddedWithItself) { + timespec result = kFourDotEightSeconds; + result += result; + EXPECT_TRUE(result == kNineDotSixSeconds); +} + +TEST(BaseTimeAdd, AddRandomNumbers) { + timespec result = number1 + number2; + EXPECT_TRUE(result == sum); +} + +TEST(BaseTimeAdd, FirstParameterAndResultAtSameAddress) { + timespec result = number1; + result += number2; + EXPECT_TRUE(result == sum); +} diff --git a/osaf/libs/core/cplusplus/base/tests/time_compare_test.cc b/osaf/libs/core/cplusplus/base/tests/time_compare_test.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/tests/time_compare_test.cc @@ -0,0 +1,195 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#include "base/time.h" +#include "gtest/gtest.h" + +static const timespec kZeroDotNineSeconds = { 0, 900000000 }; +static const timespec kOneDotOneSeconds = { 1, 100000000 }; + +TEST(BaseTimeCompare, ZeroWithZero) { + EXPECT_FALSE(base::kZeroSeconds < base::kZeroSeconds); + EXPECT_TRUE(base::kZeroSeconds <= base::kZeroSeconds); + EXPECT_TRUE(base::kZeroSeconds == base::kZeroSeconds); + EXPECT_FALSE(base::kZeroSeconds != base::kZeroSeconds); + EXPECT_TRUE(base::kZeroSeconds >= base::kZeroSeconds); + EXPECT_FALSE(base::kZeroSeconds > base::kZeroSeconds); +} + +TEST(BaseTimeCompare, ZeroWithZeroDotOne) { + EXPECT_TRUE(base::kZeroSeconds < base::kOneHundredMilliseconds); + EXPECT_TRUE(base::kZeroSeconds <= base::kOneHundredMilliseconds); + EXPECT_FALSE(base::kZeroSeconds == base::kOneHundredMilliseconds); + EXPECT_TRUE(base::kZeroSeconds != base::kOneHundredMilliseconds); + EXPECT_FALSE(base::kZeroSeconds >= base::kOneHundredMilliseconds); + EXPECT_FALSE(base::kZeroSeconds > base::kOneHundredMilliseconds); +} + +TEST(BaseTimeCompare, ZeroDotOneWithZero) { + EXPECT_FALSE(base::kOneHundredMilliseconds < base::kZeroSeconds); + EXPECT_FALSE(base::kOneHundredMilliseconds <= base::kZeroSeconds); + EXPECT_FALSE(base::kOneHundredMilliseconds == base::kZeroSeconds); + EXPECT_TRUE(base::kOneHundredMilliseconds != base::kZeroSeconds); + EXPECT_TRUE(base::kOneHundredMilliseconds >= base::kZeroSeconds); + EXPECT_TRUE(base::kOneHundredMilliseconds > base::kZeroSeconds); +} + +TEST(BaseTimeCompare, ZeroWithOne) { + EXPECT_TRUE(base::kZeroSeconds < base::kOneSecond); + EXPECT_TRUE(base::kZeroSeconds <= base::kOneSecond); + EXPECT_FALSE(base::kZeroSeconds == base::kOneSecond); + EXPECT_TRUE(base::kZeroSeconds != base::kOneSecond); + EXPECT_FALSE(base::kZeroSeconds >= base::kOneSecond); + EXPECT_FALSE(base::kZeroSeconds > base::kOneSecond); +} + +TEST(BaseTimeCompare, OneWithZero) { + EXPECT_FALSE(base::kOneSecond < base::kZeroSeconds); + EXPECT_FALSE(base::kOneSecond <= base::kZeroSeconds); + EXPECT_FALSE(base::kOneSecond == base::kZeroSeconds); + EXPECT_TRUE(base::kOneSecond != base::kZeroSeconds); + EXPECT_TRUE(base::kOneSecond >= base::kZeroSeconds); + EXPECT_TRUE(base::kOneSecond > base::kZeroSeconds); +} + +TEST(BaseTimeCompare, OneWithOne) { + EXPECT_FALSE(base::kOneSecond < base::kOneSecond); + EXPECT_TRUE(base::kOneSecond <= base::kOneSecond); + EXPECT_TRUE(base::kOneSecond == base::kOneSecond); + EXPECT_FALSE(base::kOneSecond != base::kOneSecond); + EXPECT_TRUE(base::kOneSecond >= base::kOneSecond); + EXPECT_FALSE(base::kOneSecond > base::kOneSecond); +} + +TEST(BaseTimeCompare, OneWithOneDotOne) { + EXPECT_TRUE(base::kOneSecond < kOneDotOneSeconds); + EXPECT_TRUE(base::kOneSecond <= kOneDotOneSeconds); + EXPECT_FALSE(base::kOneSecond == kOneDotOneSeconds); + EXPECT_TRUE(base::kOneSecond != kOneDotOneSeconds); + EXPECT_FALSE(base::kOneSecond >= kOneDotOneSeconds); + EXPECT_FALSE(base::kOneSecond > kOneDotOneSeconds); +} + +TEST(BaseTimeCompare, OneDotOneWithOne) { + EXPECT_FALSE(kOneDotOneSeconds < base::kOneSecond); + EXPECT_FALSE(kOneDotOneSeconds <= base::kOneSecond); + EXPECT_FALSE(kOneDotOneSeconds == base::kOneSecond); + EXPECT_TRUE(kOneDotOneSeconds != base::kOneSecond); + EXPECT_TRUE(kOneDotOneSeconds >= base::kOneSecond); + EXPECT_TRUE(kOneDotOneSeconds > base::kOneSecond); +} + +TEST(BaseTimeCompare, ZeroDotNineWithZeroDotNine) { + EXPECT_FALSE(kZeroDotNineSeconds < kZeroDotNineSeconds); + EXPECT_TRUE(kZeroDotNineSeconds <= kZeroDotNineSeconds); + EXPECT_TRUE(kZeroDotNineSeconds == kZeroDotNineSeconds); + EXPECT_FALSE(kZeroDotNineSeconds != kZeroDotNineSeconds); + EXPECT_TRUE(kZeroDotNineSeconds >= kZeroDotNineSeconds); + EXPECT_FALSE(kZeroDotNineSeconds > kZeroDotNineSeconds); +} + +TEST(BaseTimeCompare, ZeroDotNineWithOneDotOne) { + EXPECT_TRUE(kZeroDotNineSeconds < kOneDotOneSeconds); + EXPECT_TRUE(kZeroDotNineSeconds <= kOneDotOneSeconds); + EXPECT_FALSE(kZeroDotNineSeconds == kOneDotOneSeconds); + EXPECT_TRUE(kZeroDotNineSeconds != kOneDotOneSeconds); + EXPECT_FALSE(kZeroDotNineSeconds >= kOneDotOneSeconds); + EXPECT_FALSE(kZeroDotNineSeconds > kOneDotOneSeconds); +} + +TEST(BaseTimeCompare, ZeroDotNineWithZeroDotOne) { + EXPECT_FALSE(kZeroDotNineSeconds < base::kOneHundredMilliseconds); + EXPECT_FALSE(kZeroDotNineSeconds <= base::kOneHundredMilliseconds); + EXPECT_FALSE(kZeroDotNineSeconds == base::kOneHundredMilliseconds); + EXPECT_TRUE(kZeroDotNineSeconds != base::kOneHundredMilliseconds); + EXPECT_TRUE(kZeroDotNineSeconds >= base::kOneHundredMilliseconds); + EXPECT_TRUE(kZeroDotNineSeconds > base::kOneHundredMilliseconds); +} + +TEST(BaseTimeCompare, OneDotOneWithZeroDotNine) { + EXPECT_FALSE(kOneDotOneSeconds < kZeroDotNineSeconds); + EXPECT_FALSE(kOneDotOneSeconds <= kZeroDotNineSeconds); + EXPECT_FALSE(kOneDotOneSeconds == kZeroDotNineSeconds); + EXPECT_TRUE(kOneDotOneSeconds != kZeroDotNineSeconds); + EXPECT_TRUE(kOneDotOneSeconds >= kZeroDotNineSeconds); + EXPECT_TRUE(kOneDotOneSeconds > kZeroDotNineSeconds); +} + +TEST(BaseTimeCompare, ZeroDotOneWithZeroDotNine) { + EXPECT_TRUE(base::kOneHundredMilliseconds < kZeroDotNineSeconds); + EXPECT_TRUE(base::kOneHundredMilliseconds <= kZeroDotNineSeconds); + EXPECT_FALSE(base::kOneHundredMilliseconds == kZeroDotNineSeconds); + EXPECT_TRUE(base::kOneHundredMilliseconds != kZeroDotNineSeconds); + EXPECT_FALSE(base::kOneHundredMilliseconds >= kZeroDotNineSeconds); + EXPECT_FALSE(base::kOneHundredMilliseconds > kZeroDotNineSeconds); +} + +TEST(BaseTimeCompare, MaxOfTwoWithLargestLast) { + EXPECT_EQ(base::Max(kZeroDotNineSeconds, kOneDotOneSeconds), + kOneDotOneSeconds); + EXPECT_EQ(base::Max(base::kOneHundredMilliseconds, + base::kTwoHundredMilliseconds), + base::kTwoHundredMilliseconds); +} + +TEST(BaseTimeCompare, MaxOfTwoWithLargestFirst) { + EXPECT_EQ(base::Max(kOneDotOneSeconds, kZeroDotNineSeconds), + kOneDotOneSeconds); + EXPECT_EQ(base::Max(base::kTwoHundredMilliseconds, + base::kOneHundredMilliseconds), + base::kTwoHundredMilliseconds); +} + +TEST(BaseTimeCompare, MaxOfTwoEqual) { + EXPECT_EQ(base::Max(kOneDotOneSeconds, kOneDotOneSeconds), + kOneDotOneSeconds); +} + +TEST(BaseTimeCompare, MaxOfThreeWithLargestLast) { + EXPECT_EQ(base::Max(kZeroDotNineSeconds, base::kOneSecond, + kOneDotOneSeconds), + kOneDotOneSeconds); + EXPECT_EQ(base::Max(base::kOneHundredMilliseconds, + base::kTwoHundredMilliseconds, + base::kThreeHundredMilliseconds), + base::kThreeHundredMilliseconds); +} + +TEST(BaseTimeCompare, MaxOfThreeWithLargestInTheMiddle) { + EXPECT_EQ(base::Max(kZeroDotNineSeconds, kOneDotOneSeconds, + base::kOneSecond), + kOneDotOneSeconds); + EXPECT_EQ(base::Max(base::kOneHundredMilliseconds, + base::kThreeHundredMilliseconds, + base::kTwoHundredMilliseconds), + base::kThreeHundredMilliseconds); +} + +TEST(BaseTimeCompare, MaxOfThreeWithLargestFirst) { + EXPECT_EQ(base::Max(kOneDotOneSeconds, base::kOneSecond, + kZeroDotNineSeconds), + kOneDotOneSeconds); + EXPECT_EQ(base::Max(base::kThreeHundredMilliseconds, + base::kOneHundredMilliseconds, + base::kTwoHundredMilliseconds), + base::kThreeHundredMilliseconds); +} + +TEST(BaseTimeCompare, MaxOfThreeEqual) { + EXPECT_EQ(base::Max(kOneDotOneSeconds, kOneDotOneSeconds, kOneDotOneSeconds), + kOneDotOneSeconds); +} diff --git a/osaf/libs/core/cplusplus/base/tests/time_convert_test.cc b/osaf/libs/core/cplusplus/base/tests/time_convert_test.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/tests/time_convert_test.cc @@ -0,0 +1,89 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#include "base/time.h" +#include "gtest/gtest.h" + +static const timespec kTimespec = { 1576315623, 358743382 }; +static const timeval kTimeval = { 1576315623, 358743 }; +static const timespec kTruncatedTimespec = { 1576315623, 358743000 }; + +TEST(BaseTimeConvert, TimespecToTimeval) { + timeval result = base::TimespecToTimeval(kTimespec); + EXPECT_EQ(result.tv_sec, kTimeval.tv_sec); + EXPECT_EQ(result.tv_usec, kTimeval.tv_usec); +} + +TEST(BaseTimeConvert, TimevalToTimespec) { + EXPECT_EQ(base::TimevalToTimespec(kTimeval), kTruncatedTimespec); +} + +TEST(BaseTimeConvert, MillisToTimespec) { + const timespec expected_result = { 1428499100, 961000000 }; + EXPECT_EQ(base::MillisToTimespec(1428499100961ull), expected_result); +} + +TEST(BaseTimeConvert, MicrosToTimespec) { + const timespec expected_result = { 2125428499, 100961000 }; + EXPECT_EQ(base::MicrosToTimespec(2125428499100961ull), expected_result); +} + +TEST(BaseTimeConvert, NanosToTimespec) { + const struct timespec expected_result = { 1725125428, 499100961 }; + EXPECT_EQ(base::NanosToTimespec(1725125428499100961ull), expected_result); +} + +TEST(BaseTimeConvert, DoubleToTimespec) { + const struct timespec expected_result = { 15714713, 125433700 }; + const struct timespec expected_result2 = { -15714714, 874566300 }; + EXPECT_EQ(base::DoubleToTimespec(15714713.1254337), expected_result); + EXPECT_EQ(base::DoubleToTimespec(-15714713.1254337), expected_result2); +} + +TEST(BaseTimeConvert, DurationToTimespec) { + const struct timespec expected_result = { 1725125428, 499100961 }; + std::chrono::nanoseconds duration{1725125428499100961ull}; + EXPECT_EQ(base::DurationToTimespec(duration), expected_result); +} + +TEST(BaseTimeConvert, TimespecToMillis) { + const struct timespec ts = { 1428499100, 961923266 }; + EXPECT_EQ(base::TimespecToMillis(ts), 1428499100961ull); +} + +TEST(BaseTimeConvert, TimespecToMicros) { + const struct timespec ts = { 1125428499, 100961923 }; + EXPECT_EQ(base::TimespecToMicros(ts), 1125428499100961ull); +} + +TEST(BaseTimeConvert, TimespecToNanos) { + const struct timespec ts = { 1725125428, 499100961 }; + EXPECT_EQ(base::TimespecToNanos(ts), 1725125428499100961ull); +} + +TEST(BaseTimeConvert, TimespecToDouble) { + const struct timespec ts = { 15714713, 125433700 }; + const struct timespec ts2 = { -15714714, 874566300 }; + EXPECT_EQ(base::TimespecToDouble(ts), 15714713.1254337); + EXPECT_EQ(base::TimespecToDouble(ts2), -15714713.1254337); +} + +TEST(BaseTimeConvert, TimespecToDuration) { + const struct timespec ts = { 1725125428, 499100961 }; + std::chrono::nanoseconds expected_result{1725125428499100961ull}; + EXPECT_EQ(base::TimespecToDuration(ts), expected_result); +} diff --git a/osaf/libs/core/cplusplus/base/tests/time_subtract_test.cc b/osaf/libs/core/cplusplus/base/tests/time_subtract_test.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/tests/time_subtract_test.cc @@ -0,0 +1,52 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#include "base/time.h" +#include "gtest/gtest.h" + [HansN] perhaps unamed namespaces can be used instead of static? +static const timespec kOneDotOneSeconds = { 1, 100000000 }; +static const timespec kZeroDotTwoSeconds = { 0, 200000000 }; +static const timespec kZeroDotNineSeconds = { 0, 900000000 }; +static const timespec number1 = { 1576315623, 358743382 }; +static const timespec number2 = { 279394477, 639614599 }; +static const timespec difference = { 1296921145, 719128783 }; + +TEST(BaseTimeSubtract, ZeroMinusZero) { + timespec result = base::kZeroSeconds - base::kZeroSeconds; + EXPECT_TRUE(result == base::kZeroSeconds); +} + +TEST(BaseTimeSubtract, FifteenMinusFive) { + timespec result = base::kFifteenSeconds - base::kFiveSeconds; + EXPECT_TRUE(result == base::kTenSeconds); +} + +TEST(BaseTimeSubtract, OneDotOneMinusZeroDotTwo) { + timespec result = kOneDotOneSeconds - kZeroDotTwoSeconds; + EXPECT_TRUE(result == kZeroDotNineSeconds); +} + +TEST(BaseTimeSubtract, SubtractRandomNumbers) { + timespec result = number1 - number2; + EXPECT_TRUE(result == difference); +} + +TEST(BaseTimeSubtract, FirstParameterAndResultAtSameAddress) { + timespec result = number1; + result -= number2; + EXPECT_TRUE(result == difference); +} diff --git a/osaf/libs/core/cplusplus/base/time.h b/osaf/libs/core/cplusplus/base/time.h new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/time.h @@ -0,0 +1,275 @@ +/* + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Author(s): Ericsson AB + * + */ + +#ifndef OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_TIME_H_ +#define OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_TIME_H_ + +#include <stdint.h> +#include <cassert> +#include <chrono> +#include <climits> +#include <ctime> +#include "osaf_time.h" + +static inline bool operator<(const timespec& ts1, const timespec& ts2) { + return osaf_timespec_compare(&ts1, &ts2) < 0; +} + +static inline bool operator<=(const timespec& ts1, const timespec& ts2) { + return osaf_timespec_compare(&ts1, &ts2) <= 0; +} + +static inline bool operator==(const timespec& ts1, const timespec& ts2) { + return osaf_timespec_compare(&ts1, &ts2) == 0; +} + +static inline bool operator!=(const timespec& ts1, const timespec& ts2) { + return osaf_timespec_compare(&ts1, &ts2) != 0; +} + +static inline bool operator>=(const timespec& ts1, const timespec& ts2) { + return osaf_timespec_compare(&ts1, &ts2) >= 0; +} + +static inline bool operator>(const timespec& ts1, const timespec& ts2) { + return osaf_timespec_compare(&ts1, &ts2) > 0; +} + +static inline timespec operator+(const timespec& ts1, const timespec& ts2) { + timespec sum; + osaf_timespec_add(&ts1, &ts2, &sum); + return sum; +} + +static inline timespec operator-(const timespec& ts1, const timespec& ts2) { + timespec difference; + assert(ts1 >= ts2); + osaf_timespec_subtract(&ts1, &ts2, &difference); + return difference; +} + +static inline timespec& operator+=(timespec& ts1, const timespec& ts2) { + osaf_timespec_add(&ts1, &ts2, &ts1); + return ts1; +} + +static inline timespec& operator-=(timespec& ts1, const timespec& ts2) { + assert(ts1 >= ts2); + osaf_timespec_subtract(&ts1, &ts2, &ts1); + return ts1; +} + +namespace base { + +constexpr static const timespec kZeroSeconds{ 0, 0 }; +constexpr static const timespec kOneMillisecond{ 0, 1000000 }; +constexpr static const timespec kTenMilliseconds{ 0, 10000000 }; +constexpr static const timespec kTwentyMilliseconds{ 0, 20000000 }; +constexpr static const timespec kThirtyMilliseconds{ 0, 30000000 }; +constexpr static const timespec kFourtyMilliseconds{ 0, 40000000 }; +constexpr static const timespec kFiftyMilliseconds{ 0, 50000000 }; +constexpr static const timespec kOneHundredMilliseconds{ 0, 100000000 }; +constexpr static const timespec kTwoHundredMilliseconds{ 0, 200000000 }; +constexpr static const timespec kThreeHundredMilliseconds{ 0, 300000000 }; +constexpr static const timespec kFourHundredMilliseconds{ 0, 400000000 }; +constexpr static const timespec kFiveHundredMilliseconds{ 0, 500000000 }; +constexpr static const timespec kOneSecond{ 1, 0 }; +constexpr static const timespec kFiveSeconds{ 5, 0 }; +constexpr static const timespec kTenSeconds{ 10, 0 }; +constexpr static const timespec kFifteenSeconds{ 15, 0 }; +constexpr static const timespec kTwentySeconds{ 20, 0 }; +constexpr static const timespec kTwentyfiveSeconds{ 25, 0 }; +constexpr static const timespec kThirtySeconds{ 30, 0 }; +constexpr static const timespec kOneMinute{ 1 * 60, 0 }; +constexpr static const timespec kFiveMinutes{ 5 * 60, 0 }; +constexpr static const timespec kTenMinutes{ 10 * 60, 0 }; +constexpr static const timespec kFifteenMinutes{ 15 * 60, 0 }; +constexpr static const timespec kTwentyMinutes{ 20 * 60, 0 }; +constexpr static const timespec kTwentyfiveMinutes{ 25 * 60, 0 }; +constexpr static const timespec kThirtyMinutes{ 30 * 60, 0 }; +constexpr static const timespec kOneHour{ 60 * 60, 0 }; +constexpr static const timespec kTimespecMax{ sizeof(time_t) == 8 ? INT64_MAX : + INT32_MAX, 999999999 }; + +/** + * Read the current value of the system's real-time clock. For more + * information, refer to the POSIX function clock_gettime() and the clock id + * CLOCK_REALTIME. + */ +static inline timespec ReadRealtimeClock() { + timespec ts; + osaf_clock_gettime(CLOCK_REALTIME, &ts); + return ts; +} + +/** + * Read the current value of the system's monotonic clock. For more + * information, refer to the POSIX function clock_gettime() and the clock id + * CLOCK_MONOTONIC. + */ +static inline timespec ReadMonotonicClock() { + timespec ts; + osaf_clock_gettime(CLOCK_MONOTONIC, &ts); + return ts; +} + +/** + * Suspend execution of the calling thread for the specified @a duration. + */ +static inline void Sleep(const timespec& duration) { + osaf_nanosleep(&duration); +} + +/** + * Convert the specified @a duration to a timespec structure. + */ +template<class Rep, class Period> +static inline timespec DurationToTimespec( + const std::chrono::duration<Rep, Period>& duration) { + timespec ts; + std::chrono::nanoseconds nanos = duration; + osaf_nanos_to_timespec(nanos.count(), &ts); + return ts; +} + +/** + * Convert the specified timeval structure to a timespec structure. + */ +static inline timespec TimevalToTimespec(const timeval& tv) { + timespec ts; + osaf_timeval_to_timespec(&tv, &ts); + return ts; +} + +/** + * Convert the specified number of @a seconds to a timespec structure. + */ +static inline timespec DoubleToTimespec(double seconds) { + timespec ts; + osaf_double_to_timespec(seconds, &ts); + return ts; +} + +/** + * Convert the specified number of @a milliseconds to a timespec structure. + */ +static inline timespec MillisToTimespec(uint64_t milliseconds) { + timespec ts; + osaf_millis_to_timespec(milliseconds, &ts); + return ts; +} + +/** + * Convert the specified number of @a microseconds to a timespec structure. + */ +static inline timespec MicrosToTimespec(uint64_t microseconds) { + timespec ts; + osaf_micros_to_timespec(microseconds, &ts); + return ts; +} + +/** + * Convert the specified number of @a nanoseconds to a timespec structure. + */ +static inline timespec NanosToTimespec(uint64_t nanoseconds) { + timespec ts; + osaf_nanos_to_timespec(nanoseconds, &ts); + return ts; +} + +/** + * Convert the specified timespec structure @a ts to a duration with nanosecond + * resolution. + */ +static inline std::chrono::nanoseconds TimespecToDuration(const timespec& ts) { + return std::chrono::nanoseconds{osaf_timespec_to_nanos(&ts)}; +} + +/** + * Convert the specified timeval structure to a timespec structure. + */ +static inline timeval TimespecToTimeval(const timespec& ts) { + timeval tv; + osaf_timespec_to_timeval(&ts, &tv); + return tv; +} + +/** + * Convert the specified timespec structure @a ts to a double representing time + * in the unit of seconds. + */ +static inline double TimespecToDouble(const timespec& ts) { + return osaf_timespec_to_double(&ts); +} + +/** + * Convert the specified timespec structure @a ts to an integer representing + * time in the unit of milliseconds. + */ +static inline uint64_t TimespecToMillis(const timespec& ts) { + return osaf_timespec_to_millis(&ts); +} + +/** + * Convert the specified timespec structure @a ts to an integer representing + * time in the unit of microseconds. + */ +static inline uint64_t TimespecToMicros(const timespec& ts) { + return osaf_timespec_to_micros(&ts); +} + +/** + * Convert the specified timespec structure @a ts to an integer representing + * time in the unit of nanoseconds. + */ +static inline uint64_t TimespecToNanos(const timespec& ts) { + return osaf_timespec_to_nanos(&ts); +} + +/** + * Return the smallest of the two specified timespec structures. + */ +static inline timespec Min(const timespec& ts1, const timespec& ts2) { + return osaf_timespec_compare(&ts1, &ts2) <= 0 ? ts1 : ts2; +} + +/** + * Return the smallest of the three specified timespec structures. + */ +static inline timespec Min(const timespec& ts1, const timespec& ts2, + const timespec& ts3) { + return osaf_timespec_compare(&ts1, &ts2) <= 0 ? Min(ts1, ts3) : Min(ts2, ts3); +} + +/** + * Return the largest of the two specified timespec structures. + */ +static inline timespec Max(const timespec& ts1, const timespec& ts2) { + return osaf_timespec_compare(&ts1, &ts2) >= 0 ? ts1 : ts2; +} + +/** + * Return the largest of the three specified timespec structures. + */ +static inline timespec Max(const timespec& ts1, const timespec& ts2, + const timespec& ts3) { + return osaf_timespec_compare(&ts1, &ts2) >= 0 ? Max(ts1, ts3) : Max(ts2, ts3); +} + +} // namespace base + +#endif /* OPENSAF_OSAF_LIBS_CORE_CPLUSPLUS_BASE_TIME_H_ */ ------------------------------------------------------------------------------ Site24x7 APM Insight: Get Deep Visibility into Application Performance APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month Monitor end-to-end web transactions and take corrective actions now Troubleshoot faster and improve end-user experience. Signup Now! http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140 _______________________________________________ Opensaf-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/opensaf-devel ------------------------------------------------------------------------------ Transform Data into Opportunity. Accelerate data analysis in your applications with Intel Data Analytics Acceleration Library. Click to learn more. http://pubads.g.doubleclick.net/gampad/clk?id=278785351&iu=/4140 _______________________________________________ Opensaf-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/opensaf-devel
