summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am3
-rw-r--r--timer.cpp172
-rw-r--r--timer.hpp178
3 files changed, 352 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index 7b34537..61c6782 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,7 +7,8 @@ libfan_la_LIBADD = \
libfan_la_CXXFLAGS =
$(SDBUSPLUS_CFLAGS)
libfan_la_SOURCES = \
- utility.cpp
+ utility.cpp \
+ timer.cpp
SUBDIRS = .
diff --git a/timer.cpp b/timer.cpp
new file mode 100644
index 0000000..41a8138
--- /dev/null
+++ b/timer.cpp
@@ -0,0 +1,172 @@
+/**
+ * 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 <type_traits>
+#include "timer.hpp"
+
+namespace phosphor
+{
+namespace fan
+{
+namespace util
+{
+
+using namespace phosphor::logging;
+
+Timer::Timer(EventPtr& 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)));
+ //TODO openbmc/openbmc#1555 throw an elog
+ throw std::runtime_error("Timer initialization failed");
+ }
+
+ 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));
+ //TODO openbmc/openbmc#1555 throw an elog
+ throw std::runtime_error("Failed call to sd_event_source_set_enabled");
+ }
+}
+
+
+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)));
+ //TODO openbmc/openbmc#1555 throw an elog
+ throw std::runtime_error("Failed call to sd_event_source_get_enabled");
+ }
+
+ 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)));
+ //TODO openbmc/openbmc#1555 throw an elog
+ throw std::runtime_error("Failed call to sd_event_source_set_time");
+ }
+}
+
+
+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);
+}
+
+
+}
+}
+}
diff --git a/timer.hpp b/timer.hpp
new file mode 100644
index 0000000..ea28176
--- /dev/null
+++ b/timer.hpp
@@ -0,0 +1,178 @@
+#pragma once
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <systemd/sd-event.h>
+
+namespace phosphor
+{
+namespace fan
+{
+namespace util
+{
+
+struct EventSourceDeleter
+{
+ void operator()(sd_event_source* eventSource) const
+ {
+ sd_event_source_unref(eventSource);
+ }
+};
+
+using EventSourcePtr = std::unique_ptr<sd_event_source, EventSourceDeleter>;
+
+using EventPtr = std::shared_ptr<sd_event>;
+
+/**
+ * @class Timer
+ *
+ * This class implements a simple timer that runs an arbitrary
+ * function on expiration. The timeout value is set in microseconds.
+ * It can be stopped while it is running, and queried to see if it is
+ * running.
+ *
+ * If started with the 'repeating' argument, it will keep calling the
+ * callback function every <timeout> microseconds. If started with the
+ * 'oneshot' argument, it will just call the callback function one time.
+ *
+ * It needs an sd_event loop to function.
+ */
+class Timer
+{
+ public:
+
+ enum class TimerType
+ {
+ oneshot,
+ repeating
+ };
+
+ Timer() = delete;
+ Timer(const Timer&) = delete;
+ Timer& operator=(const Timer&) = delete;
+ Timer(Timer&&) = default;
+ Timer& operator=(Timer&&) = default;
+
+ /**
+ * @brief Constructs timer object
+ *
+ * @param[in] events - sd_event pointer, previously created
+ * @param[in] callbackFunc - The function to call on timer expiration
+ */
+ Timer(EventPtr& events,
+ std::function<void()> callbackFunc);
+
+ /**
+ * @brief Destructor
+ */
+ ~Timer();
+
+ /**
+ * @brief Starts the timer
+ *
+ * The input is an offset from the current steady clock.
+ *
+ * @param[in] usec - the timeout value in microseconds
+ * @param[in] type - either a oneshot, or repeating
+ */
+ void start(std::chrono::microseconds usec,
+ TimerType type = TimerType::oneshot);
+
+ /**
+ * @brief Stop the timer
+ */
+ void stop();
+
+ /**
+ * @brief Returns true if the timer is running
+ */
+ bool running();
+
+ /**
+ * @brief Returns the timeout value
+ *
+ * @return - the last value sent in via start().
+ *
+ * Could be used to restart the timer with the same
+ * timeout. i.e. start(getTimeout());
+ */
+ inline auto getTimeout() const
+ {
+ return timeout;
+ }
+
+ /**
+ * @brief Returns the timer type
+ */
+ inline auto getType() const
+ {
+ return type;
+ }
+
+ private:
+
+ /**
+ * @brief Callback function when timer goes off
+ *
+ * Calls the callback function passed in by the user.
+ *
+ * @param[in] eventSource - Source of the event
+ * @param[in] usec - time in micro seconds
+ * @param[in] userData - User data pointer
+ */
+ static int timeoutHandler(sd_event_source* eventSource,
+ uint64_t usec, void* userData);
+
+ /**
+ * @brief Gets the current time from the steady clock
+ */
+ std::chrono::microseconds getTime();
+
+ /**
+ * @brief Wrapper around sd_event_source_set_enabled
+ *
+ * @param[in] action - either SD_EVENT_OFF, SD_EVENT_ON,
+ * or SD_EVENT_ONESHOT
+ */
+ void setTimer(int action);
+
+
+ /**
+ * @brief Sets the expiration time for the timer
+ *
+ * Sets it to timeout microseconds in the future
+ */
+ void setTimeout();
+
+ /**
+ * @brief The sd_event structure
+ */
+ EventPtr timeEvent;
+
+ /**
+ * @brief Source of events
+ */
+ EventSourcePtr eventSource;
+
+ /**
+ * @brief Either 'repeating' or 'oneshot'
+ */
+ TimerType type = TimerType::oneshot;
+
+ /**
+ * @brief The function to call when the timer expires
+ */
+ std::function<void()> callback;
+
+ /**
+ * @brief What the timer was set to run for
+ *
+ * Not cleared on timer expiration
+ */
+ std::chrono::microseconds timeout;
+};
+
+}
+}
+}
OpenPOWER on IntegriCloud