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