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