Support custom callback function on timer expiration

Change-Id: I39b32d608ef342d63c57cbc1902e927fb39861c7
Signed-off-by: Andrew Geissler <andrewg@us.ibm.com>
diff --git a/softoff/test/utest.cpp b/softoff/test/utest.cpp
index a9dae9c..9ffb04e 100644
--- a/softoff/test/utest.cpp
+++ b/softoff/test/utest.cpp
@@ -15,7 +15,7 @@
         int rc;
 
         // Source of event
-        sd_event_source* eventSource;
+        sd_event_source* eventSource = nullptr;
 
         // Add a Timer Object
         Timer timer;
@@ -37,6 +37,51 @@
         }
 };
 
+
+class TimerTestCallBack : public ::testing::Test
+{
+    public:
+        // systemd event handler
+        sd_event* events;
+
+        // Need this so that events can be initialized.
+        int rc;
+
+        // Source of event
+        sd_event_source* eventSource = nullptr;
+
+        // Add a Timer Object
+        std::unique_ptr<Timer> timer = nullptr;
+
+        // Indicates optional call back fun was called
+        bool callBackDone = false;
+
+        void callBack()
+        {
+            callBackDone = true;
+        }
+
+        // Gets called as part of each TEST_F construction
+        TimerTestCallBack()
+            : rc(sd_event_default(&events))
+
+        {
+            // Check for successful creation of
+            // event handler and timer object.
+            EXPECT_GE(rc, 0);
+
+            std::function<void()> func(std::bind(
+                    &TimerTestCallBack::callBack, this));
+            timer = std::make_unique<Timer>(events, func);
+        }
+
+        // Gets called as part of each TEST_F destruction
+        ~TimerTestCallBack()
+        {
+            events = sd_event_unref(events);
+        }
+};
+
 /** @brief Makes sure that timer is expired and the
  *  callback handler gets invoked post 2 seconds
  */
@@ -162,3 +207,59 @@
     // 2 becase of one more count that happens prior to exiting
     EXPECT_EQ(2, count);
 }
+
+/** @brief Makes sure that optional callback is called */
+TEST_F(TimerTestCallBack, optionalFuncCallBackDone)
+{
+    using namespace std::chrono;
+
+    auto time = duration_cast<microseconds>(seconds(2));
+    EXPECT_GE(timer->startTimer(time), 0);
+
+    // Waiting 2 seconds is enough here since we have
+    // already spent some usec now
+    int count = 0;
+    while(count < 2 && !timer->isExpired())
+    {
+        // Returns -0- on timeout and positive number on dispatch
+        auto sleepTime = duration_cast<microseconds>(seconds(1));
+        if(!sd_event_run(events, sleepTime.count()))
+        {
+            count++;
+        }
+    }
+    EXPECT_EQ(true, timer->isExpired());
+    EXPECT_EQ(true, callBackDone);
+    EXPECT_EQ(1, count);
+}
+
+/** @brief Makes sure that timer is not expired
+ */
+TEST_F(TimerTestCallBack, timerNotExpiredAfter2SecondsNoOptionalCallBack)
+{
+    using namespace std::chrono;
+
+    auto time = duration_cast<microseconds>(seconds(2));
+    EXPECT_GE(timer->startTimer(time), 0);
+
+    // Now turn off the timer post a 1 second sleep
+    sleep(1);
+    EXPECT_GE(timer->setTimer(SD_EVENT_OFF), 0);
+
+    // Wait 2 seconds and see that timer is not expired
+    int count = 0;
+    while(count < 2)
+    {
+        // Returns -0- on timeout
+        auto sleepTime = duration_cast<microseconds>(seconds(1));
+        if(!sd_event_run(events, sleepTime.count()))
+        {
+            count++;
+        }
+    }
+    EXPECT_EQ(false, timer->isExpired());
+    EXPECT_EQ(false, callBackDone);
+
+    // 2 because of one more count that happens prior to exiting
+    EXPECT_EQ(2, count);
+}
diff --git a/softoff/timer.cpp b/softoff/timer.cpp
index c626536..4e8fd9a 100644
--- a/softoff/timer.cpp
+++ b/softoff/timer.cpp
@@ -51,6 +51,12 @@
     auto timer = static_cast<Timer*>(userData);
     timer->expired = true;
 
+    // Call optional user call back function if available
+    if(timer->userCallBack)
+    {
+        timer->userCallBack();
+    }
+
     log<level::INFO>("Timer expired");
     return 0;
 }
diff --git a/softoff/timer.hpp b/softoff/timer.hpp
index 9d597f8..cbd3444 100644
--- a/softoff/timer.hpp
+++ b/softoff/timer.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <functional>
 #include <systemd/sd-event.h>
 namespace phosphor
 {
@@ -22,9 +23,12 @@
         /** @brief Constructs timer object
          *
          *  @param[in] events - sd_event pointer
+         *  @param[in] funcCallBack - optional function callback for timer
+         *                            expirations
          */
-        Timer(sd_event* events)
-            : timeEvent(events)
+        Timer(sd_event* events,
+              std::function<void()> userCallBack = nullptr)
+            : timeEvent(events), userCallBack(userCallBack)
         {
             // Initialize the timer
             initialize();
@@ -87,6 +91,9 @@
 
         /** @brief Gets the current time from steady clock */
         static std::chrono::microseconds getTime();
+
+        /** @brief Optional function to call on timer expiration */
+        std::function<void()> userCallBack;
 };
 
 } // namespace ipmi