| Matt Spinler | 2de67cf | 2017-04-27 11:07:53 -0500 | [diff] [blame] | 1 | /** | 
|  | 2 | * Copyright © 2017 IBM Corporation | 
|  | 3 | * | 
|  | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | * you may not use this file except in compliance with the License. | 
|  | 6 | * You may obtain a copy of the License at | 
|  | 7 | * | 
|  | 8 | *     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | * | 
|  | 10 | * Unless required by applicable law or agreed to in writing, software | 
|  | 11 | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | * See the License for the specific language governing permissions and | 
|  | 14 | * limitations under the License. | 
|  | 15 | */ | 
|  | 16 | #include <chrono> | 
|  | 17 | #include <phosphor-logging/log.hpp> | 
|  | 18 | #include <type_traits> | 
|  | 19 | #include "timer.hpp" | 
|  | 20 |  | 
|  | 21 | namespace phosphor | 
|  | 22 | { | 
|  | 23 | namespace fan | 
|  | 24 | { | 
|  | 25 | namespace util | 
|  | 26 | { | 
|  | 27 |  | 
|  | 28 | using namespace phosphor::logging; | 
|  | 29 |  | 
| Matt Spinler | e824f98 | 2017-05-11 10:07:55 -0500 | [diff] [blame^] | 30 | Timer::Timer(phosphor::fan::event::EventPtr& events, | 
| Matt Spinler | 2de67cf | 2017-04-27 11:07:53 -0500 | [diff] [blame] | 31 | std::function<void()> callbackFunc) : | 
|  | 32 | timeEvent(events), | 
|  | 33 | callback(callbackFunc), | 
|  | 34 | timeout(0) | 
|  | 35 | { | 
|  | 36 | sd_event_source* source = nullptr; | 
|  | 37 |  | 
|  | 38 | // Start with an infinite expiration time | 
|  | 39 | auto r = sd_event_add_time(timeEvent.get(), | 
|  | 40 | &source, | 
|  | 41 | CLOCK_MONOTONIC, // Time base | 
|  | 42 | UINT64_MAX,      // Expire time - way long time | 
|  | 43 | 0,               // Use default event accuracy | 
|  | 44 | timeoutHandler,  // Callback handler on timeout | 
|  | 45 | this);           // User data | 
|  | 46 | if (r < 0) | 
|  | 47 | { | 
|  | 48 | log<level::ERR>("Timer::Timer failed call to sd_event_add_time", | 
|  | 49 | entry("ERROR=%s", strerror(-r))); | 
|  | 50 | //TODO openbmc/openbmc#1555 throw an elog | 
|  | 51 | throw std::runtime_error("Timer initialization failed"); | 
|  | 52 | } | 
|  | 53 |  | 
|  | 54 | eventSource.reset(source); | 
|  | 55 |  | 
|  | 56 | //Ensure timer isn't running | 
|  | 57 | setTimer(SD_EVENT_OFF); | 
|  | 58 | } | 
|  | 59 |  | 
|  | 60 |  | 
|  | 61 | Timer::~Timer() | 
|  | 62 | { | 
|  | 63 | setTimer(SD_EVENT_OFF); | 
|  | 64 | } | 
|  | 65 |  | 
|  | 66 |  | 
|  | 67 | int Timer::timeoutHandler(sd_event_source* eventSource, | 
|  | 68 | uint64_t usec, void* userData) | 
|  | 69 | { | 
|  | 70 | auto timer = static_cast<Timer*>(userData); | 
|  | 71 |  | 
|  | 72 | if (timer->type == TimerType::repeating) | 
|  | 73 | { | 
|  | 74 | //Set the next expiration time | 
|  | 75 | timer->setTimeout(); | 
|  | 76 | } | 
|  | 77 |  | 
|  | 78 | timer->callback(); | 
|  | 79 |  | 
|  | 80 | return 0; | 
|  | 81 | } | 
|  | 82 |  | 
|  | 83 |  | 
|  | 84 | std::chrono::microseconds Timer::getTime() | 
|  | 85 | { | 
|  | 86 | using namespace std::chrono; | 
|  | 87 | auto now = steady_clock::now().time_since_epoch(); | 
|  | 88 | return duration_cast<microseconds>(now); | 
|  | 89 | } | 
|  | 90 |  | 
|  | 91 |  | 
|  | 92 | void Timer::setTimer(int action) | 
|  | 93 | { | 
|  | 94 | auto r = sd_event_source_set_enabled(eventSource.get(), action); | 
|  | 95 | if (r < 0) | 
|  | 96 | { | 
|  | 97 | log<level::ERR>("Failed call to sd_event_source_set_enabled", | 
|  | 98 | entry("ERROR=%s", strerror(-r)), | 
|  | 99 | entry("ACTION=%d", action)); | 
|  | 100 | //TODO openbmc/openbmc#1555 throw an elog | 
|  | 101 | throw std::runtime_error("Failed call to sd_event_source_set_enabled"); | 
|  | 102 | } | 
|  | 103 | } | 
|  | 104 |  | 
|  | 105 |  | 
|  | 106 | void Timer::stop() | 
|  | 107 | { | 
|  | 108 | setTimer(SD_EVENT_OFF); | 
|  | 109 | } | 
|  | 110 |  | 
|  | 111 |  | 
|  | 112 | bool Timer::running() | 
|  | 113 | { | 
|  | 114 | int status = 0; | 
|  | 115 |  | 
|  | 116 | //returns SD_EVENT_OFF, SD_EVENT_ON, or SD_EVENT_ONESHOT | 
|  | 117 | auto r = sd_event_source_get_enabled(eventSource.get(), &status); | 
|  | 118 | if (r < 0) | 
|  | 119 | { | 
|  | 120 | log<level::ERR>("Failed call to sd_event_source_get_enabled", | 
|  | 121 | entry("ERROR=%s", strerror(-r))); | 
|  | 122 | //TODO openbmc/openbmc#1555 throw an elog | 
|  | 123 | throw std::runtime_error("Failed call to sd_event_source_get_enabled"); | 
|  | 124 | } | 
|  | 125 |  | 
|  | 126 | return (status != SD_EVENT_OFF); | 
|  | 127 | } | 
|  | 128 |  | 
|  | 129 |  | 
|  | 130 | void Timer::setTimeout() | 
|  | 131 | { | 
|  | 132 | //Get the current time and add the delta | 
|  | 133 | static_assert(std::is_same<decltype(getTime()), | 
|  | 134 | std::chrono::microseconds>::value, | 
|  | 135 | "Timer::getTime() is the wrong type"); | 
|  | 136 | static_assert(std::is_same<decltype(timeout), | 
|  | 137 | std::chrono::microseconds>::value, | 
|  | 138 | "Timer::timeout is the wrong type"); | 
|  | 139 |  | 
|  | 140 | auto expireTime = getTime() + timeout; | 
|  | 141 |  | 
|  | 142 | //Set the time | 
|  | 143 | auto r = sd_event_source_set_time(eventSource.get(), expireTime.count()); | 
|  | 144 | if (r < 0) | 
|  | 145 | { | 
|  | 146 | log<level::ERR>("Failed call to sd_event_source_set_time", | 
|  | 147 | entry("ERROR=%s", strerror(-r))); | 
|  | 148 | //TODO openbmc/openbmc#1555 throw an elog | 
|  | 149 | throw std::runtime_error("Failed call to sd_event_source_set_time"); | 
|  | 150 | } | 
|  | 151 | } | 
|  | 152 |  | 
|  | 153 |  | 
|  | 154 | void Timer::start(std::chrono::microseconds timeValue, | 
|  | 155 | TimerType timerType) | 
|  | 156 | { | 
|  | 157 | type = timerType; | 
|  | 158 |  | 
|  | 159 | // Disable the timer | 
|  | 160 | setTimer(SD_EVENT_OFF); | 
|  | 161 |  | 
|  | 162 | //Rearm the timer | 
|  | 163 | timeout = timeValue; | 
|  | 164 | setTimeout(); | 
|  | 165 |  | 
|  | 166 | setTimer((type == TimerType::oneshot) ? SD_EVENT_ONESHOT : SD_EVENT_ON); | 
|  | 167 | } | 
|  | 168 |  | 
|  | 169 |  | 
|  | 170 | } | 
|  | 171 | } | 
|  | 172 | } |