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 
     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