Add timer class support

Watchdog implementation uses sd_event_timer at the backend and this
commit has that timer backend support.

Change-Id: Ib6dbd5d6bb617c20d7361e0852a916aa506d5aad
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 370d024..888f8cf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,13 @@
 sbin_PROGRAMS = phosphor-watchdog
 
+noinst_HEADERS = timer.hpp
+
 phosphor_watchdog_SOURCES = \
-				argument.cpp \
-				mainapp.cpp
+                argument.cpp \
+                timer.cpp \
+                mainapp.cpp
+
+phosphor_watchdog_LDFLAGS = $(SYSTEMD_LIBS) \
+                            ${PHOSPHOR_LOGGING_LIBS}
+phosphor_watchdog_CXXFLAGS = $(SYSTEMD_CFLAGS)\
+                             ${PHOSPHOR_LOGGING_CFLAGS}
diff --git a/configure.ac b/configure.ac
index 2f48a68..2f06b7b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,6 +18,9 @@
 # For linking
 LT_INIT
 
+PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221], [], [AC_MSG_ERROR(["systemd required and not found"])])
+PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging],, [AC_MSG_ERROR([Could not find phosphor-logging...openbmc/phosphor-logging package required])])
+
 # Create configured output
 AC_CONFIG_FILES([Makefile])
 AC_OUTPUT
diff --git a/mainapp.cpp b/mainapp.cpp
index 19aeb6a..4ab811c 100644
--- a/mainapp.cpp
+++ b/mainapp.cpp
@@ -15,7 +15,9 @@
  */
 
 #include <iostream>
+#include <phosphor-logging/log.hpp>
 #include "argument.hpp"
