event: add oneshot timer support

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I9785fa52e38202821258ad162e6f9168c71e53f7
diff --git a/include/sdbusplus/event.hpp b/include/sdbusplus/event.hpp
index e1e99ec..eede9ae 100644
--- a/include/sdbusplus/event.hpp
+++ b/include/sdbusplus/event.hpp
@@ -110,6 +110,12 @@
     /** Add a eventfd-based sdbusplus::event::condition to the run-loop. */
     condition add_condition(sd_event_io_handler_t handler, void* data);
 
+    /** Add a one shot timer source to the run-loop. */
+    source add_oneshot_timer(
+        sd_event_time_handler_t handler, void* data,
+        std::chrono::microseconds time,
+        std::chrono::microseconds accuracy = std::chrono::milliseconds(1));
+
     friend source;
 
   private:
diff --git a/src/event.cpp b/src/event.cpp
index e9c11af..295191d 100644
--- a/src/event.cpp
+++ b/src/event.cpp
@@ -145,6 +145,26 @@
     }
 }
 
+source event::add_oneshot_timer(sd_event_time_handler_t handler, void* data,
+                                std::chrono::microseconds time,
+                                std::chrono::microseconds accuracy)
+{
+    auto l = obtain_lock();
+
+    source s{*this};
+
+    auto rc = sd_event_add_time_relative(eventp, &s.sourcep, CLOCK_BOOTTIME,
+                                         time.count(), accuracy.count(),
+                                         handler, data);
+
+    if (rc < 0)
+    {
+        throw exception::SdBusError(-rc, __func__);
+    }
+
+    return s;
+}
+
 int event::run_wakeup(sd_event_source*, int, uint32_t, void* data)
 {
     auto self = static_cast<event*>(data);
diff --git a/test/event/event.cpp b/test/event/event.cpp
index dbe470c..382b92b 100644
--- a/test/event/event.cpp
+++ b/test/event/event.cpp
@@ -56,3 +56,27 @@
     EXPECT_TRUE(ran);
     c.ack();
 }
+
+TEST_F(Event, Timer)
+{
+    static constexpr auto timeout = 50ms;
+
+    struct handler
+    {
+        static int _(sd_event_source*, uint64_t, void* data)
+        {
+            *static_cast<bool*>(data) = true;
+            return 0;
+        }
+    };
+    bool ran = false;
+
+    auto start = std::chrono::steady_clock::now();
+    auto c = ev.add_oneshot_timer(handler::_, &ran, timeout);
+    ev.run_one();
+    auto stop = std::chrono::steady_clock::now();
+
+    EXPECT_TRUE(ran);
+    EXPECT_TRUE(stop - start > timeout);
+    EXPECT_TRUE(stop - start < timeout * 2);
+}