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