Add Timer class

The class provides for a timer, callback invocation on timer expiry, and
timer control/cleanup. This will be used in subsequent commits.

Change-Id: Ic8f43264a47903732b590a360ef0d8af765d5a20
Signed-off-by: Nagaraju Goruganti <ngorugan@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 06b7331..dbd84f9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -14,7 +14,8 @@
 
 phosphor_chassis_state_manager_SOURCES = \
 	chassis_state_manager.cpp \
-	chassis_state_manager_main.cpp
+	chassis_state_manager_main.cpp \
+	timer.cpp
 
 phosphor_bmc_state_manager_SOURCES = \
 	bmc_state_manager.cpp \
diff --git a/timer.cpp b/timer.cpp
new file mode 100644
index 0000000..ceb4c29
--- /dev/null
+++ b/timer.cpp
@@ -0,0 +1,89 @@
+#include <chrono>
+#include <system_error>
+#include <string.h>
+#include "timer.hpp"
+
+namespace phosphor
+{
+namespace state
+{
+namespace manager
+{
+static std::chrono::microseconds getTime()
+{
+    using namespace std::chrono;
+    auto usec = steady_clock::now().time_since_epoch();
+    return duration_cast<microseconds>(usec);
+}
+
+int Timer::state(timer::Action value)
+{
+    action_ = value;
+    return sd_event_source_set_enabled(eventSource_.get(), action_);
+}
+
+timer::Action Timer::getAction() const
+{
+    return action_;
+}
+
+std::chrono::microseconds Timer::getDuration() const
+{
+    return duration_;
+}
+
+Timer::Timer(EventPtr& event, std::function<void()> callback,
+             std::chrono::microseconds usec, timer::Action action) :
+    event_(event),
+    callback_(callback), duration_(usec), action_(action)
+{
+    // Add infinite expiration time
+    sd_event_source* sourcePtr = nullptr;
+
+    auto r = sd_event_add_time(event_.get(), &sourcePtr,
+                               CLOCK_MONOTONIC,            // Time base
+                               (getTime() + usec).count(), // When to fire
+                               0,              // Use default event accuracy
+                               timeoutHandler, // Callback handler on timeout
+                               this);          // User data
+
+    if (r < 0)
+    {
+        throw std::system_error(r, std::generic_category(), strerror(-r));
+    }
+
+    eventSource_.reset(sourcePtr);
+}
+
+int Timer::timeoutHandler(sd_event_source* eventSrc, uint64_t usec,
+                          void* userData)
+{
+    auto timer = static_cast<Timer*>(userData);
+
+    if (timer->getAction() == timer::ON)
+    {
+        auto r = sd_event_source_set_time(
+            eventSrc, (getTime() + timer->getDuration()).count());
+        if (r < 0)
+        {
+            throw std::system_error(r, std::generic_category(), strerror(-r));
+        }
+
+        r = sd_event_source_set_enabled(eventSrc, timer::ON);
+        if (r < 0)
+        {
+            throw std::system_error(r, std::generic_category(), strerror(-r));
+        }
+    }
+
+    if (timer->callback_)
+    {
+        timer->callback_();
+    }
+
+    return 0;
+}
+
+} // namespace manager
+} // namespace state
+} // namespace phosphor
diff --git a/timer.hpp b/timer.hpp
new file mode 100644
index 0000000..f5f9ada
--- /dev/null
+++ b/timer.hpp
@@ -0,0 +1,112 @@
+#pragma once
+
+#include <memory>
+#include <chrono>
+#include <functional>
+#include <systemd/sd-event.h>
+
+namespace phosphor
+{
+namespace state
+{
+namespace manager
+{
+namespace timer
+{
+
+enum Action
+{
+    OFF = SD_EVENT_OFF,
+    ON = SD_EVENT_ON,
+    ONESHOT = SD_EVENT_ONESHOT
+};
+} // namespace timer
+
+/* 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 Provides a timer source and a mechanism to callback when the timer
+ *         expires.
+ *
+ *  The timer is armed upon construction. The constructor requires a timeout
+ *  handler function, the timer expiry duration, and the timer state (one-shot,
+ *  reptitive, disabled).
+ *  It's possible to change the state of the timer after it's been armed via the
+ *  state() API.
+ */
+class Timer
+{
+  public:
+    Timer() = delete;
+    Timer(const Timer&) = delete;
+    Timer& operator=(const Timer&) = delete;
+    Timer(Timer&&) = delete;
+    Timer& operator=(Timer&&) = delete;
+
+    /** @brief Constructs timer object
+     *
+     *  @param[in] events - sd_event pointer
+     *  @param[in] callback - function callback for timer expiry
+     *  @param[in] usec - timer duration, in micro seconds
+     *  @param[in] action - controls the timer's lifetime
+     */
+    Timer(EventPtr& event, std::function<void()> userCallback,
+          std::chrono::microseconds usec, timer::Action action);
+
+    /** @brief Enables / disables the timer
+     *  @param[in] action - controls the timer's lifetime
+     */
+    int state(timer::Action value);
+
+    timer::Action getAction() const;
+
+    std::chrono::microseconds getDuration() const;
+
+  private:
+    /** @brief Reference to sd_event unique pointer */
+    EventPtr& event_;
+
+    /** @brief Source of events */
+    EventSourcePtr eventSource_;
+
+    /** @brief Optional function to call on timer expiration */
+    std::function<void()> callback_{};
+
+    /** @brief Duration of the timer */
+    std::chrono::microseconds duration_{};
+
+    /** @brief whether the timer is enabled/disabled/one-shot */
+    timer::Action action_ = timer::OFF;
+
+    /** @brief Timer expiry handler - invokes callback
+     *
+     *  @param[in] eventSource - Source of the event
+     *  @param[in] usec        - time in micro seconds
+     *  @param[in] userData    - User data pointer
+     *
+     *  @return zero on success, non-zero otherwise
+     */
+    static int timeoutHandler(sd_event_source* eventSource, uint64_t usec,
+                              void* userData);
+};
+} // namespace manager
+} // namespace state
+} // namespace phosphor