1 /* 2 * Copyright (C) 2017 The Android Open Source Project 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 17 #ifndef android_hardware_automotive_vehicle_V2_0_RecurrentTimer_H_ 18 #define android_hardware_automotive_vehicle_V2_0_RecurrentTimer_H_ 19 20 #include <atomic> 21 #include <chrono> 22 #include <condition_variable> 23 #include <functional> 24 #include <list> 25 #include <mutex> 26 #include <set> 27 #include <thread> 28 #include <unordered_map> 29 30 /** 31 * This class allows to specify multiple time intervals to receive 32 * notifications. A single thread is used internally. 33 */ 34 class RecurrentTimer { 35 private: 36 using Nanos = std::chrono::nanoseconds; 37 using Clock = std::chrono::steady_clock; 38 using TimePoint = std::chrono::time_point<Clock, Nanos>; 39 public: 40 using Action = std::function<void(const std::vector<int32_t>& cookies)>; 41 42 RecurrentTimer(const Action& action) : mAction(action) { 43 mTimerThread = std::thread(&RecurrentTimer::loop, this, action); 44 } 45 46 virtual ~RecurrentTimer() { 47 stop(); 48 } 49 50 /** 51 * Registers recurrent event for a given interval. Registred events are distinguished by 52 * cookies thus calling this method multiple times with the same cookie will override the 53 * interval provided before. 54 */ 55 void registerRecurrentEvent(std::chrono::nanoseconds interval, int32_t cookie) { 56 TimePoint now = Clock::now(); 57 // Align event time point among all intervals. Thus if we have two intervals 1ms and 2ms, 58 // during every second wake-up both intervals will be triggered. 59 TimePoint absoluteTime = now - Nanos(now.time_since_epoch().count() % interval.count()); 60 61 { 62 std::lock_guard<std::mutex> g(mLock); 63 mCookieToEventsMap[cookie] = { interval, cookie, absoluteTime }; 64 } 65 mCond.notify_one(); 66 } 67 68 void unregisterRecurrentEvent(int32_t cookie) { 69 { 70 std::lock_guard<std::mutex> g(mLock); 71 mCookieToEventsMap.erase(cookie); 72 } 73 mCond.notify_one(); 74 } 75 76 77 private: 78 79 struct RecurrentEvent { 80 Nanos interval; 81 int32_t cookie; 82 TimePoint absoluteTime; // Absolute time of the next event. 83 84 void updateNextEventTime(TimePoint now) { 85 // We want to move time to next event by adding some number of intervals (usually 1) 86 // to previous absoluteTime. 87 int intervalMultiplier = (now - absoluteTime) / interval; 88 if (intervalMultiplier <= 0) intervalMultiplier = 1; 89 absoluteTime += intervalMultiplier * interval; 90 } 91 }; 92 93 void loop(const Action& action) { 94 static constexpr auto kInvalidTime = TimePoint(Nanos::max()); 95 96 std::vector<int32_t> cookies; 97 98 while (!mStopRequested) { 99 auto now = Clock::now(); 100 auto nextEventTime = kInvalidTime; 101 cookies.clear(); 102 103 { 104 std::unique_lock<std::mutex> g(mLock); 105 106 for (auto&& it : mCookieToEventsMap) { 107 RecurrentEvent& event = it.second; 108 if (event.absoluteTime <= now) { 109 event.updateNextEventTime(now); 110 cookies.push_back(event.cookie); 111 } 112 113 if (nextEventTime > event.absoluteTime) { 114 nextEventTime = event.absoluteTime; 115 } 116 } 117 } 118 119 if (cookies.size() != 0) { 120 action(cookies); 121 } 122 123 std::unique_lock<std::mutex> g(mLock); 124 mCond.wait_until(g, nextEventTime); // nextEventTime can be nanoseconds::max() 125 } 126 } 127 128 void stop() { 129 mStopRequested = true; 130 { 131 std::lock_guard<std::mutex> g(mLock); 132 mCookieToEventsMap.clear(); 133 } 134 mCond.notify_one(); 135 if (mTimerThread.joinable()) { 136 mTimerThread.join(); 137 } 138 } 139 private: 140 mutable std::mutex mLock; 141 std::thread mTimerThread; 142 std::condition_variable mCond; 143 std::atomic_bool mStopRequested { false }; 144 Action mAction; 145 std::unordered_map<int32_t, RecurrentEvent> mCookieToEventsMap; 146 }; 147 148 149 #endif // android_hardware_automotive_vehicle_V2_0_RecurrentTimer_H 150