1 /* 2 * Copyright (C) 2020 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 #pragma once 18 19 #include <android-base/thread_annotations.h> 20 #include <chrono> 21 #include <map> 22 #include <mutex> 23 #include <thread> 24 25 namespace android::mediametrics { 26 27 class TimedAction { 28 public: TimedAction()29 TimedAction() : mThread{[this](){threadLoop();}} {} 30 ~TimedAction()31 ~TimedAction() { 32 quit(); 33 } 34 35 // TODO: return a handle for cancelling the action? 36 template <typename T> // T is in units of std::chrono::duration. postIn(const T & time,std::function<void ()> f)37 void postIn(const T& time, std::function<void()> f) { 38 postAt(std::chrono::steady_clock::now() + time, f); 39 } 40 41 template <typename T> // T is in units of std::chrono::time_point postAt(const T & targetTime,std::function<void ()> f)42 void postAt(const T& targetTime, std::function<void()> f) { 43 std::lock_guard l(mLock); 44 if (mQuit) return; 45 if (mMap.empty() || targetTime < mMap.begin()->first) { 46 mMap.emplace_hint(mMap.begin(), targetTime, std::move(f)); 47 mCondition.notify_one(); 48 } else { 49 mMap.emplace(targetTime, std::move(f)); 50 } 51 } 52 clear()53 void clear() { 54 std::lock_guard l(mLock); 55 mMap.clear(); 56 } 57 quit()58 void quit() { 59 { 60 std::lock_guard l(mLock); 61 if (mQuit) return; 62 mQuit = true; 63 mMap.clear(); 64 mCondition.notify_all(); 65 } 66 mThread.join(); 67 } 68 size()69 size_t size() const { 70 std::lock_guard l(mLock); 71 return mMap.size(); 72 } 73 74 private: threadLoop()75 void threadLoop() NO_THREAD_SAFETY_ANALYSIS { // thread safety doesn't cover unique_lock 76 std::unique_lock l(mLock); 77 while (!mQuit) { 78 auto sleepUntilTime = std::chrono::time_point<std::chrono::steady_clock>::max(); 79 if (!mMap.empty()) { 80 sleepUntilTime = mMap.begin()->first; 81 if (sleepUntilTime <= std::chrono::steady_clock::now()) { 82 auto node = mMap.extract(mMap.begin()); // removes from mMap. 83 l.unlock(); 84 node.mapped()(); 85 l.lock(); 86 continue; 87 } 88 } 89 mCondition.wait_until(l, sleepUntilTime); 90 } 91 } 92 93 mutable std::mutex mLock; 94 std::condition_variable mCondition GUARDED_BY(mLock); 95 bool mQuit GUARDED_BY(mLock) = false; 96 std::multimap<std::chrono::time_point<std::chrono::steady_clock>, std::function<void()>> 97 mMap GUARDED_BY(mLock); // multiple functions could execute at the same time. 98 99 // needs to be initialized after the variables above, done in constructor initializer list. 100 std::thread mThread; 101 }; 102 103 } // namespace android::mediametrics 104