1 /*
2  * Copyright 2019 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 <array>
21 #include <functional>
22 #include <memory>
23 #include <mutex>
24 #include <string>
25 #include <string_view>
26 #include <unordered_map>
27 
28 #include "SchedulerUtils.h"
29 #include "VSyncDispatch.h"
30 
31 namespace android::scheduler {
32 
33 // VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in
34 // VSyncDispatchTimerQueue hoisted to public for unit testing.
35 class VSyncDispatchTimerQueueEntry {
36 public:
37     // This is the state of the entry. There are 3 states, armed, running, disarmed.
38     // Valid transition: disarmed -> armed ( when scheduled )
39     // Valid transition: armed -> running -> disarmed ( when timer is called)
40     // Valid transition: armed -> disarmed ( when cancelled )
41     VSyncDispatchTimerQueueEntry(std::string const& name, VSyncDispatch::Callback const& fn,
42                                  nsecs_t minVsyncDistance);
43     std::string_view name() const;
44 
45     // Start: functions that are not threadsafe.
46     // Return the last vsync time this callback was invoked.
47     std::optional<nsecs_t> lastExecutedVsyncTarget() const;
48 
49     // This moves the state from disarmed->armed and will calculate the wakeupTime.
50     ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
51                             nsecs_t now);
52     // This will update armed entries with the latest vsync information. Entry remains armed.
53     void update(VSyncTracker& tracker, nsecs_t now);
54 
55     // This will return empty if not armed, or the next calculated wakeup time if armed.
56     // It will not update the wakeupTime.
57     std::optional<nsecs_t> wakeupTime() const;
58 
59     std::optional<nsecs_t> targetVsync() const;
60 
61     // This moves state from armed->disarmed.
62     void disarm();
63 
64     // This moves the state from armed->running.
65     // Store the timestamp that this was intended for as the last called timestamp.
66     nsecs_t executing();
67 
68     // Adds a pending upload of the earliestVSync and workDuration that will be applied on the next
69     // call to update()
70     void addPendingWorkloadUpdate(nsecs_t workDuration, nsecs_t earliestVsync);
71 
72     // Checks if there is a pending update to the workload, returning true if so.
73     bool hasPendingWorkloadUpdate() const;
74     // End: functions that are not threadsafe.
75 
76     // Invoke the callback with the two given timestamps, moving the state from running->disarmed.
77     void callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp);
78     // Block calling thread while the callback is executing.
79     void ensureNotRunning();
80 
81     void dump(std::string& result) const;
82 
83 private:
84     std::string const mName;
85     VSyncDispatch::Callback const mCallback;
86 
87     nsecs_t mWorkDuration;
88     nsecs_t mEarliestVsync;
89     nsecs_t const mMinVsyncDistance;
90 
91     struct ArmingInfo {
92         nsecs_t mActualWakeupTime;
93         nsecs_t mActualVsyncTime;
94     };
95     std::optional<ArmingInfo> mArmedInfo;
96     std::optional<nsecs_t> mLastDispatchTime;
97 
98     struct WorkloadUpdateInfo {
99         nsecs_t duration;
100         nsecs_t earliestVsync;
101     };
102     std::optional<WorkloadUpdateInfo> mWorkloadUpdateInfo;
103 
104     mutable std::mutex mRunningMutex;
105     std::condition_variable mCv;
106     bool mRunning GUARDED_BY(mRunningMutex) = false;
107 };
108 
109 /*
110  * VSyncDispatchTimerQueue is a class that will dispatch callbacks as per VSyncDispatch interface
111  * using a single timer queue.
112  */
113 class VSyncDispatchTimerQueue : public VSyncDispatch {
114 public:
115     // Constructs a VSyncDispatchTimerQueue.
116     // \param[in] tk                    A timekeeper.
117     // \param[in] tracker               A tracker.
118     // \param[in] timerSlack            The threshold at which different similarly timed callbacks
119     //                                  should be grouped into one wakeup.
120     // \param[in] minVsyncDistance      The minimum distance between two vsync estimates before the
121     //                                  vsyncs are considered the same vsync event.
122     explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
123                                      nsecs_t timerSlack, nsecs_t minVsyncDistance);
124     ~VSyncDispatchTimerQueue();
125 
126     CallbackToken registerCallback(Callback const& callbackFn, std::string callbackName) final;
127     void unregisterCallback(CallbackToken token) final;
128     ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
129     CancelResult cancel(CallbackToken token) final;
130     void dump(std::string& result) const final;
131 
132 private:
133     VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
134     VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete;
135 
136     using CallbackMap =
137             std::unordered_map<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
138 
139     void timerCallback();
140     void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
141     void rearmTimer(nsecs_t now) REQUIRES(mMutex);
142     void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
143             REQUIRES(mMutex);
144     void cancelTimer() REQUIRES(mMutex);
145 
146     static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
147     std::unique_ptr<TimeKeeper> const mTimeKeeper;
148     VSyncTracker& mTracker;
149     nsecs_t const mTimerSlack;
150     nsecs_t const mMinVsyncDistance;
151 
152     std::mutex mutable mMutex;
153     size_t mCallbackToken GUARDED_BY(mMutex) = 0;
154 
155     CallbackMap mCallbacks GUARDED_BY(mMutex);
156     nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
157 
158     struct TraceBuffer {
159         static constexpr char const kTraceNamePrefix[] = "-alarm in:";
160         static constexpr char const kTraceNameSeparator[] = " for vs:";
161         static constexpr size_t kMaxNamePrint = 4;
162         static constexpr size_t kNumTsPrinted = 2;
163         static constexpr size_t maxlen = kMaxNamePrint + arrayLen(kTraceNamePrefix) +
164                 arrayLen(kTraceNameSeparator) - 1 + (kNumTsPrinted * max64print);
165         std::array<char, maxlen> str_buffer;
166         void note(std::string_view name, nsecs_t in, nsecs_t vs);
167     } mTraceBuffer GUARDED_BY(mMutex);
168 
169     // For debugging purposes
170     nsecs_t mLastTimerCallback GUARDED_BY(mMutex) = kInvalidTime;
171     nsecs_t mLastTimerSchedule GUARDED_BY(mMutex) = kInvalidTime;
172 };
173 
174 } // namespace android::scheduler
175