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 HWUI_WORKQUEUE_H
18 #define HWUI_WORKQUEUE_H
19 
20 #include "utils/Macros.h"
21 
22 #include <log/log.h>
23 #include <utils/Timers.h>
24 
25 #include <condition_variable>
26 #include <functional>
27 #include <future>
28 #include <mutex>
29 #include <variant>
30 #include <vector>
31 
32 namespace android::uirenderer {
33 
34 struct MonotonicClock {
nowMonotonicClock35     static nsecs_t now() { return systemTime(CLOCK_MONOTONIC); }
36 };
37 
38 class WorkQueue {
39     PREVENT_COPY_AND_ASSIGN(WorkQueue);
40 
41 public:
42     using clock = MonotonicClock;
43 
44 private:
45     struct WorkItem {
46         WorkItem() = delete;
47         WorkItem(const WorkItem& other) = delete;
48         WorkItem& operator=(const WorkItem& other) = delete;
49         WorkItem(WorkItem&& other) = default;
50         WorkItem& operator=(WorkItem&& other) = default;
51 
WorkItemWorkItem52         WorkItem(nsecs_t runAt, std::function<void()>&& work)
53                 : runAt(runAt), work(std::move(work)) {}
54 
55         nsecs_t runAt;
56         std::function<void()> work;
57     };
58 
59 public:
WorkQueue(std::function<void ()> && wakeFunc,std::mutex & lock)60     WorkQueue(std::function<void()>&& wakeFunc, std::mutex& lock)
61             : mWakeFunc(move(wakeFunc)), mLock(lock) {}
62 
process()63     void process() {
64         auto now = clock::now();
65         std::vector<WorkItem> toProcess;
66         {
67             std::unique_lock _lock{mLock};
68             if (mWorkQueue.empty()) return;
69             toProcess = std::move(mWorkQueue);
70             auto moveBack = find_if(std::begin(toProcess), std::end(toProcess),
71                                     [&now](WorkItem& item) { return item.runAt > now; });
72             if (moveBack != std::end(toProcess)) {
73                 mWorkQueue.reserve(std::distance(moveBack, std::end(toProcess)) + 5);
74                 std::move(moveBack, std::end(toProcess), std::back_inserter(mWorkQueue));
75                 toProcess.erase(moveBack, std::end(toProcess));
76             }
77         }
78         for (auto& item : toProcess) {
79             item.work();
80         }
81     }
82 
83     template <class F>
postAt(nsecs_t time,F && func)84     void postAt(nsecs_t time, F&& func) {
85         enqueue(WorkItem{time, std::function<void()>(std::forward<F>(func))});
86     }
87 
88     template <class F>
postDelayed(nsecs_t delay,F && func)89     void postDelayed(nsecs_t delay, F&& func) {
90         enqueue(WorkItem{clock::now() + delay, std::function<void()>(std::forward<F>(func))});
91     }
92 
93     template <class F>
post(F && func)94     void post(F&& func) {
95         postAt(0, std::forward<F>(func));
96     }
97 
98     template <class F>
99     auto async(F&& func) -> std::future<decltype(func())> {
100         typedef std::packaged_task<decltype(func())()> task_t;
101         auto task = std::make_shared<task_t>(std::forward<F>(func));
102         post([task]() { std::invoke(*task); });
103         return task->get_future();
104     }
105 
106     template <class F>
107     auto runSync(F&& func) -> decltype(func()) {
108         std::packaged_task<decltype(func())()> task{std::forward<F>(func)};
109         post([&task]() { std::invoke(task); });
110         return task.get_future().get();
111     };
112 
nextWakeup(std::unique_lock<std::mutex> & lock)113     nsecs_t nextWakeup(std::unique_lock<std::mutex>& lock) {
114         if (mWorkQueue.empty()) {
115             return std::numeric_limits<nsecs_t>::max();
116         } else {
117             return std::begin(mWorkQueue)->runAt;
118         }
119     }
120 
121 private:
enqueue(WorkItem && item)122     void enqueue(WorkItem&& item) {
123         bool needsWakeup;
124         {
125             std::unique_lock _lock{mLock};
126             auto insertAt = std::find_if(
127                     std::begin(mWorkQueue), std::end(mWorkQueue),
128                     [time = item.runAt](WorkItem & item) { return item.runAt > time; });
129             needsWakeup = std::begin(mWorkQueue) == insertAt;
130             mWorkQueue.emplace(insertAt, std::move(item));
131         }
132         if (needsWakeup) {
133             mWakeFunc();
134         }
135     }
136 
137     std::function<void()> mWakeFunc;
138 
139     std::mutex& mLock;
140     std::vector<WorkItem> mWorkQueue;
141 };
142 
143 }  // namespace android::uirenderer
144 
145 #endif  // HWUI_WORKQUEUE_H
146