| #pragma once |
| |
| #include <systemd/sd-event.h> |
| |
| #include <chrono> |
| #include <functional> |
| #include <stdexcept> |
| |
| namespace phosphor |
| { |
| |
| /** @class Timer |
| * @brief Manages starting watchdog timers and handling timeouts |
| */ |
| class Timer |
| { |
| public: |
| /** @brief Only need the default Timer */ |
| Timer() = delete; |
| Timer(const Timer&) = delete; |
| Timer& operator=(const Timer&) = delete; |
| Timer(Timer&&) = delete; |
| Timer& operator=(Timer&&) = delete; |
| |
| /** @brief Constructs timer object |
| * Uses the default sd_event object |
| * |
| * @param[in] funcCallBack - optional function callback for timer |
| * expirations |
| */ |
| Timer(std::function<void()> userCallBack = nullptr) : |
| event(nullptr), eventSource(nullptr), expired(false), |
| userCallBack(userCallBack) |
| { |
| // take a reference to the default event object |
| sd_event_default(&event); |
| // Initialize the timer |
| initialize(); |
| } |
| |
| /** @brief Constructs timer object |
| * |
| * @param[in] events - sd_event pointer |
| * @param[in] funcCallBack - optional function callback for timer |
| * expirations |
| */ |
| Timer(sd_event* event, std::function<void()> userCallBack = nullptr) : |
| event(event), eventSource(nullptr), expired(false), |
| userCallBack(userCallBack) |
| { |
| if (!event) |
| { |
| // take a reference to the default event object |
| sd_event_default(&event); |
| } |
| else |
| { |
| // take a reference to the event; the caller can |
| // keep their ref or drop it without harm |
| sd_event_ref(event); |
| } |
| // Initialize the timer |
| initialize(); |
| } |
| |
| ~Timer() |
| { |
| // the *_unref functions do nothing if passed nullptr |
| // so no need to check first |
| eventSource = sd_event_source_unref(eventSource); |
| event = sd_event_unref(event); |
| } |
| |
| inline bool isExpired() const |
| { |
| return expired; |
| } |
| |
| inline bool isRunning() const |
| { |
| int running = 0; |
| if (sd_event_source_get_enabled(eventSource, &running) < 0) |
| { |
| return false; |
| } |
| return running != SD_EVENT_OFF; |
| } |
| |
| /** @brief Starts the timer with specified expiration value. |
| * input is an offset from the current steady_clock |
| */ |
| int start(std::chrono::microseconds usec, bool periodic = false) |
| { |
| // Disable the timer |
| stop(); |
| expired = false; |
| duration = usec; |
| if (periodic) |
| { |
| // A periodic timer means that when the timer goes off, |
| // it automatically rearms and starts running again |
| runType = SD_EVENT_ON; |
| } |
| else |
| { |
| // A ONESHOT timer means that when the timer goes off, |
| // it moves to disabled state. |
| runType = SD_EVENT_ONESHOT; |
| } |
| |
| // Get the current MONOTONIC time and add the delta |
| auto expireTime = getTime() + usec; |
| |
| // Set the time |
| int r = sd_event_source_set_time(eventSource, expireTime.count()); |
| if (r < 0) |
| { |
| throw std::runtime_error("Failure to set timer"); |
| } |
| |
| r = setEnabled(runType); |
| if (r < 0) |
| { |
| throw std::runtime_error("Failure to start timer"); |
| } |
| return r; |
| } |
| |
| int stop() |
| { |
| return setEnabled(SD_EVENT_OFF); |
| } |
| |
| private: |
| /** @brief the sd_event structure */ |
| sd_event* event; |
| |
| /** @brief Source of events */ |
| sd_event_source* eventSource; |
| |
| /** @brief Returns if the associated timer is expired |
| * |
| * This is set to true when the timeoutHandler is called into |
| */ |
| bool expired; |
| |
| /** @brief Optional function to call on timer expiration */ |
| std::function<void()> userCallBack; |
| |
| /** @brief timer duration */ |
| std::chrono::microseconds duration; |
| |
| /** @brief timer run type (oneshot or periodic) */ |
| int runType; |
| |
| /** @brief Initializes the timer object with infinite |
| * expiration time and sets up the callback handler |
| * |
| * @return None. |
| * |
| * @error std::runtime exception thrown |
| */ |
| void initialize() |
| { |
| if (!event) |
| { |
| throw std::runtime_error("Timer has no event loop"); |
| } |
| // This can not be called more than once. |
| if (eventSource) |
| { |
| throw std::runtime_error("Timer already initialized"); |
| } |
| |
| // Add infinite expiration time |
| auto r = sd_event_add_time( |
| event, &eventSource, |
| CLOCK_MONOTONIC, // Time base |
| UINT64_MAX, // Expire time - way long time |
| 0, // Use default event accuracy |
| [](sd_event_source* /*eventSource*/, uint64_t /*usec*/, |
| void* userData) { |
| auto timer = static_cast<Timer*>(userData); |
| return timer->timeoutHandler(); |
| }, // Callback handler on timeout |
| this); // User data |
| if (r < 0) |
| { |
| throw std::runtime_error("Timer initialization failed"); |
| } |
| |
| // Disable the timer for now |
| r = stop(); |
| if (r < 0) |
| { |
| throw std::runtime_error("Disabling the timer failed"); |
| } |
| } |
| |
| /** @brief Enables / disables the timer */ |
| int setEnabled(int action) |
| { |
| return sd_event_source_set_enabled(eventSource, action); |
| } |
| |
| /** @brief Callback function when timer goes off |
| * |
| * On getting the signal, initiate the hard power off request |
| * |
| * @param[in] eventSource - Source of the event |
| * @param[in] usec - time in micro seconds |
| * @param[in] userData - User data pointer |
| * |
| */ |
| int timeoutHandler() |
| { |
| if (runType == SD_EVENT_ON) |
| { |
| // set the event to the future |
| auto expireTime = getTime() + duration; |
| |
| // Set the time |
| int r = sd_event_source_set_time(eventSource, expireTime.count()); |
| if (r < 0) |
| { |
| throw std::runtime_error("Failure to set timer"); |
| } |
| } |
| expired = true; |
| |
| // Call optional user call back function if available |
| if (userCallBack) |
| { |
| userCallBack(); |
| } |
| |
| int running; |
| if (sd_event_source_get_enabled(eventSource, &running) < 0 || |
| running == SD_EVENT_ONESHOT) |
| { |
| stop(); |
| } |
| return 0; |
| } |
| |
| /** @brief Gets the current time from steady clock */ |
| static std::chrono::microseconds getTime() |
| { |
| using namespace std::chrono; |
| auto usec = steady_clock::now().time_since_epoch(); |
| return duration_cast<microseconds>(usec); |
| } |
| }; |
| |
| } // namespace phosphor |