+#include "timer.hpp"
 
 static void exitWithError(const char* err, char** argv)
 {
@@ -26,6 +28,8 @@
 
 int main(int argc, char** argv)
 {
+    using namespace phosphor::logging;
+
     // Read arguments.
     auto options = phosphor::watchdog::ArgumentParser(argc, argv);
 
@@ -48,5 +52,29 @@
     // on meeting a condition.
     auto target = (options)["target"];
 
+    sd_event* event = nullptr;
+    auto r = sd_event_default(&event);
+    if (r < 0)
+    {
+        log<level::ERR>("Error creating a default sd_event handler");
+        return r;
+    }
+    phosphor::watchdog::EventPtr eventP{event};
+    event = nullptr;
+
+    // TODO: Creating the timer object would be inside watchdog implementation.
+    //       Putting this here for completion of this piece
+    phosphor::watchdog::Timer timer(eventP);
+
+    while(!timer.expired())
+    {
+        // -1 denotes wait for ever
+        r = sd_event_run(eventP.get(), (uint64_t)-1);
+        if (r < 0)
+        {
+            log<level::ERR>("Error waiting for events");
+            return -1;
+        }
+    }
     return 0;
 }
diff --git a/timer.cpp b/timer.cpp
new file mode 100644
index 0000000..bf1a470
--- /dev/null
+++ b/timer.cpp
@@ -0,0 +1,118 @@
+#include <chrono>
+#include <systemd/sd-event.h>
+#include <phosphor-logging/log.hpp>
+#include "timer.hpp"
+namespace phosphor
+{
+namespace watchdog
+{
+
+// Initializes the timer object
+void Timer::initialize()
+{
+    // This can not be called more than once.
+    if (eventSource.get())
+    {
+        // TODO: Need to throw elog exception stating its already added.
+        throw std::runtime_error("Timer already initialized");
+    }
+
+    // Add infinite expiration time
+    decltype(eventSource.get()) sourcePtr = nullptr;
+    auto r = sd_event_add_time(event.get(),
+                               &sourcePtr,
+                               CLOCK_MONOTONIC, // Time base
+                               UINT64_MAX,      // Expire time - way long time
+                               0,               // Use default event accuracy
+                               timeoutHandler,  // Callback handler on timeout
+                               this);           // User data
+    eventSource.reset(sourcePtr);
+
+    if (r < 0)
+    {
+        // TODO: throw elog exception
+        throw std::runtime_error("Timer initialization failed");
+    }
+
+    // Disable the timer for now
+    setEnabled<std::false_type>();
+}
+
+// callback handler on timeout
+int Timer::timeoutHandler(sd_event_source* eventSource,
+                          uint64_t usec, void* userData)
+{
+    using namespace phosphor::logging;
+
+    auto timer = static_cast<Timer*>(userData);
+    timer->expire = true;
+
+    log<level::INFO>("Timer Expired");
+
+    //TODO: Need to call user callback function.
+    return 0;
+}
+
+// Gets the time from steady_clock
+std::chrono::microseconds Timer::getCurrentTime()
+{
+    using namespace std::chrono;
+    auto usec = steady_clock::now().time_since_epoch();
+    return duration_cast<microseconds>(usec);
+}
+
+// Sets the expiration time and arms the timer
+void Timer::start(std::chrono::microseconds usec)
+{
+    // Get the current MONOTONIC time and add the delta
+    auto expireTime = getCurrentTime() + usec;
+
+    // Set the time
+    auto r = sd_event_source_set_time(eventSource.get(),
+                                      expireTime.count());
+    if (r < 0)
+    {
+        // TODO throw elog exception
+        throw std::runtime_error("Error setting the expiration time");
+    }
+}
+
+// Returns current timer enablement type
+int Timer::getEnabled() const
+{
+    int enabled{};
+    auto r = sd_event_source_get_enabled(eventSource.get(), &enabled);
+    if (r < 0)
+    {
+        // TODO: Need to throw elog exception
+        throw std::runtime_error("Error geting current time enablement state");
+    }
+    return enabled;
+}
+
+// Enables / disables the timer
+void Timer::setEnabled(int type)
+{
+    auto r = sd_event_source_set_enabled(eventSource.get(), type);
+    if (r < 0)
+    {
+        // TODO: Need to throw elog exception
+        throw std::runtime_error("Error altering enabled property");
+    }
+}
+
+// Returns time remaining before expiration
+std::chrono::microseconds Timer::getRemaining() const
+{
+    uint64_t next = 0;
+    auto r = sd_event_source_get_time(eventSource.get(), &next);
+    if (r < 0)
+    {
+        // TODO: Need to throw elog exception
+        throw std::runtime_error("Error altering enabled property");
+    }
+    return std::chrono::microseconds(next);
+}
+
+} // namespace watchdog
+} // namespace phosphor
diff --git a/timer.hpp b/timer.hpp
new file mode 100644
index 0000000..49580d7
--- /dev/null
+++ b/timer.hpp
@@ -0,0 +1,132 @@
+#pragma once
+
+#include <memory>
+#include <chrono>
+#include <systemd/sd-event.h>
+namespace phosphor
+{
+namespace watchdog
+{
+
+/* Need a custom deleter for freeing up sd_event */
+struct EventDeleter
+{
+    void operator()(sd_event* event) const
+    {
+        event = sd_event_unref(event);
+    }
+};
+using EventPtr = std::unique_ptr<sd_event, EventDeleter>;
+
+/* Need a custom deleter for freeing up sd_event_source */
+struct EventSourceDeleter
+{
+    void operator()(sd_event_source* eventSource) const
+    {
+        eventSource = sd_event_source_unref(eventSource);
+    }
+};
+using EventSourcePtr = std::unique_ptr<sd_event_source, EventSourceDeleter>;
+
+/** @class Timer
+ *  @brief Manages starting timers and handling timeouts
+ */
+class Timer
+{
+    public:
+        Timer() = delete;
+        ~Timer() = default;
+        Timer(const Timer&) = delete;
+        Timer& operator=(const Timer&) = delete;
+        Timer(Timer&&) = delete;
+        Timer& operator=(Timer&&) = delete;
+
+        /** @brief Constructs timer object
+         *
+         *  @param[in] event - sd_event unique pointer reference
+         */
+        Timer(EventPtr& event)
+            : event(event)
+        {
+            // Initialize the timer
+            initialize();
+        }
+
+        /** @brief Tells whether the timer is expired or not */
+        inline auto expired() const
+        {
+            return expire;
+        }
+
+        /** @brief Returns the current Timer enablement type */
+        int getEnabled() const;
+
+        /** @brief Enables / disables the timer.
+         *         <T> is an integral constant boolean
+         */
+        template <typename T> void setEnabled()
+        {
+            constexpr auto type = T::value ? SD_EVENT_ONESHOT : SD_EVENT_OFF;
+            return setEnabled(type);
+        }
+
+        /** @brief Returns time remaining in usec before expiration
+         *         which is an offset to current steady clock
+         */
+        std::chrono::microseconds getRemaining() const;
+
+        /** @brief Starts the timer with specified expiration value.
+         *         std::steady_clock is used for base time.
+         *
+         *  @param[in] usec - Microseconds from the current time
+         *                    before expiration.
+         *
+         *  @return None.
+         *
+         *  @error Throws exception
+         */
+        void start(std::chrono::microseconds usec);
+
+        /** @brief Gets the current time from steady clock */
+        static std::chrono::microseconds getCurrentTime();
+
+    private:
+        /** @brief Reference to sd_event unique pointer */
+        EventPtr& event;
+
+        /** @brief event source */
+        EventSourcePtr eventSource;
+
+        /** @brief Set to true when the timeoutHandler is called into */
+        bool expire = false;
+
+        /** @brief Initializes the timer object with infinite
+         *         expiration time and sets up the callback handler
+         *
+         *  @return None.
+         *
+         *  @error Throws exception
+         */
+        void initialize();
+
+        /** @brief Callback function when timer goes off
+         *
+         *  @param[in] eventSource - Source of the event
+         *  @param[in] usec        - time in microseconds
+         *  @param[in] userData    - User data pointer
+         *
+         */
+        static int timeoutHandler(sd_event_source* eventSource,
+                                  uint64_t usec, void* userData);
+
+        /** @brief Enables / disables the timer
+         *
+         *  @param[in] type - Timer type.
+         *                    This implementation uses only SD_EVENT_OFF
+         *                    and SD_EVENT_ONESHOT
+         */
+        void setEnabled(int type);
+};
+
+} // namespace watchdog
+} // namespace phosphor