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/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);
+}
+
+
+}
+}