• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
18 
19 #include <vector>
20 
21 #include <android-base/stringprintf.h>
22 #include <ftl/concat.h>
23 #include <gui/TraceUtils.h>
24 #include <log/log_main.h>
25 
26 #include <scheduler/TimeKeeper.h>
27 
28 #include <common/FlagManager.h>
29 #include "VSyncDispatchTimerQueue.h"
30 #include "VSyncTracker.h"
31 
32 #undef LOG_TAG
33 #define LOG_TAG "VSyncDispatch"
34 
35 namespace android::scheduler {
36 
37 using base::StringAppendF;
38 
39 namespace {
40 
getExpectedCallbackTime(nsecs_t nextVsyncTime,const VSyncDispatch::ScheduleTiming & timing)41 ScheduleResult getExpectedCallbackTime(nsecs_t nextVsyncTime,
42                                        const VSyncDispatch::ScheduleTiming& timing) {
43     return {TimePoint::fromNs(nextVsyncTime - timing.readyDuration - timing.workDuration),
44             TimePoint::fromNs(nextVsyncTime)};
45 }
46 
traceEntry(const VSyncDispatchTimerQueueEntry & entry,nsecs_t now)47 void traceEntry(const VSyncDispatchTimerQueueEntry& entry, nsecs_t now) {
48     if (!ATRACE_ENABLED() || !entry.wakeupTime().has_value() || !entry.targetVsync().has_value()) {
49         return;
50     }
51 
52     ftl::Concat trace(ftl::truncated<5>(entry.name()), " alarm in ",
53                       ns2us(*entry.wakeupTime() - now), "us; VSYNC in ",
54                       ns2us(*entry.targetVsync() - now), "us");
55     ATRACE_FORMAT_INSTANT(trace.c_str());
56 }
57 
58 } // namespace
59 
60 VSyncDispatch::~VSyncDispatch() = default;
61 VSyncTracker::~VSyncTracker() = default;
62 
VSyncDispatchTimerQueueEntry(std::string name,VSyncDispatch::Callback callback,nsecs_t minVsyncDistance)63 VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string name,
64                                                            VSyncDispatch::Callback callback,
65                                                            nsecs_t minVsyncDistance)
66       : mName(std::move(name)),
67         mCallback(std::move(callback)),
68         mMinVsyncDistance(minVsyncDistance) {}
69 
lastExecutedVsyncTarget() const70 std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
71     return mLastDispatchTime;
72 }
73 
name() const74 std::string_view VSyncDispatchTimerQueueEntry::name() const {
75     return mName;
76 }
77 
wakeupTime() const78 std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const {
79     if (!mArmedInfo) {
80         return {};
81     }
82     return {mArmedInfo->mActualWakeupTime};
83 }
84 
readyTime() const85 std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::readyTime() const {
86     if (!mArmedInfo) {
87         return {};
88     }
89     return {mArmedInfo->mActualReadyTime};
90 }
91 
targetVsync() const92 std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const {
93     if (!mArmedInfo) {
94         return {};
95     }
96     return {mArmedInfo->mActualVsyncTime};
97 }
98 
schedule(VSyncDispatch::ScheduleTiming timing,VSyncTracker & tracker,nsecs_t now)99 ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
100                                                       VSyncTracker& tracker, nsecs_t now) {
101     ATRACE_NAME("VSyncDispatchTimerQueueEntry::schedule");
102     auto nextVsyncTime =
103             tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
104                                                           now + timing.workDuration +
105                                                                   timing.readyDuration),
106                                                  timing.lastVsync);
107     auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
108 
109     bool const wouldSkipAVsyncTarget =
110             mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
111     bool const wouldSkipAWakeup =
112             mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
113     ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
114                           wouldSkipAVsyncTarget, wouldSkipAWakeup);
115     if (FlagManager::getInstance().dont_skip_on_early_ro()) {
116         if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
117             nextVsyncTime = mArmedInfo->mActualVsyncTime;
118         } else {
119             nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
120         }
121         nextWakeupTime = std::max(now, nextVsyncTime - timing.workDuration - timing.readyDuration);
122     } else {
123         if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
124             return getExpectedCallbackTime(nextVsyncTime, timing);
125         }
126         nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
127         nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
128     }
129 
130     auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
131     mScheduleTiming = timing;
132     mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
133     return ScheduleResult{TimePoint::fromNs(nextWakeupTime), TimePoint::fromNs(nextVsyncTime)};
134 }
135 
addPendingWorkloadUpdate(VSyncTracker & tracker,nsecs_t now,VSyncDispatch::ScheduleTiming timing)136 ScheduleResult VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(
137         VSyncTracker& tracker, nsecs_t now, VSyncDispatch::ScheduleTiming timing) {
138     mWorkloadUpdateInfo = timing;
139     const auto armedInfo = getArmedInfo(tracker, now, timing, mArmedInfo);
140     return {TimePoint::fromNs(armedInfo.mActualWakeupTime),
141             TimePoint::fromNs(armedInfo.mActualVsyncTime)};
142 }
143 
hasPendingWorkloadUpdate() const144 bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const {
145     return mWorkloadUpdateInfo.has_value();
146 }
147 
adjustVsyncIfNeeded(VSyncTracker & tracker,nsecs_t nextVsyncTime) const148 nsecs_t VSyncDispatchTimerQueueEntry::adjustVsyncIfNeeded(VSyncTracker& tracker,
149                                                           nsecs_t nextVsyncTime) const {
150     bool const alreadyDispatchedForVsync = mLastDispatchTime &&
151             ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
152              (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
153     const nsecs_t currentPeriod = tracker.currentPeriod();
154     bool const nextVsyncTooClose = mLastDispatchTime &&
155             (nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod;
156     if (alreadyDispatchedForVsync) {
157         ATRACE_FORMAT_INSTANT("alreadyDispatchedForVsync");
158         return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance,
159                                                     *mLastDispatchTime);
160     }
161 
162     if (nextVsyncTooClose) {
163         ATRACE_FORMAT_INSTANT("nextVsyncTooClose");
164         return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod,
165                                                     *mLastDispatchTime + currentPeriod);
166     }
167 
168     return nextVsyncTime;
169 }
170 
getArmedInfo(VSyncTracker & tracker,nsecs_t now,VSyncDispatch::ScheduleTiming timing,std::optional<ArmingInfo> armedInfo) const171 auto VSyncDispatchTimerQueueEntry::getArmedInfo(VSyncTracker& tracker, nsecs_t now,
172                                                 VSyncDispatch::ScheduleTiming timing,
173                                                 std::optional<ArmingInfo> armedInfo) const
174         -> ArmingInfo {
175     ATRACE_NAME("VSyncDispatchTimerQueueEntry::getArmedInfo");
176     const auto earliestReadyBy = now + timing.workDuration + timing.readyDuration;
177     const auto earliestVsync = std::max(earliestReadyBy, timing.lastVsync);
178 
179     const auto nextVsyncTime =
180             adjustVsyncIfNeeded(tracker, /*nextVsyncTime*/
181                                 tracker.nextAnticipatedVSyncTimeFrom(earliestVsync,
182                                                                      timing.lastVsync));
183     const auto nextReadyTime = nextVsyncTime - timing.readyDuration;
184     const auto nextWakeupTime = nextReadyTime - timing.workDuration;
185 
186     if (FlagManager::getInstance().dont_skip_on_early_ro()) {
187         bool const wouldSkipAVsyncTarget =
188                 armedInfo && (nextVsyncTime > (armedInfo->mActualVsyncTime + mMinVsyncDistance));
189         bool const wouldSkipAWakeup =
190                 armedInfo && (nextWakeupTime > (armedInfo->mActualWakeupTime + mMinVsyncDistance));
191         ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
192                               wouldSkipAVsyncTarget, wouldSkipAWakeup);
193         if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
194             return *armedInfo;
195         }
196     }
197 
198     return ArmingInfo{nextWakeupTime, nextVsyncTime, nextReadyTime};
199 }
200 
update(VSyncTracker & tracker,nsecs_t now)201 void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
202     ATRACE_NAME("VSyncDispatchTimerQueueEntry::update");
203     if (!mArmedInfo && !mWorkloadUpdateInfo) {
204         return;
205     }
206 
207     if (mWorkloadUpdateInfo) {
208         const auto workDelta = mWorkloadUpdateInfo->workDuration - mScheduleTiming.workDuration;
209         const auto readyDelta = mWorkloadUpdateInfo->readyDuration - mScheduleTiming.readyDuration;
210         const auto lastVsyncDelta = mWorkloadUpdateInfo->lastVsync - mScheduleTiming.lastVsync;
211         ATRACE_FORMAT_INSTANT("Workload updated workDelta=%" PRId64 " readyDelta=%" PRId64
212                               " lastVsyncDelta=%" PRId64,
213                               workDelta, readyDelta, lastVsyncDelta);
214         mScheduleTiming = *mWorkloadUpdateInfo;
215         mWorkloadUpdateInfo.reset();
216     }
217 
218     mArmedInfo = getArmedInfo(tracker, now, mScheduleTiming, mArmedInfo);
219 }
220 
disarm()221 void VSyncDispatchTimerQueueEntry::disarm() {
222     mArmedInfo.reset();
223 }
224 
executing()225 nsecs_t VSyncDispatchTimerQueueEntry::executing() {
226     mLastDispatchTime = mArmedInfo->mActualVsyncTime;
227     disarm();
228     return *mLastDispatchTime;
229 }
230 
callback(nsecs_t vsyncTimestamp,nsecs_t wakeupTimestamp,nsecs_t deadlineTimestamp)231 void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp,
232                                             nsecs_t deadlineTimestamp) {
233     {
234         std::lock_guard<std::mutex> lk(mRunningMutex);
235         mRunning = true;
236     }
237 
238     mCallback(vsyncTimestamp, wakeupTimestamp, deadlineTimestamp);
239 
240     std::lock_guard<std::mutex> lk(mRunningMutex);
241     mRunning = false;
242     mCv.notify_all();
243 }
244 
ensureNotRunning()245 void VSyncDispatchTimerQueueEntry::ensureNotRunning() {
246     std::unique_lock<std::mutex> lk(mRunningMutex);
247     mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
248 }
249 
dump(std::string & result) const250 void VSyncDispatchTimerQueueEntry::dump(std::string& result) const {
251     std::lock_guard<std::mutex> lk(mRunningMutex);
252     std::string armedInfo;
253     if (mArmedInfo) {
254         StringAppendF(&armedInfo,
255                       "[wake up in %.2fms deadline in %.2fms for vsync %.2fms from now]",
256                       (mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f,
257                       (mArmedInfo->mActualReadyTime - systemTime()) / 1e6f,
258                       (mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f);
259     }
260 
261     StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
262                   mRunning ? "(in callback function)" : "", armedInfo.c_str());
263     StringAppendF(&result,
264                   "\t\t\tworkDuration: %.2fms readyDuration: %.2fms lastVsync: %.2fms relative "
265                   "to now\n",
266                   mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f,
267                   (mScheduleTiming.lastVsync - systemTime()) / 1e6f);
268 
269     if (mLastDispatchTime) {
270         StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
271                       (systemTime() - *mLastDispatchTime) / 1e6f);
272     } else {
273         StringAppendF(&result, "\t\t\tmLastDispatchTime unknown\n");
274     }
275 }
276 
VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,VsyncSchedule::TrackerPtr tracker,nsecs_t timerSlack,nsecs_t minVsyncDistance)277 VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
278                                                  VsyncSchedule::TrackerPtr tracker,
279                                                  nsecs_t timerSlack, nsecs_t minVsyncDistance)
280       : mTimeKeeper(std::move(tk)),
281         mTracker(std::move(tracker)),
282         mTimerSlack(timerSlack),
283         mMinVsyncDistance(minVsyncDistance) {}
284 
~VSyncDispatchTimerQueue()285 VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
286     std::lock_guard lock(mMutex);
287     mRunning = false;
288     cancelTimer();
289     for (auto& [_, entry] : mCallbacks) {
290         ALOGE("Forgot to unregister a callback on VSyncDispatch!");
291         entry->ensureNotRunning();
292     }
293 }
294 
cancelTimer()295 void VSyncDispatchTimerQueue::cancelTimer() {
296     mIntendedWakeupTime = kInvalidTime;
297     mTimeKeeper->alarmCancel();
298 }
299 
setTimer(nsecs_t targetTime,nsecs_t)300 void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {
301     mIntendedWakeupTime = targetTime;
302     mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
303                          mIntendedWakeupTime);
304     mLastTimerSchedule = mTimeKeeper->now();
305 }
306 
rearmTimer(nsecs_t now)307 void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) {
308     rearmTimerSkippingUpdateFor(now, mCallbacks.cend());
309 }
310 
rearmTimerSkippingUpdateFor(nsecs_t now,CallbackMap::const_iterator skipUpdateIt)311 void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
312         nsecs_t now, CallbackMap::const_iterator skipUpdateIt) {
313     ATRACE_CALL();
314     std::optional<nsecs_t> min;
315     std::optional<nsecs_t> targetVsync;
316     std::optional<std::string_view> nextWakeupName;
317     for (auto it = mCallbacks.cbegin(); it != mCallbacks.cend(); ++it) {
318         auto& callback = it->second;
319         if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
320             continue;
321         }
322 
323         if (it != skipUpdateIt) {
324             callback->update(*mTracker, now);
325         }
326 
327         traceEntry(*callback, now);
328 
329         const auto wakeupTime = *callback->wakeupTime();
330         if (!min || *min > wakeupTime) {
331             nextWakeupName = callback->name();
332             min = wakeupTime;
333             targetVsync = callback->targetVsync();
334         }
335     }
336 
337     if (min && min < mIntendedWakeupTime) {
338         setTimer(*min, now);
339     } else {
340         ATRACE_NAME("cancel timer");
341         cancelTimer();
342     }
343 }
344 
timerCallback()345 void VSyncDispatchTimerQueue::timerCallback() {
346     ATRACE_CALL();
347     struct Invocation {
348         std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
349         nsecs_t vsyncTimestamp;
350         nsecs_t wakeupTimestamp;
351         nsecs_t deadlineTimestamp;
352     };
353     std::vector<Invocation> invocations;
354     {
355         std::lock_guard lock(mMutex);
356         if (!mRunning) {
357             ALOGD("TimerQueue is not running. Skipping callback.");
358             return;
359         }
360         auto const now = mTimeKeeper->now();
361         mLastTimerCallback = now;
362         for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
363             auto& callback = it->second;
364             auto const wakeupTime = callback->wakeupTime();
365             if (!wakeupTime) {
366                 continue;
367             }
368 
369             traceEntry(*callback, now);
370 
371             auto const readyTime = callback->readyTime();
372             auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
373             if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
374                 callback->executing();
375                 invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
376                                                     *wakeupTime, *readyTime});
377             }
378         }
379 
380         mIntendedWakeupTime = kInvalidTime;
381         rearmTimer(mTimeKeeper->now());
382     }
383 
384     for (auto const& invocation : invocations) {
385         ftl::Concat trace(ftl::truncated<5>(invocation.callback->name()));
386         ATRACE_FORMAT("%s: %s", __func__, trace.c_str());
387         invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
388                                       invocation.deadlineTimestamp);
389     }
390 }
391 
registerCallback(Callback callback,std::string callbackName)392 VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
393         Callback callback, std::string callbackName) {
394     std::lock_guard lock(mMutex);
395     return mCallbacks
396             .try_emplace(++mCallbackToken,
397                          std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName),
398                                                                         std::move(callback),
399                                                                         mMinVsyncDistance))
400             .first->first;
401 }
402 
unregisterCallback(CallbackToken token)403 void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
404     std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
405     {
406         std::lock_guard lock(mMutex);
407         auto it = mCallbacks.find(token);
408         if (it != mCallbacks.end()) {
409             entry = it->second;
410             mCallbacks.erase(it->first);
411         }
412     }
413 
414     if (entry) {
415         entry->ensureNotRunning();
416     }
417 }
418 
schedule(CallbackToken token,ScheduleTiming scheduleTiming)419 std::optional<ScheduleResult> VSyncDispatchTimerQueue::schedule(CallbackToken token,
420                                                                 ScheduleTiming scheduleTiming) {
421     std::lock_guard lock(mMutex);
422     return scheduleLocked(token, scheduleTiming);
423 }
424 
scheduleLocked(CallbackToken token,ScheduleTiming scheduleTiming)425 std::optional<ScheduleResult> VSyncDispatchTimerQueue::scheduleLocked(
426         CallbackToken token, ScheduleTiming scheduleTiming) {
427     auto it = mCallbacks.find(token);
428     if (it == mCallbacks.end()) {
429         return {};
430     }
431     auto& callback = it->second;
432     auto const now = mTimeKeeper->now();
433 
434     /* If the timer thread will run soon, we'll apply this work update via the callback
435      * timer recalculation to avoid cancelling a callback that is about to fire. */
436     auto const rearmImminent = now > mIntendedWakeupTime;
437     if (CC_UNLIKELY(rearmImminent)) {
438         return callback->addPendingWorkloadUpdate(*mTracker, now, scheduleTiming);
439     }
440 
441     const auto result = callback->schedule(scheduleTiming, *mTracker, now);
442 
443     if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
444         rearmTimerSkippingUpdateFor(now, it);
445     }
446 
447     return result;
448 }
449 
update(CallbackToken token,ScheduleTiming scheduleTiming)450 std::optional<ScheduleResult> VSyncDispatchTimerQueue::update(CallbackToken token,
451                                                               ScheduleTiming scheduleTiming) {
452     std::lock_guard lock(mMutex);
453     const auto it = mCallbacks.find(token);
454     if (it == mCallbacks.end()) {
455         return {};
456     }
457 
458     auto& callback = it->second;
459     if (!callback->targetVsync().has_value()) {
460         return {};
461     }
462 
463     return scheduleLocked(token, scheduleTiming);
464 }
465 
cancel(CallbackToken token)466 CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
467     std::lock_guard lock(mMutex);
468 
469     auto it = mCallbacks.find(token);
470     if (it == mCallbacks.end()) {
471         return CancelResult::Error;
472     }
473     auto& callback = it->second;
474 
475     auto const wakeupTime = callback->wakeupTime();
476     if (wakeupTime) {
477         callback->disarm();
478 
479         if (*wakeupTime == mIntendedWakeupTime) {
480             mIntendedWakeupTime = kInvalidTime;
481             rearmTimer(mTimeKeeper->now());
482         }
483         return CancelResult::Cancelled;
484     }
485     return CancelResult::TooLate;
486 }
487 
dump(std::string & result) const488 void VSyncDispatchTimerQueue::dump(std::string& result) const {
489     std::lock_guard lock(mMutex);
490     StringAppendF(&result, "\tTimer:\n");
491     mTimeKeeper->dump(result);
492     StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
493                   mMinVsyncDistance / 1e6f);
494     StringAppendF(&result, "\tmIntendedWakeupTime: %.2fms from now\n",
495                   (mIntendedWakeupTime - mTimeKeeper->now()) / 1e6f);
496     StringAppendF(&result, "\tmLastTimerCallback: %.2fms ago mLastTimerSchedule: %.2fms ago\n",
497                   (mTimeKeeper->now() - mLastTimerCallback) / 1e6f,
498                   (mTimeKeeper->now() - mLastTimerSchedule) / 1e6f);
499     StringAppendF(&result, "\tCallbacks:\n");
500     for (const auto& [token, entry] : mCallbacks) {
501         entry->dump(result);
502     }
503 }
504 
VSyncCallbackRegistration(std::shared_ptr<VSyncDispatch> dispatch,VSyncDispatch::Callback callback,std::string callbackName)505 VSyncCallbackRegistration::VSyncCallbackRegistration(std::shared_ptr<VSyncDispatch> dispatch,
506                                                      VSyncDispatch::Callback callback,
507                                                      std::string callbackName)
508       : mDispatch(std::move(dispatch)),
509         mToken(mDispatch->registerCallback(std::move(callback), std::move(callbackName))) {}
510 
VSyncCallbackRegistration(VSyncCallbackRegistration && other)511 VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
512       : mDispatch(std::move(other.mDispatch)), mToken(std::exchange(other.mToken, std::nullopt)) {}
513 
operator =(VSyncCallbackRegistration && other)514 VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) {
515     if (this == &other) return *this;
516     if (mToken) {
517         mDispatch->unregisterCallback(*mToken);
518     }
519     mDispatch = std::move(other.mDispatch);
520     mToken = std::exchange(other.mToken, std::nullopt);
521     return *this;
522 }
523 
~VSyncCallbackRegistration()524 VSyncCallbackRegistration::~VSyncCallbackRegistration() {
525     if (mToken) mDispatch->unregisterCallback(*mToken);
526 }
527 
schedule(VSyncDispatch::ScheduleTiming scheduleTiming)528 std::optional<ScheduleResult> VSyncCallbackRegistration::schedule(
529         VSyncDispatch::ScheduleTiming scheduleTiming) {
530     if (!mToken) {
531         return std::nullopt;
532     }
533     return mDispatch->schedule(*mToken, scheduleTiming);
534 }
535 
update(VSyncDispatch::ScheduleTiming scheduleTiming)536 std::optional<ScheduleResult> VSyncCallbackRegistration::update(
537         VSyncDispatch::ScheduleTiming scheduleTiming) {
538     if (!mToken) {
539         return std::nullopt;
540     }
541     return mDispatch->update(*mToken, scheduleTiming);
542 }
543 
cancel()544 CancelResult VSyncCallbackRegistration::cancel() {
545     if (!mToken) {
546         return CancelResult::Error;
547     }
548     return mDispatch->cancel(*mToken);
549 }
550 
551 } // namespace android::scheduler
552