Add Timer class
This class is used to call a callback function
when the timer expires. It can be one shot, or repeating.
Copied from the phosphor-fan-presence repository.
Change-Id: I9d63c2e6fd550286a2a5360e1e8d13f6a3c25923
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1f4edce
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,26 @@
+*.o
+*.lo
+*.la
+*.sw*
+Makefile
+Makefile.in
+configure
+.deps
+.libs
+aclocal.m4
+ar-lib
+arm-openbmc-linux-gnueabi-libtool
+autom4te.cache/
+compile
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+depcomp
+install-sh
+i586-openbmc-linux-libtool
+ltmain.sh
+missing
+stamp-h1
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..ef4d98b
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,14 @@
+AM_DEFAULT_SOURCE_EXT = .cpp
+
+noinst_LTLIBRARIES = libpower.la
+libpower_la_LDFLAGS = -static
+libpower_la_LIBADD = \
+ $(PHOSPHOR_LOGGING_LIBS) \
+ $(SDBUSPLUS_LIBS)
+libpower_la_CXXFLAGS = \
+ $(PHOSPHOR_LOGGING_CFLAGS) \
+ $(SDBUSPLUS_CFLAGS)
+
+libpower_la_SOURCES = timer.cpp
+
+SUBDIRS = .
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..4c0b0f5
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+AUTOCONF_FILES="Makefile.in aclocal.m4 ar-lib autom4te.cache compile \
+ config.guess config.h.in config.h.in~ config.sub configure depcomp \
+ install-sh ltmain.sh missing *libtool"
+
+case $1 in
+ clean)
+ test -f Makefile && make maintainer-clean
+ for file in ${AUTOCONF_FILES}; do
+ find -name "$file" | xargs -r rm -rf
+ done
+ exit 0
+ ;;
+esac
+
+autoreconf -if
+echo 'Run "./configure ${CONFIGURE_FLAGS} && make"'
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..7a2f09b
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,32 @@
+# Initialization
+AC_PREREQ([2.69])
+AC_INIT([witherspoon-pfault-analysis], [1.0],
+[https://github.com/openbmc/witherspoon-pfault-analysis/issues])
+AC_LANG([C++])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz])
+AM_SILENT_RULES([yes])
+
+# Checks for programs.
+AC_PROG_CXX
+AM_PROG_AR
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+
+# Checks for typedefs, structures, and compiler characteristics.
+AX_CXX_COMPILE_STDCXX_14([noext])
+AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS])
+
+PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus],,
+ AC_MSG_ERROR(["Requires sdbusplus package."]))
+PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging],,\
+ AC_MSG_ERROR(["Requires phosphor-logging package."]))
+PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces],,\
+ AC_MSG_ERROR(["Requires phosphor-dbus-interfaces package."]))
+
+# Checks for library functions.
+LT_INIT # Required for systemd linking
+
+# Create configured output
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/event.hpp b/event.hpp
new file mode 100644
index 0000000..6b2c95f
--- /dev/null
+++ b/event.hpp
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <memory>
+#include <systemd/sd-event.h>
+
+namespace witherspoon
+{
+namespace power
+{
+namespace event
+{
+
+/**
+ * Custom deleter for sd_event_source
+ */
+struct EventSourceDeleter
+{
+ void operator()(sd_event_source* eventSource) const
+ {
+ sd_event_source_unref(eventSource);
+ }
+};
+
+using EventSource = std::unique_ptr<sd_event_source, EventSourceDeleter>;
+
+/**
+ * Customer deleter for sd_event
+ */
+struct EventDeleter
+{
+ void operator()(sd_event* event) const
+ {
+ sd_event_unref(event);
+ }
+};
+
+using Event = std::unique_ptr<sd_event, EventDeleter>;
+
+}
+}
+}
diff --git a/timer.cpp b/timer.cpp
new file mode 100644
index 0000000..7039338
--- /dev/null
+++ b/timer.cpp
@@ -0,0 +1,170 @@
+/**
+ * Copyright © 2017 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <chrono>
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+#include <type_traits>
+#include "timer.hpp"
+
+namespace witherspoon
+{
+namespace power
+{
+
+using namespace phosphor::logging;
+using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
+ Error::InternalFailure;
+
+Timer::Timer(event::Event& events,
+ std::function<void()> callbackFunc) :
+ timeEvent(events),
+ callback(callbackFunc),
+ timeout(0)
+{
+ sd_event_source* source = nullptr;
+
+ // Start with an infinite expiration time
+ auto r = sd_event_add_time(timeEvent.get(),
+ &source,
+ CLOCK_MONOTONIC, // Time base
+ UINT64_MAX, // Expire time - way long time
+ 0, // Use default event accuracy
+ timeoutHandler, // Callback handler on timeout
+ this); // User data
+ if (r < 0)
+ {
+ log<level::ERR>("Timer::Timer failed call to sd_event_add_time",
+ entry("ERROR=%s", strerror(-r)));
+ elog<InternalFailure>();
+ }
+
+ eventSource.reset(source);
+
+ //Ensure timer isn't running
+ setTimer(SD_EVENT_OFF);
+}
+
+
+Timer::~Timer()
+{
+ setTimer(SD_EVENT_OFF);
+}
+
+
+int Timer::timeoutHandler(sd_event_source* eventSource,
+ uint64_t usec, void* userData)
+{
+ auto timer = static_cast<Timer*>(userData);
+
+ if (timer->type == TimerType::repeating)
+ {
+ //Set the next expiration time
+ timer->setTimeout();
+ }
+
+ timer->callback();
+
+ return 0;
+}
+
+
+std::chrono::microseconds Timer::getTime()
+{
+ using namespace std::chrono;
+ auto now = steady_clock::now().time_since_epoch();
+ return duration_cast<microseconds>(now);
+}
+
+
+void Timer::setTimer(int action)
+{
+ auto r = sd_event_source_set_enabled(eventSource.get(), action);
+ if (r < 0)
+ {
+ log<level::ERR>("Failed call to sd_event_source_set_enabled",
+ entry("ERROR=%s", strerror(-r)),
+ entry("ACTION=%d", action));
+ elog<InternalFailure>();
+ }
+}
+
+
+void Timer::stop()
+{
+ setTimer(SD_EVENT_OFF);
+}
+
+
+bool Timer::running()
+{
+ int status = 0;
+
+ //returns SD_EVENT_OFF, SD_EVENT_ON, or SD_EVENT_ONESHOT
+ auto r = sd_event_source_get_enabled(eventSource.get(), &status);
+ if (r < 0)
+ {
+ log<level::ERR>("Failed call to sd_event_source_get_enabled",
+ entry("ERROR=%s", strerror(-r)));
+ elog<InternalFailure>();
+ }
+
+ return (status != SD_EVENT_OFF);
+}
+
+
+void Timer::setTimeout()
+{
+ //Get the current time and add the delta
+ static_assert(std::is_same<decltype(getTime()),
+ std::chrono::microseconds>::value,
+ "Timer::getTime() is the wrong type");
+ static_assert(std::is_same<decltype(timeout),
+ std::chrono::microseconds>::value,
+ "Timer::timeout is the wrong type");
+
+ auto expireTime = getTime() + timeout;
+
+ //Set the time
+ auto r = sd_event_source_set_time(eventSource.get(), expireTime.count());
+ if (r < 0)
+ {
+ log<level::ERR>("Failed call to sd_event_source_set_time",
+ entry("ERROR=%s", strerror(-r)));
+ elog<InternalFailure>();
+ }
+}
+
+
+void Timer::start(std::chrono::microseconds timeValue,
+ TimerType timerType)
+{
+ type = timerType;
+
+ // Disable the timer
+ setTimer(SD_EVENT_OFF);
+
+ //Rearm the timer
+ timeout = timeValue;
+ setTimeout();
+
+ setTimer((type == TimerType::oneshot) ? SD_EVENT_ONESHOT : SD_EVENT_ON);
+}
+
+
+}
+}
diff --git a/timer.hpp b/timer.hpp
new file mode 100644
index 0000000..0f46de7
--- /dev/null
+++ b/timer.hpp
@@ -0,0 +1,161 @@
+#pragma once
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include "event.hpp"
+
+namespace witherspoon
+{
+namespace power
+{
+
+
+/**
+ * @class Timer
+ *
+ * This class implements a simple timer that runs an arbitrary
+ * function on expiration. The timeout value is set in microseconds.
+ * It can be stopped while it is running, and queried to see if it is
+ * running.
+ *
+ * If started with the 'repeating' argument, it will keep calling the
+ * callback function every <timeout> microseconds. If started with the
+ * 'oneshot' argument, it will just call the callback function one time.
+ *
+ * It needs an sd_event loop to function.
+ */
+class Timer
+{
+ public:
+
+ enum class TimerType
+ {
+ oneshot,
+ repeating
+ };
+
+ Timer() = delete;
+ Timer(const Timer&) = delete;
+ Timer& operator=(const Timer&) = delete;
+ Timer(Timer&&) = default;
+ Timer& operator=(Timer&&) = default;
+
+ /**
+ * @brief Constructs timer object
+ *
+ * @param[in] events - sd_event pointer, previously created
+ * @param[in] callbackFunc - The function to call on timer expiration
+ */
+ Timer(event::Event& events,
+ std::function<void()> callbackFunc);
+
+ /**
+ * @brief Destructor
+ */
+ ~Timer();
+
+ /**
+ * @brief Starts the timer
+ *
+ * The input is an offset from the current steady clock.
+ *
+ * @param[in] usec - the timeout value in microseconds
+ * @param[in] type - either a oneshot, or repeating
+ */
+ void start(std::chrono::microseconds usec,
+ TimerType type = TimerType::oneshot);
+
+ /**
+ * @brief Stop the timer
+ */
+ void stop();
+
+ /**
+ * @brief Returns true if the timer is running
+ */
+ bool running();
+
+ /**
+ * @brief Returns the timeout value
+ *
+ * @return - the last value sent in via start().
+ */
+ inline auto getTimeout() const
+ {
+ return timeout;
+ }
+
+ /**
+ * @brief Returns the timer type
+ */
+ inline auto getType() const
+ {
+ return type;
+ }
+
+ private:
+
+ /**
+ * @brief Callback function when timer goes off
+ *
+ * Calls the callback function passed in by the user.
+ *
+ * @param[in] eventSource - Source of the event
+ * @param[in] usec - time in micro seconds
+ * @param[in] userData - User data pointer
+ */
+ static int timeoutHandler(sd_event_source* eventSource,
+ uint64_t usec, void* userData);
+
+ /**
+ * @brief Gets the current time from the steady clock
+ */
+ std::chrono::microseconds getTime();
+
+ /**
+ * @brief Wrapper around sd_event_source_set_enabled
+ *
+ * @param[in] action - either SD_EVENT_OFF, SD_EVENT_ON,
+ * or SD_EVENT_ONESHOT
+ */
+ void setTimer(int action);
+
+
+ /**
+ * @brief Sets the expiration time for the timer
+ *
+ * Sets it to timeout microseconds in the future
+ */
+ void setTimeout();
+
+ /**
+ * @brief The sd_event structure
+ */
+ event::Event& timeEvent;
+
+ /**
+ * @brief Source of events
+ */
+ event::EventSource eventSource;
+
+ /**
+ * @brief Either 'repeating' or 'oneshot'
+ */
+ TimerType type = TimerType::oneshot;
+
+ /**
+ * @brief The function to call when the timer expires
+ */
+ std::function<void()> callback;
+
+ /**
+ * @brief What the timer was set to run for
+ *
+ * Not cleared on timer expiration
+ */
+ std::chrono::microseconds timeout;
+};
+
+}
+}